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 72115 : DataModel::AttributeEntry AttributeEntryFrom(const ConcreteClusterPath & clusterPath, const EmberAfAttributeMetadata & attribute)
99 : {
100 72115 : const ConcreteAttributePath attributePath(clusterPath.mEndpointId, clusterPath.mClusterId, attribute.attributeId);
101 :
102 : using DataModel::AttributeQualityFlags;
103 :
104 : DataModel::AttributeEntry entry(
105 72115 : attribute.attributeId,
106 72115 : BitFlags<DataModel::AttributeQualityFlags>{}
107 72115 : .Set(AttributeQualityFlags::kListAttribute, (attribute.attributeType == ZCL_ARRAY_ATTRIBUTE_TYPE))
108 72115 : .Set(DataModel::AttributeQualityFlags::kTimed, attribute.MustUseTimedWrite()),
109 72115 : RequiredPrivilege::ForReadAttribute(attributePath),
110 216345 : 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 72115 : return entry;
123 : }
124 :
125 : DefaultAttributePersistenceProvider gDefaultAttributePersistence;
126 :
127 : } // namespace
128 :
129 159 : CHIP_ERROR CodegenDataModelProvider::Shutdown()
130 : {
131 159 : Reset();
132 159 : mRegistry.ClearContext();
133 159 : return CHIP_NO_ERROR;
134 : }
135 :
136 399 : CHIP_ERROR CodegenDataModelProvider::Startup(DataModel::InteractionModelContext context)
137 : {
138 399 : 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 399 : if (GetAttributePersistenceProvider() == nullptr)
145 : {
146 : #if CHIP_CONFIG_DATA_MODEL_EXTRA_LOGGING
147 396 : ChipLogProgress(DataManagement, "Ember attribute persistence requires setting up");
148 : #endif
149 396 : 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 394 : ChipLogError(DataManagement, "No storage delegate available, will not set up attribute persistence.");
158 : #endif
159 : }
160 : }
161 :
162 399 : InitDataModelForTesting();
163 :
164 399 : return mRegistry.SetContext(ServerClusterContext{
165 : .provider = this,
166 399 : .storage = mPersistentStorageDelegate,
167 399 : .interactionContext = &mContext,
168 399 : });
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 2936 : CHIP_ERROR CodegenDataModelProvider::Endpoints(ReadOnlyBufferBuilder<DataModel::EndpointEntry> & builder)
201 : {
202 2936 : const uint16_t endpointCount = emberAfEndpointCount();
203 :
204 2936 : ReturnErrorOnFailure(builder.EnsureAppendCapacity(endpointCount));
205 :
206 17190 : for (uint16_t endpointIndex = 0; endpointIndex < endpointCount; endpointIndex++)
207 : {
208 14254 : if (!emberAfEndpointIndexIsEnabled(endpointIndex))
209 : {
210 4333 : continue;
211 : }
212 :
213 : DataModel::EndpointEntry entry;
214 9921 : entry.id = emberAfEndpointFromIndex(endpointIndex);
215 9921 : entry.parentId = emberAfParentEndpointFromIndex(endpointIndex);
216 :
217 9921 : switch (GetCompositionForEndpointIndex(endpointIndex))
218 : {
219 8440 : case EndpointComposition::kFullFamily:
220 8440 : entry.compositionPattern = DataModel::EndpointCompositionPattern::kFullFamily;
221 8440 : 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 9921 : ReturnErrorOnFailure(builder.Append(entry));
228 : }
229 :
230 2936 : 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 2241 : CHIP_ERROR CodegenDataModelProvider::EventInfo(const ConcreteEventPath & path, DataModel::EventEntry & eventInfo)
254 : {
255 2241 : if (auto * cluster = mRegistry.Get(path); cluster != nullptr)
256 : {
257 2 : return cluster->EventInfo(path, eventInfo);
258 : }
259 :
260 2239 : eventInfo.readPrivilege = RequiredPrivilege::ForReadEvent(path);
261 2239 : return CHIP_NO_ERROR;
262 : }
263 :
264 8463 : CHIP_ERROR CodegenDataModelProvider::ServerClusters(EndpointId endpointId,
265 : ReadOnlyBufferBuilder<DataModel::ServerClusterEntry> & builder)
266 : {
267 8463 : const EmberAfEndpointType * endpoint = emberAfFindEndpointType(endpointId);
268 :
269 8463 : VerifyOrReturnValue(endpoint != nullptr, CHIP_ERROR_NOT_FOUND);
270 8454 : VerifyOrReturnValue(endpoint->clusterCount > 0, CHIP_NO_ERROR);
271 8454 : VerifyOrReturnValue(endpoint->cluster != nullptr, CHIP_NO_ERROR);
272 :
273 : // We build the cluster list by merging two lists:
274 : // - mRegistry items from ServerClusterInterfaces
275 : // - ember metadata clusters
276 : //
277 : // This is done because `ServerClusterInterface` allows full control for all its metadata,
278 : // in particular `data version` and `flags`.
279 : //
280 : // To allow cluster implementations to be incrementally converted to storing their own data versions,
281 : // instead of relying on the out-of-band emberAfDataVersionStorage, first check for clusters that are
282 : // using the new data version storage and are registered via ServerClusterInterfaceRegistry, then fill
283 : // in the data versions for the rest via the out-of-band mechanism.
284 :
285 : // assume the clusters on endpoint does not change in between these two loops
286 8454 : auto clusters = mRegistry.ClustersOnEndpoint(endpointId);
287 8454 : size_t registryClusterCount = 0;
288 8455 : for ([[maybe_unused]] auto _ : clusters)
289 : {
290 1 : registryClusterCount++;
291 : }
292 :
293 8454 : ReturnErrorOnFailure(builder.EnsureAppendCapacity(registryClusterCount));
294 :
295 8454 : ReadOnlyBufferBuilder<ClusterId> knownClustersBuilder;
296 8454 : ReturnErrorOnFailure(knownClustersBuilder.EnsureAppendCapacity(registryClusterCount));
297 8455 : for (const auto clusterId : mRegistry.ClustersOnEndpoint(endpointId))
298 : {
299 1 : ConcreteClusterPath path(endpointId, clusterId);
300 1 : ServerClusterInterface * cluster = mRegistry.Get(path);
301 :
302 : // path MUST be valid: we just got it from iterating our registrations...
303 1 : VerifyOrReturnError(cluster != nullptr, CHIP_ERROR_INTERNAL);
304 :
305 1 : ReturnErrorOnFailure(builder.Append({
306 : .clusterId = path.mClusterId,
307 : .dataVersion = cluster->GetDataVersion(path),
308 : .flags = cluster->GetClusterFlags(path),
309 : }));
310 1 : ReturnErrorOnFailure(knownClustersBuilder.Append(path.mClusterId));
311 : }
312 :
313 8454 : ReadOnlyBuffer<ClusterId> knownClusters = knownClustersBuilder.TakeBuffer();
314 :
315 8454 : ReturnErrorOnFailure(builder.EnsureAppendCapacity(emberAfClusterCountForEndpointType(endpoint, /* server = */ true)));
316 :
317 8454 : const EmberAfCluster * begin = endpoint->cluster;
318 8454 : const EmberAfCluster * end = endpoint->cluster + endpoint->clusterCount;
319 21420 : for (const EmberAfCluster * cluster = begin; cluster != end; cluster++)
320 : {
321 12966 : if (!cluster->IsServer())
322 : {
323 1503 : continue;
324 : }
325 :
326 : // linear search as this is a somewhat compact number list, so performance is probably not too bad
327 : // This results in smaller code than some memory allocation + std::sort + std::binary_search
328 11463 : bool found = false;
329 11464 : for (ClusterId clusterId : knownClusters)
330 : {
331 2 : if (clusterId == cluster->clusterId)
332 : {
333 1 : found = true;
334 1 : break;
335 : }
336 : }
337 11463 : if (found)
338 : {
339 : // value already filled from the ServerClusterRegistry. That one has the correct/overriden
340 : // flags and data version
341 1 : continue;
342 : }
343 :
344 11462 : ReturnErrorOnFailure(builder.Append(ServerClusterEntryFrom(endpointId, *cluster)));
345 : }
346 :
347 8454 : return CHIP_NO_ERROR;
348 8454 : }
349 :
350 18334 : CHIP_ERROR CodegenDataModelProvider::Attributes(const ConcreteClusterPath & path,
351 : ReadOnlyBufferBuilder<DataModel::AttributeEntry> & builder)
352 : {
353 18334 : if (auto * cluster = mRegistry.Get(path); cluster != nullptr)
354 : {
355 1 : return cluster->Attributes(path, builder);
356 : }
357 :
358 18333 : const EmberAfCluster * cluster = FindServerCluster(path);
359 :
360 18333 : VerifyOrReturnValue(cluster != nullptr, CHIP_ERROR_NOT_FOUND);
361 17312 : VerifyOrReturnValue(cluster->attributeCount > 0, CHIP_NO_ERROR);
362 17312 : VerifyOrReturnValue(cluster->attributes != nullptr, CHIP_NO_ERROR);
363 :
364 : // TODO: if ember would encode data in AttributeEntry form, we could reference things directly (shorter code,
365 : // although still allocation overhead due to global attributes not in metadata)
366 : //
367 : // We have Attributes from ember + global attributes that are NOT in ember metadata.
368 : // We have to report them all
369 17312 : constexpr size_t kGlobalAttributeNotInMetadataCount = MATTER_ARRAY_SIZE(GlobalAttributesNotInMetadata);
370 :
371 17312 : ReturnErrorOnFailure(builder.EnsureAppendCapacity(cluster->attributeCount + kGlobalAttributeNotInMetadataCount));
372 :
373 17312 : Span<const EmberAfAttributeMetadata> attributeSpan(cluster->attributes, cluster->attributeCount);
374 :
375 89427 : for (auto & attribute : attributeSpan)
376 : {
377 72115 : ReturnErrorOnFailure(builder.Append(AttributeEntryFrom(path, attribute)));
378 : }
379 :
380 69248 : for (auto & attributeId : GlobalAttributesNotInMetadata)
381 : {
382 :
383 : // This "GlobalListEntry" is specific for metadata that ember does not include
384 : // in its attribute list metadata.
385 : //
386 : // By spec these Attribute/AcceptedCommands/GeneratedCommants lists are:
387 : // - lists of elements
388 : // - read-only, with read privilege view
389 : // - fixed value (no such flag exists, so this is not a quality flag we set/track)
390 : DataModel::AttributeEntry globalListEntry(attributeId, DataModel::AttributeQualityFlags::kListAttribute,
391 51936 : Access::Privilege::kView, std::nullopt);
392 :
393 51936 : ReturnErrorOnFailure(builder.Append(std::move(globalListEntry)));
394 : }
395 :
396 17312 : return CHIP_NO_ERROR;
397 : }
398 :
399 4 : CHIP_ERROR CodegenDataModelProvider::ClientClusters(EndpointId endpointId, ReadOnlyBufferBuilder<ClusterId> & builder)
400 : {
401 4 : const EmberAfEndpointType * endpoint = emberAfFindEndpointType(endpointId);
402 :
403 4 : VerifyOrReturnValue(endpoint != nullptr, CHIP_ERROR_NOT_FOUND);
404 2 : VerifyOrReturnValue(endpoint->clusterCount > 0, CHIP_NO_ERROR);
405 2 : VerifyOrReturnValue(endpoint->cluster != nullptr, CHIP_NO_ERROR);
406 :
407 2 : ReturnErrorOnFailure(builder.EnsureAppendCapacity(emberAfClusterCountForEndpointType(endpoint, /* server = */ false)));
408 :
409 2 : const EmberAfCluster * begin = endpoint->cluster;
410 2 : const EmberAfCluster * end = endpoint->cluster + endpoint->clusterCount;
411 10 : for (const EmberAfCluster * cluster = begin; cluster != end; cluster++)
412 : {
413 8 : if (!cluster->IsClient())
414 : {
415 4 : continue;
416 : }
417 4 : ReturnErrorOnFailure(builder.Append(cluster->clusterId));
418 : }
419 :
420 2 : return CHIP_NO_ERROR;
421 : }
422 :
423 19138 : const EmberAfCluster * CodegenDataModelProvider::FindServerCluster(const ConcreteClusterPath & path)
424 : {
425 36903 : if (mPreviouslyFoundCluster.has_value() && (mPreviouslyFoundCluster->path == path) &&
426 17765 : (mEmberMetadataStructureGeneration == emberAfMetadataStructureGeneration()))
427 :
428 : {
429 17656 : return mPreviouslyFoundCluster->cluster;
430 : }
431 :
432 1482 : const EmberAfCluster * cluster = emberAfFindServerCluster(path.mEndpointId, path.mClusterId);
433 1482 : if (cluster != nullptr)
434 : {
435 448 : mPreviouslyFoundCluster = std::make_optional<ClusterReference>(path, cluster);
436 448 : mEmberMetadataStructureGeneration = emberAfMetadataStructureGeneration();
437 : }
438 1482 : return cluster;
439 : }
440 :
441 429 : CHIP_ERROR CodegenDataModelProvider::AcceptedCommands(const ConcreteClusterPath & path,
442 : ReadOnlyBufferBuilder<DataModel::AcceptedCommandEntry> & builder)
443 : {
444 429 : if (auto * cluster = mRegistry.Get(path); cluster != nullptr)
445 : {
446 1 : return cluster->AcceptedCommands(path, builder);
447 : }
448 :
449 : // Some CommandHandlerInterface instances are registered of ALL endpoints, so make sure first that
450 : // the cluster actually exists on this endpoint before asking the CommandHandlerInterface what commands
451 : // it claims to support.
452 428 : const EmberAfCluster * serverCluster = FindServerCluster(path);
453 428 : VerifyOrReturnError(serverCluster != nullptr, CHIP_ERROR_NOT_FOUND);
454 :
455 : CommandHandlerInterface * interface =
456 421 : CommandHandlerInterfaceRegistry::Instance().GetCommandHandler(path.mEndpointId, path.mClusterId);
457 421 : if (interface != nullptr)
458 : {
459 16 : size_t commandCount = 0;
460 :
461 16 : CHIP_ERROR err = interface->EnumerateAcceptedCommands(
462 : path,
463 6 : [](CommandId id, void * context) -> Loop {
464 6 : *reinterpret_cast<size_t *>(context) += 1;
465 6 : return Loop::Continue;
466 : },
467 : reinterpret_cast<void *>(&commandCount));
468 :
469 16 : if (err == CHIP_NO_ERROR)
470 : {
471 : using EnumerationData = struct
472 : {
473 : ConcreteCommandPath commandPath;
474 : ReadOnlyBufferBuilder<DataModel::AcceptedCommandEntry> * acceptedCommandList;
475 : CHIP_ERROR processingError;
476 : };
477 :
478 8 : EnumerationData enumerationData;
479 8 : enumerationData.commandPath = ConcreteCommandPath(path.mEndpointId, path.mClusterId, kInvalidCommandId);
480 8 : enumerationData.processingError = CHIP_NO_ERROR;
481 8 : enumerationData.acceptedCommandList = &builder;
482 :
483 8 : ReturnErrorOnFailure(builder.EnsureAppendCapacity(commandCount));
484 :
485 14 : ReturnErrorOnFailure(interface->EnumerateAcceptedCommands(
486 : path,
487 : [](CommandId commandId, void * context) -> Loop {
488 : auto input = reinterpret_cast<EnumerationData *>(context);
489 : input->commandPath.mCommandId = commandId;
490 : CHIP_ERROR appendError = input->acceptedCommandList->Append(AcceptedCommandEntryFor(input->commandPath));
491 : if (appendError != CHIP_NO_ERROR)
492 : {
493 : input->processingError = appendError;
494 : return Loop::Break;
495 : }
496 : return Loop::Continue;
497 : },
498 : reinterpret_cast<void *>(&enumerationData)));
499 8 : ReturnErrorOnFailure(enumerationData.processingError);
500 :
501 : // the two invocations MUST return the same sizes.
502 8 : VerifyOrReturnError(builder.Size() == commandCount, CHIP_ERROR_INTERNAL);
503 8 : return CHIP_NO_ERROR;
504 : }
505 8 : VerifyOrReturnError(err == CHIP_ERROR_NOT_IMPLEMENTED, err);
506 : }
507 :
508 413 : VerifyOrReturnError(serverCluster->acceptedCommandList != nullptr, CHIP_NO_ERROR);
509 :
510 28 : const chip::CommandId * endOfList = serverCluster->acceptedCommandList;
511 101 : while (*endOfList != kInvalidCommandId)
512 : {
513 73 : endOfList++;
514 : }
515 28 : const auto commandCount = static_cast<size_t>(endOfList - serverCluster->acceptedCommandList);
516 :
517 : // TODO: if ember would store command entries, we could simplify this code to use static data
518 28 : ReturnErrorOnFailure(builder.EnsureAppendCapacity(commandCount));
519 :
520 28 : ConcreteCommandPath commandPath = ConcreteCommandPath(path.mEndpointId, path.mClusterId, kInvalidCommandId);
521 101 : for (const chip::CommandId * p = serverCluster->acceptedCommandList; p != endOfList; p++)
522 : {
523 73 : commandPath.mCommandId = *p;
524 73 : ReturnErrorOnFailure(builder.Append(AcceptedCommandEntryFor(commandPath)));
525 : }
526 :
527 28 : return CHIP_NO_ERROR;
528 : }
529 :
530 378 : CHIP_ERROR CodegenDataModelProvider::GeneratedCommands(const ConcreteClusterPath & path, ReadOnlyBufferBuilder<CommandId> & builder)
531 : {
532 378 : if (auto * cluster = mRegistry.Get(path); cluster != nullptr)
533 : {
534 1 : return cluster->GeneratedCommands(path, builder);
535 : }
536 :
537 : // Some CommandHandlerInterface instances are registered of ALL endpoints, so make sure first that
538 : // the cluster actually exists on this endpoint before asking the CommandHandlerInterface what commands
539 : // it claims to support.
540 377 : const EmberAfCluster * serverCluster = FindServerCluster(path);
541 377 : VerifyOrReturnError(serverCluster != nullptr, CHIP_ERROR_NOT_FOUND);
542 :
543 : CommandHandlerInterface * interface =
544 371 : CommandHandlerInterfaceRegistry::Instance().GetCommandHandler(path.mEndpointId, path.mClusterId);
545 371 : if (interface != nullptr)
546 : {
547 4 : size_t commandCount = 0;
548 :
549 4 : CHIP_ERROR err = interface->EnumerateGeneratedCommands(
550 : path,
551 1 : [](CommandId id, void * context) -> Loop {
552 1 : *reinterpret_cast<size_t *>(context) += 1;
553 1 : return Loop::Continue;
554 : },
555 : reinterpret_cast<void *>(&commandCount));
556 :
557 4 : if (err == CHIP_NO_ERROR)
558 : {
559 2 : ReturnErrorOnFailure(builder.EnsureAppendCapacity(commandCount));
560 :
561 : using EnumerationData = struct
562 : {
563 : ReadOnlyBufferBuilder<CommandId> * generatedCommandList;
564 : CHIP_ERROR processingError;
565 : };
566 : EnumerationData enumerationData;
567 2 : enumerationData.processingError = CHIP_NO_ERROR;
568 2 : enumerationData.generatedCommandList = &builder;
569 :
570 3 : ReturnErrorOnFailure(interface->EnumerateGeneratedCommands(
571 : path,
572 : [](CommandId id, void * context) -> Loop {
573 : auto input = reinterpret_cast<EnumerationData *>(context);
574 :
575 : CHIP_ERROR appendError = input->generatedCommandList->Append(id);
576 : if (appendError != CHIP_NO_ERROR)
577 : {
578 : input->processingError = appendError;
579 : return Loop::Break;
580 : }
581 : return Loop::Continue;
582 : },
583 : reinterpret_cast<void *>(&enumerationData)));
584 2 : ReturnErrorOnFailure(enumerationData.processingError);
585 :
586 : // the two invocations MUST return the same sizes.
587 2 : VerifyOrReturnError(builder.Size() == commandCount, CHIP_ERROR_INTERNAL);
588 2 : return CHIP_NO_ERROR;
589 : }
590 2 : VerifyOrReturnError(err == CHIP_ERROR_NOT_IMPLEMENTED, err);
591 : }
592 :
593 369 : VerifyOrReturnError(serverCluster->generatedCommandList != nullptr, CHIP_NO_ERROR);
594 :
595 2 : const chip::CommandId * endOfList = serverCluster->generatedCommandList;
596 6 : while (*endOfList != kInvalidCommandId)
597 : {
598 4 : endOfList++;
599 : }
600 2 : const auto commandCount = static_cast<size_t>(endOfList - serverCluster->generatedCommandList);
601 2 : return builder.ReferenceExisting({ serverCluster->generatedCommandList, commandCount });
602 : }
603 :
604 392 : void CodegenDataModelProvider::InitDataModelForTesting()
605 : {
606 : // Call the Ember-specific InitDataModelHandler
607 392 : InitDataModelHandler();
608 392 : }
609 :
610 3 : CHIP_ERROR CodegenDataModelProvider::DeviceTypes(EndpointId endpointId, ReadOnlyBufferBuilder<DataModel::DeviceTypeEntry> & builder)
611 : {
612 3 : std::optional<unsigned> endpoint_index = TryFindEndpointIndex(endpointId);
613 3 : if (!endpoint_index.has_value())
614 : {
615 0 : return {};
616 : }
617 :
618 3 : CHIP_ERROR err = CHIP_NO_ERROR;
619 :
620 3 : return builder.ReferenceExisting(emberAfDeviceTypeListFromEndpointIndex(*endpoint_index, err));
621 : }
622 :
623 2 : CHIP_ERROR CodegenDataModelProvider::SemanticTags(EndpointId endpointId, ReadOnlyBufferBuilder<SemanticTag> & builder)
624 : {
625 2 : DataModel::Provider::SemanticTag semanticTag;
626 2 : size_t count = 0;
627 :
628 5 : while (GetSemanticTagForEndpointAtIndex(endpointId, count, semanticTag) == CHIP_NO_ERROR)
629 : {
630 3 : count++;
631 : }
632 :
633 2 : ReturnErrorOnFailure(builder.EnsureAppendCapacity(count));
634 :
635 5 : for (size_t idx = 0; idx < count; idx++)
636 : {
637 3 : ReturnErrorOnFailure(GetSemanticTagForEndpointAtIndex(endpointId, idx, semanticTag));
638 3 : ReturnErrorOnFailure(builder.Append(semanticTag));
639 : }
640 :
641 2 : return CHIP_NO_ERROR;
642 : }
643 : #if CHIP_CONFIG_USE_ENDPOINT_UNIQUE_ID
644 : CHIP_ERROR CodegenDataModelProvider::EndpointUniqueID(EndpointId endpointId, MutableCharSpan & epUniqueId)
645 : {
646 : char buffer[Clusters::Descriptor::Attributes::EndpointUniqueID::TypeInfo::MaxLength()] = { 0 };
647 : MutableCharSpan epUniqueIdSpan(buffer);
648 : emberAfGetEndpointUniqueIdForEndPoint(endpointId, epUniqueIdSpan);
649 :
650 : memcpy(epUniqueId.data(), epUniqueIdSpan.data(), epUniqueIdSpan.size());
651 : return CHIP_NO_ERROR;
652 : }
653 : #endif
654 :
655 : } // namespace app
656 : } // namespace chip
|