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/MetadataList.h>
31 : #include <app/data-model-provider/MetadataTypes.h>
32 : #include <app/data-model-provider/Provider.h>
33 : #include <app/server-cluster/ServerClusterContext.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/ScopedBuffer.h>
47 : #include <lib/support/SpanSearchValue.h>
48 :
49 : #include <cstdint>
50 : #include <optional>
51 :
52 : namespace chip {
53 : namespace app {
54 : namespace {
55 :
56 79 : DataModel::AcceptedCommandEntry AcceptedCommandEntryFor(const ConcreteCommandPath & path)
57 : {
58 79 : const CommandId commandId = path.mCommandId;
59 :
60 79 : DataModel::AcceptedCommandEntry entry;
61 :
62 79 : entry.commandId = path.mCommandId;
63 79 : entry.invokePrivilege = RequiredPrivilege::ForInvokeCommand(path);
64 79 : entry.flags.Set(DataModel::CommandQualityFlags::kTimed, CommandNeedsTimedInvoke(path.mClusterId, commandId));
65 79 : entry.flags.Set(DataModel::CommandQualityFlags::kFabricScoped, CommandIsFabricScoped(path.mClusterId, commandId));
66 79 : entry.flags.Set(DataModel::CommandQualityFlags::kLargeMessage, CommandHasLargePayload(path.mClusterId, commandId));
67 :
68 79 : return entry;
69 : }
70 :
71 11453 : DataModel::ServerClusterEntry ServerClusterEntryFrom(EndpointId endpointId, const EmberAfCluster & cluster)
72 : {
73 11453 : DataModel::ServerClusterEntry entry;
74 :
75 11453 : entry.clusterId = cluster.clusterId;
76 :
77 11453 : DataVersion * versionPtr = emberAfDataVersionStorage(ConcreteClusterPath(endpointId, cluster.clusterId));
78 11453 : if (versionPtr == nullptr)
79 : {
80 : #if CHIP_CONFIG_DATA_MODEL_EXTRA_LOGGING
81 0 : ChipLogError(AppServer, "Failed to get data version for %d/" ChipLogFormatMEI, endpointId,
82 : ChipLogValueMEI(cluster.clusterId));
83 : #endif
84 0 : entry.dataVersion = 0;
85 : }
86 : else
87 : {
88 11453 : entry.dataVersion = *versionPtr;
89 : }
90 :
91 : // TODO: set entry flags:
92 : // entry.flags.Set(ClusterQualityFlags::kDiagnosticsData)
93 :
94 11453 : return entry;
95 : }
96 :
97 56385 : DataModel::AttributeEntry AttributeEntryFrom(const ConcreteClusterPath & clusterPath, const EmberAfAttributeMetadata & attribute)
98 : {
99 56385 : DataModel::AttributeEntry entry;
100 :
101 56385 : const ConcreteAttributePath attributePath(clusterPath.mEndpointId, clusterPath.mClusterId, attribute.attributeId);
102 :
103 56385 : entry.attributeId = attribute.attributeId;
104 56385 : entry.readPrivilege = RequiredPrivilege::ForReadAttribute(attributePath);
105 56385 : if (!attribute.IsReadOnly())
106 : {
107 31267 : entry.writePrivilege = RequiredPrivilege::ForWriteAttribute(attributePath);
108 : }
109 :
110 56385 : entry.flags.Set(DataModel::AttributeQualityFlags::kListAttribute, (attribute.attributeType == ZCL_ARRAY_ATTRIBUTE_TYPE));
111 56385 : entry.flags.Set(DataModel::AttributeQualityFlags::kTimed, attribute.MustUseTimedWrite());
112 :
113 : // NOTE: we do NOT provide additional info for:
114 : // - IsExternal/IsSingleton/IsAutomaticallyPersisted is not used by IM handling
115 : // - IsSingleton spec defines it for CLUSTERS where as we have it for ATTRIBUTES
116 : // - Several specification flags are not available (reportable, quieter reporting,
117 : // fixed, source attribution)
118 :
119 : // TODO: Set additional flags:
120 : // entry.flags.Set(DataModel::AttributeQualityFlags::kFabricScoped)
121 : // entry.flags.Set(DataModel::AttributeQualityFlags::kFabricSensitive)
122 : // entry.flags.Set(DataModel::AttributeQualityFlags::kChangesOmitted)
123 56385 : return entry;
124 : }
125 :
126 : const ConcreteCommandPath kInvalidCommandPath(kInvalidEndpointId, kInvalidClusterId, kInvalidCommandId);
127 :
128 : DefaultAttributePersistenceProvider gDefaultAttributePersistence;
129 :
130 : } // namespace
131 :
132 157 : CHIP_ERROR CodegenDataModelProvider::Shutdown()
133 : {
134 157 : Reset();
135 157 : mRegistry.ClearContext();
136 157 : return CHIP_NO_ERROR;
137 : }
138 :
139 393 : CHIP_ERROR CodegenDataModelProvider::Startup(DataModel::InteractionModelContext context)
140 : {
141 393 : ReturnErrorOnFailure(DataModel::Provider::Startup(context));
142 :
143 : // Ember NVM requires have a data model provider. attempt to create one if one is not available
144 : //
145 : // It is not a critical failure to not have one, however if one is not set up, ember NVM operations
146 : // will error out with a `persistence not available`.
147 393 : if (GetAttributePersistenceProvider() == nullptr)
148 : {
149 : #if CHIP_CONFIG_DATA_MODEL_EXTRA_LOGGING
150 391 : ChipLogProgress(DataManagement, "Ember attribute persistence requires setting up");
151 : #endif
152 391 : if (mPersistentStorageDelegate != nullptr)
153 : {
154 2 : ReturnErrorOnFailure(gDefaultAttributePersistence.Init(mPersistentStorageDelegate));
155 2 : SetAttributePersistenceProvider(&gDefaultAttributePersistence);
156 : #if CHIP_CONFIG_DATA_MODEL_EXTRA_LOGGING
157 : }
158 : else
159 : {
160 389 : ChipLogError(DataManagement, "No storage delegate available, will not set up attribute persistence.");
161 : #endif
162 : }
163 : }
164 :
165 393 : InitDataModelForTesting();
166 :
167 393 : return mRegistry.SetContext(ServerClusterContext{
168 : .provider = this,
169 393 : .storage = mPersistentStorageDelegate,
170 393 : .interactionContext = &mContext,
171 393 : });
172 : }
173 :
174 6 : std::optional<DataModel::ActionReturnStatus> CodegenDataModelProvider::InvokeCommand(const DataModel::InvokeRequest & request,
175 : TLV::TLVReader & input_arguments,
176 : CommandHandler * handler)
177 : {
178 6 : if (auto * cluster = mRegistry.Get(request.path); cluster != nullptr)
179 : {
180 1 : return cluster->InvokeCommand(request, input_arguments, handler);
181 : }
182 :
183 : CommandHandlerInterface * handler_interface =
184 5 : CommandHandlerInterfaceRegistry::Instance().GetCommandHandler(request.path.mEndpointId, request.path.mClusterId);
185 :
186 5 : if (handler_interface)
187 : {
188 3 : CommandHandlerInterface::HandlerContext context(*handler, request.path, input_arguments);
189 3 : handler_interface->InvokeCommand(context);
190 :
191 : // If the command was handled, don't proceed any further and return successfully.
192 3 : if (context.mCommandHandled)
193 : {
194 3 : return std::nullopt;
195 : }
196 : }
197 :
198 : // Ember always sets the return in the handler
199 2 : DispatchSingleClusterCommand(request.path, input_arguments, handler);
200 2 : return std::nullopt;
201 : }
202 :
203 2932 : CHIP_ERROR CodegenDataModelProvider::Endpoints(DataModel::ListBuilder<DataModel::EndpointEntry> & builder)
204 : {
205 2932 : const uint16_t endpointCount = emberAfEndpointCount();
206 :
207 2932 : ReturnErrorOnFailure(builder.EnsureAppendCapacity(endpointCount));
208 :
209 17161 : for (uint16_t endpointIndex = 0; endpointIndex < endpointCount; endpointIndex++)
210 : {
211 14229 : if (!emberAfEndpointIndexIsEnabled(endpointIndex))
212 : {
213 4333 : continue;
214 : }
215 :
216 : DataModel::EndpointEntry entry;
217 9896 : entry.id = emberAfEndpointFromIndex(endpointIndex);
218 9896 : entry.parentId = emberAfParentEndpointFromIndex(endpointIndex);
219 :
220 9896 : switch (GetCompositionForEndpointIndex(endpointIndex))
221 : {
222 8416 : case EndpointComposition::kFullFamily:
223 8416 : entry.compositionPattern = DataModel::EndpointCompositionPattern::kFullFamily;
224 8416 : break;
225 1480 : case EndpointComposition::kTree:
226 : case EndpointComposition::kInvalid: // should NOT happen, but force compiler to check we validate all versions
227 1480 : entry.compositionPattern = DataModel::EndpointCompositionPattern::kTree;
228 1480 : break;
229 : }
230 9896 : ReturnErrorOnFailure(builder.Append(entry));
231 : }
232 :
233 2932 : return CHIP_NO_ERROR;
234 : }
235 :
236 3 : std::optional<unsigned> CodegenDataModelProvider::TryFindEndpointIndex(EndpointId id) const
237 : {
238 3 : const uint16_t lastEndpointIndex = emberAfEndpointCount();
239 :
240 6 : if ((mEndpointIterationHint < lastEndpointIndex) && emberAfEndpointIndexIsEnabled(mEndpointIterationHint) &&
241 3 : (id == emberAfEndpointFromIndex(mEndpointIterationHint)))
242 : {
243 1 : return std::make_optional(mEndpointIterationHint);
244 : }
245 :
246 : // Linear search, this may be slow
247 2 : uint16_t idx = emberAfIndexFromEndpoint(id);
248 2 : if (idx == kEmberInvalidEndpointIndex)
249 : {
250 0 : return std::nullopt;
251 : }
252 :
253 2 : return std::make_optional<unsigned>(idx);
254 : }
255 :
256 8456 : CHIP_ERROR CodegenDataModelProvider::ServerClusters(EndpointId endpointId,
257 : DataModel::ListBuilder<DataModel::ServerClusterEntry> & builder)
258 : {
259 8456 : const EmberAfEndpointType * endpoint = emberAfFindEndpointType(endpointId);
260 :
261 8456 : VerifyOrReturnValue(endpoint != nullptr, CHIP_ERROR_NOT_FOUND);
262 8445 : VerifyOrReturnValue(endpoint->clusterCount > 0, CHIP_NO_ERROR);
263 8445 : VerifyOrReturnValue(endpoint->cluster != nullptr, CHIP_NO_ERROR);
264 :
265 : // We build the cluster list by merging two lists:
266 : // - mRegistry items from ServerClusterInterfaces
267 : // - ember metadata clusters
268 : //
269 : // This is done because `ServerClusterInterface` allows full control for all its metadata,
270 : // in particular `data version` and `flags`.
271 : //
272 : // To allow cluster implementations to be incrementally converted to storing their own data versions,
273 : // instead of relying on the out-of-band emberAfDataVersionStorage, first check for clusters that are
274 : // using the new data version storage and are registered via ServerClusterInterfaceRegistry, then fill
275 : // in the data versions for the rest via the out-of-band mechanism.
276 :
277 : // assume the clusters on endpoint does not change in between these two loops
278 8445 : auto clusters = mRegistry.ClustersOnEndpoint(endpointId);
279 8445 : size_t registryClusterCount = 0;
280 8446 : for ([[maybe_unused]] auto _ : clusters)
281 : {
282 1 : registryClusterCount++;
283 : }
284 :
285 8445 : ReturnErrorOnFailure(builder.EnsureAppendCapacity(registryClusterCount));
286 :
287 8445 : DataModel::ListBuilder<ClusterId> knownClustersBuilder;
288 8445 : ReturnErrorOnFailure(knownClustersBuilder.EnsureAppendCapacity(registryClusterCount));
289 8446 : for (auto * cluster : mRegistry.ClustersOnEndpoint(endpointId))
290 : {
291 1 : const ConcreteClusterPath path = cluster->GetPath();
292 1 : ReturnErrorOnFailure(builder.Append({
293 : .clusterId = path.mClusterId,
294 : .dataVersion = cluster->GetDataVersion(),
295 : .flags = cluster->GetClusterFlags(),
296 : }));
297 1 : ReturnErrorOnFailure(knownClustersBuilder.Append(path.mClusterId));
298 : }
299 :
300 8445 : DataModel::ReadOnlyBuffer<ClusterId> knownClusters = knownClustersBuilder.TakeBuffer();
301 :
302 8445 : ReturnErrorOnFailure(builder.EnsureAppendCapacity(emberAfClusterCountForEndpointType(endpoint, /* server = */ true)));
303 :
304 8445 : const EmberAfCluster * begin = endpoint->cluster;
305 8445 : const EmberAfCluster * end = endpoint->cluster + endpoint->clusterCount;
306 21298 : for (const EmberAfCluster * cluster = begin; cluster != end; cluster++)
307 : {
308 12853 : if (!cluster->IsServer())
309 : {
310 1399 : continue;
311 : }
312 :
313 : // linear search as this is a somewhat compact number list, so performance is probably not too bad
314 : // This results in smaller code than some memory allocation + std::sort + std::binary_search
315 11454 : bool found = false;
316 11455 : for (ClusterId clusterId : knownClusters)
317 : {
318 2 : if (clusterId == cluster->clusterId)
319 : {
320 1 : found = true;
321 1 : break;
322 : }
323 : }
324 11454 : if (found)
325 : {
326 : // value already filled from the ServerClusterRegistry. That one has the correct/overriden
327 : // flags and data version
328 1 : continue;
329 : }
330 :
331 11453 : ReturnErrorOnFailure(builder.Append(ServerClusterEntryFrom(endpointId, *cluster)));
332 : }
333 :
334 8445 : return CHIP_NO_ERROR;
335 8445 : }
336 :
337 13377 : CHIP_ERROR CodegenDataModelProvider::Attributes(const ConcreteClusterPath & path,
338 : DataModel::ListBuilder<DataModel::AttributeEntry> & builder)
339 : {
340 13377 : if (auto * cluster = mRegistry.Get(path); cluster != nullptr)
341 : {
342 1 : return cluster->Attributes(path, builder);
343 : }
344 :
345 13376 : const EmberAfCluster * cluster = FindServerCluster(path);
346 :
347 13376 : VerifyOrReturnValue(cluster != nullptr, CHIP_ERROR_NOT_FOUND);
348 12675 : VerifyOrReturnValue(cluster->attributeCount > 0, CHIP_NO_ERROR);
349 12675 : VerifyOrReturnValue(cluster->attributes != nullptr, CHIP_NO_ERROR);
350 :
351 : // TODO: if ember would encode data in AttributeEntry form, we could reference things directly (shorter code,
352 : // although still allocation overhead due to global attributes not in metadata)
353 : //
354 : // We have Attributes from ember + global attributes that are NOT in ember metadata.
355 : // We have to report them all
356 12675 : constexpr size_t kGlobalAttributeNotInMetadataCount = MATTER_ARRAY_SIZE(GlobalAttributesNotInMetadata);
357 :
358 12675 : ReturnErrorOnFailure(builder.EnsureAppendCapacity(cluster->attributeCount + kGlobalAttributeNotInMetadataCount));
359 :
360 12675 : Span<const EmberAfAttributeMetadata> attributeSpan(cluster->attributes, cluster->attributeCount);
361 :
362 69060 : for (auto & attribute : attributeSpan)
363 : {
364 56385 : ReturnErrorOnFailure(builder.Append(AttributeEntryFrom(path, attribute)));
365 : }
366 :
367 : // This "GlobalListEntry" is specific for metadata that ember does not include
368 : // in its attribute list metadata.
369 : //
370 : // By spec these Attribute/AcceptedCommands/GeneratedCommants lists are:
371 : // - lists of elements
372 : // - read-only, with read privilege view
373 : // - fixed value (no such flag exists, so this is not a quality flag we set/track)
374 12675 : DataModel::AttributeEntry globalListEntry;
375 :
376 12675 : globalListEntry.readPrivilege = Access::Privilege::kView;
377 12675 : globalListEntry.flags.Set(DataModel::AttributeQualityFlags::kListAttribute);
378 :
379 50700 : for (auto & attribute : GlobalAttributesNotInMetadata)
380 : {
381 38025 : globalListEntry.attributeId = attribute;
382 38025 : ReturnErrorOnFailure(builder.Append(globalListEntry));
383 : }
384 :
385 12675 : return CHIP_NO_ERROR;
386 : }
387 :
388 4 : CHIP_ERROR CodegenDataModelProvider::ClientClusters(EndpointId endpointId, DataModel::ListBuilder<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 14181 : const EmberAfCluster * CodegenDataModelProvider::FindServerCluster(const ConcreteClusterPath & path)
413 : {
414 27318 : if (mPreviouslyFoundCluster.has_value() && (mPreviouslyFoundCluster->path == path) &&
415 13137 : (mEmberMetadataStructureGeneration == emberAfMetadataStructureGeneration()))
416 :
417 : {
418 13026 : return mPreviouslyFoundCluster->cluster;
419 : }
420 :
421 1155 : const EmberAfCluster * cluster = emberAfFindServerCluster(path.mEndpointId, path.mClusterId);
422 1155 : if (cluster != nullptr)
423 : {
424 441 : mPreviouslyFoundCluster = std::make_optional<ClusterReference>(path, cluster);
425 441 : mEmberMetadataStructureGeneration = emberAfMetadataStructureGeneration();
426 : }
427 1155 : return cluster;
428 : }
429 :
430 429 : CHIP_ERROR CodegenDataModelProvider::AcceptedCommands(const ConcreteClusterPath & path,
431 : DataModel::ListBuilder<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 : DataModel::ListBuilder<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,
520 : DataModel::ListBuilder<CommandId> & builder)
521 : {
522 378 : if (auto * cluster = mRegistry.Get(path); cluster != nullptr)
523 : {
524 1 : return cluster->GeneratedCommands(path, builder);
525 : }
526 :
527 : // Some CommandHandlerInterface instances are registered of ALL endpoints, so make sure first that
528 : // the cluster actually exists on this endpoint before asking the CommandHandlerInterface what commands
529 : // it claims to support.
530 377 : const EmberAfCluster * serverCluster = FindServerCluster(path);
531 377 : VerifyOrReturnError(serverCluster != nullptr, CHIP_ERROR_NOT_FOUND);
532 :
533 : CommandHandlerInterface * interface =
534 371 : CommandHandlerInterfaceRegistry::Instance().GetCommandHandler(path.mEndpointId, path.mClusterId);
535 371 : if (interface != nullptr)
536 : {
537 4 : size_t commandCount = 0;
538 :
539 4 : CHIP_ERROR err = interface->EnumerateGeneratedCommands(
540 : path,
541 1 : [](CommandId id, void * context) -> Loop {
542 1 : *reinterpret_cast<size_t *>(context) += 1;
543 1 : return Loop::Continue;
544 : },
545 : reinterpret_cast<void *>(&commandCount));
546 :
547 4 : if (err == CHIP_NO_ERROR)
548 : {
549 2 : ReturnErrorOnFailure(builder.EnsureAppendCapacity(commandCount));
550 :
551 : using EnumerationData = struct
552 : {
553 : DataModel::ListBuilder<CommandId> * generatedCommandList;
554 : CHIP_ERROR processingError;
555 : };
556 : EnumerationData enumerationData;
557 2 : enumerationData.processingError = CHIP_NO_ERROR;
558 2 : enumerationData.generatedCommandList = &builder;
559 :
560 3 : ReturnErrorOnFailure(interface->EnumerateGeneratedCommands(
561 : path,
562 : [](CommandId id, void * context) -> Loop {
563 : auto input = reinterpret_cast<EnumerationData *>(context);
564 :
565 : CHIP_ERROR appendError = input->generatedCommandList->Append(id);
566 : if (appendError != CHIP_NO_ERROR)
567 : {
568 : input->processingError = appendError;
569 : return Loop::Break;
570 : }
571 : return Loop::Continue;
572 : },
573 : reinterpret_cast<void *>(&enumerationData)));
574 2 : ReturnErrorOnFailure(enumerationData.processingError);
575 :
576 : // the two invocations MUST return the same sizes.
577 2 : VerifyOrReturnError(builder.Size() == commandCount, CHIP_ERROR_INTERNAL);
578 2 : return CHIP_NO_ERROR;
579 : }
580 2 : VerifyOrReturnError(err == CHIP_ERROR_NOT_IMPLEMENTED, err);
581 : }
582 :
583 369 : VerifyOrReturnError(serverCluster->generatedCommandList != nullptr, CHIP_NO_ERROR);
584 :
585 2 : const chip::CommandId * endOfList = serverCluster->generatedCommandList;
586 6 : while (*endOfList != kInvalidCommandId)
587 : {
588 4 : endOfList++;
589 : }
590 2 : const auto commandCount = static_cast<size_t>(endOfList - serverCluster->generatedCommandList);
591 2 : return builder.ReferenceExisting({ serverCluster->generatedCommandList, commandCount });
592 : }
593 :
594 386 : void CodegenDataModelProvider::InitDataModelForTesting()
595 : {
596 : // Call the Ember-specific InitDataModelHandler
597 386 : InitDataModelHandler();
598 386 : }
599 :
600 3 : CHIP_ERROR CodegenDataModelProvider::DeviceTypes(EndpointId endpointId,
601 : DataModel::ListBuilder<DataModel::DeviceTypeEntry> & builder)
602 : {
603 3 : std::optional<unsigned> endpoint_index = TryFindEndpointIndex(endpointId);
604 3 : if (!endpoint_index.has_value())
605 : {
606 0 : return {};
607 : }
608 :
609 3 : CHIP_ERROR err = CHIP_NO_ERROR;
610 :
611 3 : builder.ReferenceExisting(emberAfDeviceTypeListFromEndpointIndex(*endpoint_index, err));
612 3 : return CHIP_NO_ERROR;
613 : }
614 :
615 2 : CHIP_ERROR CodegenDataModelProvider::SemanticTags(EndpointId endpointId, DataModel::ListBuilder<SemanticTag> & builder)
616 : {
617 2 : DataModel::Provider::SemanticTag semanticTag;
618 2 : size_t count = 0;
619 :
620 5 : while (GetSemanticTagForEndpointAtIndex(endpointId, count, semanticTag) == CHIP_NO_ERROR)
621 : {
622 3 : count++;
623 : }
624 :
625 2 : ReturnErrorOnFailure(builder.EnsureAppendCapacity(count));
626 :
627 5 : for (size_t idx = 0; idx < count; idx++)
628 : {
629 3 : ReturnErrorOnFailure(GetSemanticTagForEndpointAtIndex(endpointId, idx, semanticTag));
630 3 : ReturnErrorOnFailure(builder.Append(semanticTag));
631 : }
632 :
633 2 : return CHIP_NO_ERROR;
634 : }
635 :
636 : } // namespace app
637 : } // namespace chip
|