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