Line data Source code
1 : /*
2 : * Copyright (c) 2024 Project CHIP Authors
3 : * All rights reserved.
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 : #include <data-model-providers/codegen/CodegenDataModelProvider.h>
18 :
19 : #include <access/AccessControl.h>
20 : #include <access/Privilege.h>
21 : #include <app-common/zap-generated/attribute-type.h>
22 : #include <app/CommandHandlerInterface.h>
23 : #include <app/CommandHandlerInterfaceRegistry.h>
24 : #include <app/ConcreteAttributePath.h>
25 : #include <app/ConcreteClusterPath.h>
26 : #include <app/ConcreteCommandPath.h>
27 : #include <app/EventPathParams.h>
28 : #include <app/GlobalAttributes.h>
29 : #include <app/RequiredPrivilege.h>
30 : #include <app/data-model-provider/MetadataTypes.h>
31 : #include <app/data-model-provider/Provider.h>
32 : #include <app/server-cluster/ServerClusterContext.h>
33 : #include <app/server-cluster/ServerClusterInterface.h>
34 : #include <app/util/DataModelHandler.h>
35 : #include <app/util/IMClusterCommandHandler.h>
36 : #include <app/util/af-types.h>
37 : #include <app/util/attribute-metadata.h>
38 : #include <app/util/attribute-storage.h>
39 : #include <app/util/endpoint-config-api.h>
40 : #include <app/util/persistence/AttributePersistenceProvider.h>
41 : #include <app/util/persistence/DefaultAttributePersistenceProvider.h>
42 : #include <data-model-providers/codegen/EmberMetadata.h>
43 : #include <lib/core/CHIPError.h>
44 : #include <lib/core/DataModelTypes.h>
45 : #include <lib/support/CodeUtils.h>
46 : #include <lib/support/ReadOnlyBuffer.h>
47 : #include <lib/support/ScopedBuffer.h>
48 : #include <lib/support/SpanSearchValue.h>
49 :
50 : #include <cstdint>
51 : #include <optional>
52 :
53 : namespace chip {
54 : namespace app {
55 : namespace {
56 :
57 79 : DataModel::AcceptedCommandEntry AcceptedCommandEntryFor(const ConcreteCommandPath & path)
58 : {
59 79 : const CommandId commandId = path.mCommandId;
60 :
61 : DataModel::AcceptedCommandEntry entry(
62 79 : path.mCommandId,
63 79 : BitFlags<DataModel::CommandQualityFlags>{}
64 79 : .Set(DataModel::CommandQualityFlags::kTimed, CommandNeedsTimedInvoke(path.mClusterId, commandId))
65 79 : .Set(DataModel::CommandQualityFlags::kFabricScoped, CommandIsFabricScoped(path.mClusterId, commandId))
66 79 : .Set(DataModel::CommandQualityFlags::kLargeMessage, CommandHasLargePayload(path.mClusterId, commandId)),
67 158 : RequiredPrivilege::ForInvokeCommand(path));
68 :
69 79 : return entry;
70 : }
71 :
72 11462 : DataModel::ServerClusterEntry ServerClusterEntryFrom(EndpointId endpointId, const EmberAfCluster & cluster)
73 : {
74 11462 : DataModel::ServerClusterEntry entry;
75 :
76 11462 : entry.clusterId = cluster.clusterId;
77 :
78 11462 : DataVersion * versionPtr = emberAfDataVersionStorage(ConcreteClusterPath(endpointId, cluster.clusterId));
79 11462 : if (versionPtr == nullptr)
80 : {
81 : #if CHIP_CONFIG_DATA_MODEL_EXTRA_LOGGING
82 0 : ChipLogError(AppServer, "Failed to get data version for %d/" ChipLogFormatMEI, endpointId,
83 : ChipLogValueMEI(cluster.clusterId));
84 : #endif
85 0 : entry.dataVersion = 0;
86 : }
87 : else
88 : {
89 11462 : entry.dataVersion = *versionPtr;
90 : }
91 :
92 : // TODO: set entry flags:
93 : // entry.flags.Set(ClusterQualityFlags::kDiagnosticsData)
94 :
95 11462 : return entry;
96 : }
97 :
98 66607 : DataModel::AttributeEntry AttributeEntryFrom(const ConcreteClusterPath & clusterPath, const EmberAfAttributeMetadata & attribute)
99 : {
100 66607 : const ConcreteAttributePath attributePath(clusterPath.mEndpointId, clusterPath.mClusterId, attribute.attributeId);
101 :
102 : using DataModel::AttributeQualityFlags;
103 :
104 : DataModel::AttributeEntry entry(
105 66607 : attribute.attributeId,
106 66607 : BitFlags<DataModel::AttributeQualityFlags>{}
107 66607 : .Set(AttributeQualityFlags::kListAttribute, (attribute.attributeType == ZCL_ARRAY_ATTRIBUTE_TYPE))
108 66607 : .Set(DataModel::AttributeQualityFlags::kTimed, attribute.MustUseTimedWrite()),
109 66607 : RequiredPrivilege::ForReadAttribute(attributePath),
110 199821 : attribute.IsReadOnly() ? std::nullopt : std::make_optional(RequiredPrivilege::ForWriteAttribute(attributePath)));
111 :
112 : // NOTE: we do NOT provide additional info for:
113 : // - IsExternal/IsSingleton/IsAutomaticallyPersisted is not used by IM handling
114 : // - IsSingleton spec defines it for CLUSTERS where as we have it for ATTRIBUTES
115 : // - Several specification flags are not available (reportable, quieter reporting,
116 : // fixed, source attribution)
117 :
118 : // TODO: Set additional flags:
119 : // entry.flags.Set(DataModel::AttributeQualityFlags::kFabricScoped)
120 : // entry.flags.Set(DataModel::AttributeQualityFlags::kFabricSensitive)
121 : // entry.flags.Set(DataModel::AttributeQualityFlags::kChangesOmitted)
122 66607 : return entry;
123 : }
124 :
125 : DefaultAttributePersistenceProvider gDefaultAttributePersistence;
126 :
127 : } // namespace
128 :
129 157 : CHIP_ERROR CodegenDataModelProvider::Shutdown()
130 : {
131 157 : Reset();
132 157 : mRegistry.ClearContext();
133 157 : return CHIP_NO_ERROR;
134 : }
135 :
136 395 : CHIP_ERROR CodegenDataModelProvider::Startup(DataModel::InteractionModelContext context)
137 : {
138 395 : ReturnErrorOnFailure(DataModel::Provider::Startup(context));
139 :
140 : // Ember NVM requires have a data model provider. attempt to create one if one is not available
141 : //
142 : // It is not a critical failure to not have one, however if one is not set up, ember NVM operations
143 : // will error out with a `persistence not available`.
144 395 : if (GetAttributePersistenceProvider() == nullptr)
145 : {
146 : #if CHIP_CONFIG_DATA_MODEL_EXTRA_LOGGING
147 393 : ChipLogProgress(DataManagement, "Ember attribute persistence requires setting up");
148 : #endif
149 393 : if (mPersistentStorageDelegate != nullptr)
150 : {
151 2 : ReturnErrorOnFailure(gDefaultAttributePersistence.Init(mPersistentStorageDelegate));
152 2 : SetAttributePersistenceProvider(&gDefaultAttributePersistence);
153 : #if CHIP_CONFIG_DATA_MODEL_EXTRA_LOGGING
154 : }
155 : else
156 : {
157 391 : ChipLogError(DataManagement, "No storage delegate available, will not set up attribute persistence.");
158 : #endif
159 : }
160 : }
161 :
162 395 : InitDataModelForTesting();
163 :
164 395 : return mRegistry.SetContext(ServerClusterContext{
165 : .provider = this,
166 395 : .storage = mPersistentStorageDelegate,
167 395 : .interactionContext = &mContext,
168 395 : });
169 : }
170 :
171 6 : std::optional<DataModel::ActionReturnStatus> CodegenDataModelProvider::InvokeCommand(const DataModel::InvokeRequest & request,
172 : TLV::TLVReader & input_arguments,
173 : CommandHandler * handler)
174 : {
175 6 : if (auto * cluster = mRegistry.Get(request.path); cluster != nullptr)
176 : {
177 1 : return cluster->InvokeCommand(request, input_arguments, handler);
178 : }
179 :
180 : CommandHandlerInterface * handler_interface =
181 5 : CommandHandlerInterfaceRegistry::Instance().GetCommandHandler(request.path.mEndpointId, request.path.mClusterId);
182 :
183 5 : if (handler_interface)
184 : {
185 3 : CommandHandlerInterface::HandlerContext context(*handler, request.path, input_arguments);
186 3 : handler_interface->InvokeCommand(context);
187 :
188 : // If the command was handled, don't proceed any further and return successfully.
189 3 : if (context.mCommandHandled)
190 : {
191 3 : return std::nullopt;
192 : }
193 : }
194 :
195 : // Ember always sets the return in the handler
196 2 : DispatchSingleClusterCommand(request.path, input_arguments, handler);
197 2 : return std::nullopt;
198 : }
199 :
200 2938 : CHIP_ERROR CodegenDataModelProvider::Endpoints(ReadOnlyBufferBuilder<DataModel::EndpointEntry> & builder)
201 : {
202 2938 : const uint16_t endpointCount = emberAfEndpointCount();
203 :
204 2938 : ReturnErrorOnFailure(builder.EnsureAppendCapacity(endpointCount));
205 :
206 17198 : for (uint16_t endpointIndex = 0; endpointIndex < endpointCount; endpointIndex++)
207 : {
208 14260 : if (!emberAfEndpointIndexIsEnabled(endpointIndex))
209 : {
210 4333 : continue;
211 : }
212 :
213 : DataModel::EndpointEntry entry;
214 9927 : entry.id = emberAfEndpointFromIndex(endpointIndex);
215 9927 : entry.parentId = emberAfParentEndpointFromIndex(endpointIndex);
216 :
217 9927 : switch (GetCompositionForEndpointIndex(endpointIndex))
218 : {
219 8446 : case EndpointComposition::kFullFamily:
220 8446 : entry.compositionPattern = DataModel::EndpointCompositionPattern::kFullFamily;
221 8446 : break;
222 1481 : case EndpointComposition::kTree:
223 : case EndpointComposition::kInvalid: // should NOT happen, but force compiler to check we validate all versions
224 1481 : entry.compositionPattern = DataModel::EndpointCompositionPattern::kTree;
225 1481 : break;
226 : }
227 9927 : ReturnErrorOnFailure(builder.Append(entry));
228 : }
229 :
230 2938 : return CHIP_NO_ERROR;
231 : }
232 :
233 3 : std::optional<unsigned> CodegenDataModelProvider::TryFindEndpointIndex(EndpointId id) const
234 : {
235 3 : const uint16_t lastEndpointIndex = emberAfEndpointCount();
236 :
237 6 : if ((mEndpointIterationHint < lastEndpointIndex) && emberAfEndpointIndexIsEnabled(mEndpointIterationHint) &&
238 3 : (id == emberAfEndpointFromIndex(mEndpointIterationHint)))
239 : {
240 1 : return std::make_optional(mEndpointIterationHint);
241 : }
242 :
243 : // Linear search, this may be slow
244 2 : uint16_t idx = emberAfIndexFromEndpoint(id);
245 2 : if (idx == kEmberInvalidEndpointIndex)
246 : {
247 0 : return std::nullopt;
248 : }
249 :
250 2 : return std::make_optional<unsigned>(idx);
251 : }
252 :
253 8465 : CHIP_ERROR CodegenDataModelProvider::ServerClusters(EndpointId endpointId,
254 : ReadOnlyBufferBuilder<DataModel::ServerClusterEntry> & builder)
255 : {
256 8465 : const EmberAfEndpointType * endpoint = emberAfFindEndpointType(endpointId);
257 :
258 8465 : VerifyOrReturnValue(endpoint != nullptr, CHIP_ERROR_NOT_FOUND);
259 8454 : VerifyOrReturnValue(endpoint->clusterCount > 0, CHIP_NO_ERROR);
260 8454 : VerifyOrReturnValue(endpoint->cluster != nullptr, CHIP_NO_ERROR);
261 :
262 : // We build the cluster list by merging two lists:
263 : // - mRegistry items from ServerClusterInterfaces
264 : // - ember metadata clusters
265 : //
266 : // This is done because `ServerClusterInterface` allows full control for all its metadata,
267 : // in particular `data version` and `flags`.
268 : //
269 : // To allow cluster implementations to be incrementally converted to storing their own data versions,
270 : // instead of relying on the out-of-band emberAfDataVersionStorage, first check for clusters that are
271 : // using the new data version storage and are registered via ServerClusterInterfaceRegistry, then fill
272 : // in the data versions for the rest via the out-of-band mechanism.
273 :
274 : // assume the clusters on endpoint does not change in between these two loops
275 8454 : auto clusters = mRegistry.ClustersOnEndpoint(endpointId);
276 8454 : size_t registryClusterCount = 0;
277 8455 : for ([[maybe_unused]] auto _ : clusters)
278 : {
279 1 : registryClusterCount++;
280 : }
281 :
282 8454 : ReturnErrorOnFailure(builder.EnsureAppendCapacity(registryClusterCount));
283 :
284 8454 : ReadOnlyBufferBuilder<ClusterId> knownClustersBuilder;
285 8454 : ReturnErrorOnFailure(knownClustersBuilder.EnsureAppendCapacity(registryClusterCount));
286 8455 : for (const auto clusterId : mRegistry.ClustersOnEndpoint(endpointId))
287 : {
288 1 : ConcreteClusterPath path(endpointId, clusterId);
289 1 : ServerClusterInterface * cluster = mRegistry.Get(path);
290 :
291 : // path MUST be valid: we just got it from iterating our registrations...
292 1 : VerifyOrReturnError(cluster != nullptr, CHIP_ERROR_INTERNAL);
293 :
294 1 : ReturnErrorOnFailure(builder.Append({
295 : .clusterId = path.mClusterId,
296 : .dataVersion = cluster->GetDataVersion(path),
297 : .flags = cluster->GetClusterFlags(path),
298 : }));
299 1 : ReturnErrorOnFailure(knownClustersBuilder.Append(path.mClusterId));
300 : }
301 :
302 8454 : ReadOnlyBuffer<ClusterId> knownClusters = knownClustersBuilder.TakeBuffer();
303 :
304 8454 : ReturnErrorOnFailure(builder.EnsureAppendCapacity(emberAfClusterCountForEndpointType(endpoint, /* server = */ true)));
305 :
306 8454 : const EmberAfCluster * begin = endpoint->cluster;
307 8454 : const EmberAfCluster * end = endpoint->cluster + endpoint->clusterCount;
308 21420 : for (const EmberAfCluster * cluster = begin; cluster != end; cluster++)
309 : {
310 12966 : if (!cluster->IsServer())
311 : {
312 1503 : continue;
313 : }
314 :
315 : // linear search as this is a somewhat compact number list, so performance is probably not too bad
316 : // This results in smaller code than some memory allocation + std::sort + std::binary_search
317 11463 : bool found = false;
318 11464 : for (ClusterId clusterId : knownClusters)
319 : {
320 2 : if (clusterId == cluster->clusterId)
321 : {
322 1 : found = true;
323 1 : break;
324 : }
325 : }
326 11463 : if (found)
327 : {
328 : // value already filled from the ServerClusterRegistry. That one has the correct/overriden
329 : // flags and data version
330 1 : continue;
331 : }
332 :
333 11462 : ReturnErrorOnFailure(builder.Append(ServerClusterEntryFrom(endpointId, *cluster)));
334 : }
335 :
336 8454 : return CHIP_NO_ERROR;
337 8454 : }
338 :
339 16768 : CHIP_ERROR CodegenDataModelProvider::Attributes(const ConcreteClusterPath & path,
340 : ReadOnlyBufferBuilder<DataModel::AttributeEntry> & builder)
341 : {
342 16768 : if (auto * cluster = mRegistry.Get(path); cluster != nullptr)
343 : {
344 1 : return cluster->Attributes(path, builder);
345 : }
346 :
347 16767 : const EmberAfCluster * cluster = FindServerCluster(path);
348 :
349 16767 : VerifyOrReturnValue(cluster != nullptr, CHIP_ERROR_NOT_FOUND);
350 16066 : VerifyOrReturnValue(cluster->attributeCount > 0, CHIP_NO_ERROR);
351 16066 : VerifyOrReturnValue(cluster->attributes != nullptr, CHIP_NO_ERROR);
352 :
353 : // TODO: if ember would encode data in AttributeEntry form, we could reference things directly (shorter code,
354 : // although still allocation overhead due to global attributes not in metadata)
355 : //
356 : // We have Attributes from ember + global attributes that are NOT in ember metadata.
357 : // We have to report them all
358 16066 : constexpr size_t kGlobalAttributeNotInMetadataCount = MATTER_ARRAY_SIZE(GlobalAttributesNotInMetadata);
359 :
360 16066 : ReturnErrorOnFailure(builder.EnsureAppendCapacity(cluster->attributeCount + kGlobalAttributeNotInMetadataCount));
361 :
362 16066 : Span<const EmberAfAttributeMetadata> attributeSpan(cluster->attributes, cluster->attributeCount);
363 :
364 82673 : for (auto & attribute : attributeSpan)
365 : {
366 66607 : ReturnErrorOnFailure(builder.Append(AttributeEntryFrom(path, attribute)));
367 : }
368 :
369 64264 : for (auto & attributeId : GlobalAttributesNotInMetadata)
370 : {
371 :
372 : // This "GlobalListEntry" is specific for metadata that ember does not include
373 : // in its attribute list metadata.
374 : //
375 : // By spec these Attribute/AcceptedCommands/GeneratedCommants lists are:
376 : // - lists of elements
377 : // - read-only, with read privilege view
378 : // - fixed value (no such flag exists, so this is not a quality flag we set/track)
379 : DataModel::AttributeEntry globalListEntry(attributeId, DataModel::AttributeQualityFlags::kListAttribute,
380 48198 : Access::Privilege::kView, std::nullopt);
381 :
382 48198 : ReturnErrorOnFailure(builder.Append(std::move(globalListEntry)));
383 : }
384 :
385 16066 : return CHIP_NO_ERROR;
386 : }
387 :
388 4 : CHIP_ERROR CodegenDataModelProvider::ClientClusters(EndpointId endpointId, ReadOnlyBufferBuilder<ClusterId> & builder)
389 : {
390 4 : const EmberAfEndpointType * endpoint = emberAfFindEndpointType(endpointId);
391 :
392 4 : VerifyOrReturnValue(endpoint != nullptr, CHIP_ERROR_NOT_FOUND);
393 2 : VerifyOrReturnValue(endpoint->clusterCount > 0, CHIP_NO_ERROR);
394 2 : VerifyOrReturnValue(endpoint->cluster != nullptr, CHIP_NO_ERROR);
395 :
396 2 : ReturnErrorOnFailure(builder.EnsureAppendCapacity(emberAfClusterCountForEndpointType(endpoint, /* server = */ false)));
397 :
398 2 : const EmberAfCluster * begin = endpoint->cluster;
399 2 : const EmberAfCluster * end = endpoint->cluster + endpoint->clusterCount;
400 10 : for (const EmberAfCluster * cluster = begin; cluster != end; cluster++)
401 : {
402 8 : if (!cluster->IsClient())
403 : {
404 4 : continue;
405 : }
406 4 : ReturnErrorOnFailure(builder.Append(cluster->clusterId));
407 : }
408 :
409 2 : return CHIP_NO_ERROR;
410 : }
411 :
412 17572 : const EmberAfCluster * CodegenDataModelProvider::FindServerCluster(const ConcreteClusterPath & path)
413 : {
414 34091 : if (mPreviouslyFoundCluster.has_value() && (mPreviouslyFoundCluster->path == path) &&
415 16519 : (mEmberMetadataStructureGeneration == emberAfMetadataStructureGeneration()))
416 :
417 : {
418 16410 : return mPreviouslyFoundCluster->cluster;
419 : }
420 :
421 1162 : const EmberAfCluster * cluster = emberAfFindServerCluster(path.mEndpointId, path.mClusterId);
422 1162 : if (cluster != nullptr)
423 : {
424 448 : mPreviouslyFoundCluster = std::make_optional<ClusterReference>(path, cluster);
425 448 : mEmberMetadataStructureGeneration = emberAfMetadataStructureGeneration();
426 : }
427 1162 : return cluster;
428 : }
429 :
430 429 : CHIP_ERROR CodegenDataModelProvider::AcceptedCommands(const ConcreteClusterPath & path,
431 : ReadOnlyBufferBuilder<DataModel::AcceptedCommandEntry> & builder)
432 : {
433 429 : if (auto * cluster = mRegistry.Get(path); cluster != nullptr)
434 : {
435 1 : return cluster->AcceptedCommands(path, builder);
436 : }
437 :
438 : // Some CommandHandlerInterface instances are registered of ALL endpoints, so make sure first that
439 : // the cluster actually exists on this endpoint before asking the CommandHandlerInterface what commands
440 : // it claims to support.
441 428 : const EmberAfCluster * serverCluster = FindServerCluster(path);
442 428 : VerifyOrReturnError(serverCluster != nullptr, CHIP_ERROR_NOT_FOUND);
443 :
444 : CommandHandlerInterface * interface =
445 421 : CommandHandlerInterfaceRegistry::Instance().GetCommandHandler(path.mEndpointId, path.mClusterId);
446 421 : if (interface != nullptr)
447 : {
448 16 : size_t commandCount = 0;
449 :
450 16 : CHIP_ERROR err = interface->EnumerateAcceptedCommands(
451 : path,
452 6 : [](CommandId id, void * context) -> Loop {
453 6 : *reinterpret_cast<size_t *>(context) += 1;
454 6 : return Loop::Continue;
455 : },
456 : reinterpret_cast<void *>(&commandCount));
457 :
458 16 : if (err == CHIP_NO_ERROR)
459 : {
460 : using EnumerationData = struct
461 : {
462 : ConcreteCommandPath commandPath;
463 : ReadOnlyBufferBuilder<DataModel::AcceptedCommandEntry> * acceptedCommandList;
464 : CHIP_ERROR processingError;
465 : };
466 :
467 8 : EnumerationData enumerationData;
468 8 : enumerationData.commandPath = ConcreteCommandPath(path.mEndpointId, path.mClusterId, kInvalidCommandId);
469 8 : enumerationData.processingError = CHIP_NO_ERROR;
470 8 : enumerationData.acceptedCommandList = &builder;
471 :
472 8 : ReturnErrorOnFailure(builder.EnsureAppendCapacity(commandCount));
473 :
474 14 : ReturnErrorOnFailure(interface->EnumerateAcceptedCommands(
475 : path,
476 : [](CommandId commandId, void * context) -> Loop {
477 : auto input = reinterpret_cast<EnumerationData *>(context);
478 : input->commandPath.mCommandId = commandId;
479 : CHIP_ERROR appendError = input->acceptedCommandList->Append(AcceptedCommandEntryFor(input->commandPath));
480 : if (appendError != CHIP_NO_ERROR)
481 : {
482 : input->processingError = appendError;
483 : return Loop::Break;
484 : }
485 : return Loop::Continue;
486 : },
487 : reinterpret_cast<void *>(&enumerationData)));
488 8 : ReturnErrorOnFailure(enumerationData.processingError);
489 :
490 : // the two invocations MUST return the same sizes.
491 8 : VerifyOrReturnError(builder.Size() == commandCount, CHIP_ERROR_INTERNAL);
492 8 : return CHIP_NO_ERROR;
493 : }
494 8 : VerifyOrReturnError(err == CHIP_ERROR_NOT_IMPLEMENTED, err);
495 : }
496 :
497 413 : VerifyOrReturnError(serverCluster->acceptedCommandList != nullptr, CHIP_NO_ERROR);
498 :
499 28 : const chip::CommandId * endOfList = serverCluster->acceptedCommandList;
500 101 : while (*endOfList != kInvalidCommandId)
501 : {
502 73 : endOfList++;
503 : }
504 28 : const auto commandCount = static_cast<size_t>(endOfList - serverCluster->acceptedCommandList);
505 :
506 : // TODO: if ember would store command entries, we could simplify this code to use static data
507 28 : ReturnErrorOnFailure(builder.EnsureAppendCapacity(commandCount));
508 :
509 28 : ConcreteCommandPath commandPath = ConcreteCommandPath(path.mEndpointId, path.mClusterId, kInvalidCommandId);
510 101 : for (const chip::CommandId * p = serverCluster->acceptedCommandList; p != endOfList; p++)
511 : {
512 73 : commandPath.mCommandId = *p;
513 73 : ReturnErrorOnFailure(builder.Append(AcceptedCommandEntryFor(commandPath)));
514 : }
515 :
516 28 : return CHIP_NO_ERROR;
517 : }
518 :
519 378 : CHIP_ERROR CodegenDataModelProvider::GeneratedCommands(const ConcreteClusterPath & path, ReadOnlyBufferBuilder<CommandId> & builder)
520 : {
521 378 : if (auto * cluster = mRegistry.Get(path); cluster != nullptr)
522 : {
523 1 : return cluster->GeneratedCommands(path, builder);
524 : }
525 :
526 : // Some CommandHandlerInterface instances are registered of ALL endpoints, so make sure first that
527 : // the cluster actually exists on this endpoint before asking the CommandHandlerInterface what commands
528 : // it claims to support.
529 377 : const EmberAfCluster * serverCluster = FindServerCluster(path);
530 377 : VerifyOrReturnError(serverCluster != nullptr, CHIP_ERROR_NOT_FOUND);
531 :
532 : CommandHandlerInterface * interface =
533 371 : CommandHandlerInterfaceRegistry::Instance().GetCommandHandler(path.mEndpointId, path.mClusterId);
534 371 : if (interface != nullptr)
535 : {
536 4 : size_t commandCount = 0;
537 :
538 4 : CHIP_ERROR err = interface->EnumerateGeneratedCommands(
539 : path,
540 1 : [](CommandId id, void * context) -> Loop {
541 1 : *reinterpret_cast<size_t *>(context) += 1;
542 1 : return Loop::Continue;
543 : },
544 : reinterpret_cast<void *>(&commandCount));
545 :
546 4 : if (err == CHIP_NO_ERROR)
547 : {
548 2 : ReturnErrorOnFailure(builder.EnsureAppendCapacity(commandCount));
549 :
550 : using EnumerationData = struct
551 : {
552 : ReadOnlyBufferBuilder<CommandId> * generatedCommandList;
553 : CHIP_ERROR processingError;
554 : };
555 : EnumerationData enumerationData;
556 2 : enumerationData.processingError = CHIP_NO_ERROR;
557 2 : enumerationData.generatedCommandList = &builder;
558 :
559 3 : ReturnErrorOnFailure(interface->EnumerateGeneratedCommands(
560 : path,
561 : [](CommandId id, void * context) -> Loop {
562 : auto input = reinterpret_cast<EnumerationData *>(context);
563 :
564 : CHIP_ERROR appendError = input->generatedCommandList->Append(id);
565 : if (appendError != CHIP_NO_ERROR)
566 : {
567 : input->processingError = appendError;
568 : return Loop::Break;
569 : }
570 : return Loop::Continue;
571 : },
572 : reinterpret_cast<void *>(&enumerationData)));
573 2 : ReturnErrorOnFailure(enumerationData.processingError);
574 :
575 : // the two invocations MUST return the same sizes.
576 2 : VerifyOrReturnError(builder.Size() == commandCount, CHIP_ERROR_INTERNAL);
577 2 : return CHIP_NO_ERROR;
578 : }
579 2 : VerifyOrReturnError(err == CHIP_ERROR_NOT_IMPLEMENTED, err);
580 : }
581 :
582 369 : VerifyOrReturnError(serverCluster->generatedCommandList != nullptr, CHIP_NO_ERROR);
583 :
584 2 : const chip::CommandId * endOfList = serverCluster->generatedCommandList;
585 6 : while (*endOfList != kInvalidCommandId)
586 : {
587 4 : endOfList++;
588 : }
589 2 : const auto commandCount = static_cast<size_t>(endOfList - serverCluster->generatedCommandList);
590 2 : return builder.ReferenceExisting({ serverCluster->generatedCommandList, commandCount });
591 : }
592 :
593 388 : void CodegenDataModelProvider::InitDataModelForTesting()
594 : {
595 : // Call the Ember-specific InitDataModelHandler
596 388 : InitDataModelHandler();
597 388 : }
598 :
599 3 : CHIP_ERROR CodegenDataModelProvider::DeviceTypes(EndpointId endpointId, ReadOnlyBufferBuilder<DataModel::DeviceTypeEntry> & builder)
600 : {
601 3 : std::optional<unsigned> endpoint_index = TryFindEndpointIndex(endpointId);
602 3 : if (!endpoint_index.has_value())
603 : {
604 0 : return {};
605 : }
606 :
607 3 : CHIP_ERROR err = CHIP_NO_ERROR;
608 :
609 3 : return builder.ReferenceExisting(emberAfDeviceTypeListFromEndpointIndex(*endpoint_index, err));
610 : }
611 :
612 2 : CHIP_ERROR CodegenDataModelProvider::SemanticTags(EndpointId endpointId, ReadOnlyBufferBuilder<SemanticTag> & builder)
613 : {
614 2 : DataModel::Provider::SemanticTag semanticTag;
615 2 : size_t count = 0;
616 :
617 5 : while (GetSemanticTagForEndpointAtIndex(endpointId, count, semanticTag) == CHIP_NO_ERROR)
618 : {
619 3 : count++;
620 : }
621 :
622 2 : ReturnErrorOnFailure(builder.EnsureAppendCapacity(count));
623 :
624 5 : for (size_t idx = 0; idx < count; idx++)
625 : {
626 3 : ReturnErrorOnFailure(GetSemanticTagForEndpointAtIndex(endpointId, idx, semanticTag));
627 3 : ReturnErrorOnFailure(builder.Append(semanticTag));
628 : }
629 :
630 2 : return CHIP_NO_ERROR;
631 : }
632 : #if CHIP_CONFIG_USE_ENDPOINT_UNIQUE_ID
633 : CHIP_ERROR CodegenDataModelProvider::EndpointUniqueID(EndpointId endpointId, MutableCharSpan & epUniqueId)
634 : {
635 : char buffer[Clusters::Descriptor::Attributes::EndpointUniqueID::TypeInfo::MaxLength()] = { 0 };
636 : MutableCharSpan epUniqueIdSpan(buffer);
637 : emberAfGetEndpointUniqueIdForEndPoint(endpointId, epUniqueIdSpan);
638 :
639 : memcpy(epUniqueId.data(), epUniqueIdSpan.data(), epUniqueIdSpan.size());
640 : return CHIP_NO_ERROR;
641 : }
642 : #endif
643 :
644 : } // namespace app
645 : } // namespace chip
|