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