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-detail.h>
42 : #include <app/util/attribute-storage.h>
43 : #include <app/util/endpoint-config-api.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/ScopedMemoryBuffer.h>
49 :
50 : #include <cstdint>
51 : #include <optional>
52 :
53 : namespace chip {
54 : namespace app {
55 : namespace {
56 :
57 119 : DataModel::AcceptedCommandEntry AcceptedCommandEntryFor(const ConcreteCommandPath & path)
58 : {
59 119 : const CommandId commandId = path.mCommandId;
60 :
61 : DataModel::AcceptedCommandEntry entry(
62 119 : path.mCommandId,
63 119 : BitFlags<DataModel::CommandQualityFlags>{}
64 119 : .Set(DataModel::CommandQualityFlags::kTimed, CommandNeedsTimedInvoke(path.mClusterId, commandId))
65 119 : .Set(DataModel::CommandQualityFlags::kFabricScoped, CommandIsFabricScoped(path.mClusterId, commandId))
66 119 : .Set(DataModel::CommandQualityFlags::kLargeMessage, CommandHasLargePayload(path.mClusterId, commandId)),
67 238 : RequiredPrivilege::ForInvokeCommand(path));
68 :
69 119 : return entry;
70 : }
71 :
72 15151 : DataModel::ServerClusterEntry ServerClusterEntryFrom(EndpointId endpointId, const EmberAfCluster & cluster)
73 : {
74 15151 : DataModel::ServerClusterEntry entry;
75 :
76 15151 : entry.clusterId = cluster.clusterId;
77 :
78 15151 : DataVersion * versionPtr = emberAfDataVersionStorage(ConcreteClusterPath(endpointId, cluster.clusterId));
79 15151 : 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 15151 : entry.dataVersion = *versionPtr;
90 : }
91 :
92 : // TODO: set entry flags:
93 : // entry.flags.Set(ClusterQualityFlags::kDiagnosticsData)
94 :
95 15151 : return entry;
96 : }
97 :
98 80922 : DataModel::AttributeEntry AttributeEntryFrom(const ConcreteClusterPath & clusterPath, const EmberAfAttributeMetadata & attribute)
99 : {
100 80922 : const ConcreteAttributePath attributePath(clusterPath.mEndpointId, clusterPath.mClusterId, attribute.attributeId);
101 :
102 : using DataModel::AttributeQualityFlags;
103 :
104 : DataModel::AttributeEntry entry(
105 80922 : attribute.attributeId,
106 80922 : BitFlags<DataModel::AttributeQualityFlags>{}
107 80922 : .Set(AttributeQualityFlags::kListAttribute, (attribute.attributeType == ZCL_ARRAY_ATTRIBUTE_TYPE))
108 80922 : .Set(DataModel::AttributeQualityFlags::kTimed, attribute.MustUseTimedWrite()),
109 161844 : attribute.IsReadable() ? std::make_optional(RequiredPrivilege::ForReadAttribute(attributePath)) : std::nullopt,
110 242766 : attribute.IsWritable() ? std::make_optional(RequiredPrivilege::ForWriteAttribute(attributePath)) : std::nullopt);
111 :
112 : // NOTE: we do NOT provide additional info for:
113 : // - IsExternal/IsAutomaticallyPersisted is not used by IM handling
114 : // - Several specification flags are not available (reportable, quieter reporting,
115 : // fixed, source attribution)
116 :
117 : // TODO: Set additional flags:
118 : // entry.flags.Set(DataModel::AttributeQualityFlags::kFabricScoped)
119 : // entry.flags.Set(DataModel::AttributeQualityFlags::kFabricSensitive)
120 : // entry.flags.Set(DataModel::AttributeQualityFlags::kChangesOmitted)
121 80922 : return entry;
122 : }
123 :
124 : DefaultAttributePersistenceProvider gDefaultAttributePersistence;
125 :
126 : } // namespace
127 :
128 141 : CHIP_ERROR CodegenDataModelProvider::Shutdown()
129 : {
130 : #if CHIP_CONFIG_ENABLE_SERVER_RESTART_SUPPORT
131 : // Shutdown ember cluster implementations (symmetric to InitDataModelForTesting()
132 : // in Startup which creates them). This calls the per-cluster shutdown callbacks
133 : // and unregisters AAI/CHI so clusters can be re-created on the next Startup().
134 141 : emAfCallShutdowns(MatterClusterShutdownType::kClusterShutdown);
135 141 : ChipLogProgress(DataManagement, "CodegenDataModelProvider::Shutdown() complete");
136 : #endif // CHIP_CONFIG_ENABLE_SERVER_RESTART_SUPPORT
137 141 : Reset();
138 141 : mContext.reset();
139 141 : mRegistry.ClearContext();
140 141 : return DataModel::Provider::Shutdown();
141 : }
142 :
143 198 : CHIP_ERROR CodegenDataModelProvider::Startup(DataModel::InteractionModelContext context)
144 : {
145 : // server clusters require a valid persistent storage delegate
146 198 : VerifyOrReturnError(mPersistentStorageDelegate != nullptr, CHIP_ERROR_INCORRECT_STATE);
147 81 : ReturnErrorOnFailure(DataModel::Provider::Startup(context));
148 :
149 81 : mContext.emplace(context);
150 :
151 : // Ember NVM requires have a data model provider. attempt to create one if one is not available
152 : //
153 : // It is not a critical failure to not have one, however if one is not set up, ember NVM operations
154 : // will error out with a `persistence not available`.
155 81 : if (GetAttributePersistenceProvider() == nullptr)
156 : {
157 : #if CHIP_CONFIG_DATA_MODEL_EXTRA_LOGGING
158 3 : ChipLogProgress(DataManagement, "Ember attribute persistence requires setting up");
159 : #endif
160 3 : ReturnErrorOnFailure(gDefaultAttributePersistence.Init(mPersistentStorageDelegate));
161 3 : SetAttributePersistenceProvider(&gDefaultAttributePersistence);
162 : }
163 :
164 81 : InitDataModelForTesting();
165 :
166 162 : return mRegistry.SetContext(ServerClusterContext{
167 : .provider = *this,
168 81 : .storage = *mPersistentStorageDelegate,
169 81 : .attributeStorage = *GetAttributePersistenceProvider(), // guaranteed set up by the above logic
170 81 : .interactionContext = *mContext, // NOLINT(bugprone-unchecked-optional-access): emplaced above
171 81 : });
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 returns responses via the handler, so std::nullopt must be returned here to follow the InvokeCommand API
199 : // contract
200 2 : DispatchSingleClusterCommand(request.path, input_arguments, handler);
201 2 : return std::nullopt;
202 : }
203 :
204 2979 : CHIP_ERROR CodegenDataModelProvider::Endpoints(ReadOnlyBufferBuilder<DataModel::EndpointEntry> & builder)
205 : {
206 2979 : const uint16_t endpointCount = emberAfEndpointCount();
207 :
208 2979 : ReturnErrorOnFailure(builder.EnsureAppendCapacity(endpointCount));
209 :
210 17385 : for (uint16_t endpointIndex = 0; endpointIndex < endpointCount; endpointIndex++)
211 : {
212 14406 : if (!emberAfEndpointIndexIsEnabled(endpointIndex))
213 : {
214 4344 : continue;
215 : }
216 :
217 : DataModel::EndpointEntry entry;
218 10062 : entry.id = emberAfEndpointFromIndex(endpointIndex);
219 10062 : entry.parentId = emberAfParentEndpointFromIndex(endpointIndex);
220 :
221 10062 : switch (GetCompositionForEndpointIndex(endpointIndex))
222 : {
223 8580 : case EndpointComposition::kFullFamily:
224 8580 : entry.compositionPattern = DataModel::EndpointCompositionPattern::kFullFamily;
225 8580 : break;
226 1482 : case EndpointComposition::kTree:
227 : case EndpointComposition::kInvalid: // should NOT happen, but force compiler to check we validate all versions
228 1482 : entry.compositionPattern = DataModel::EndpointCompositionPattern::kTree;
229 1482 : break;
230 : }
231 10062 : ReturnErrorOnFailure(builder.Append(entry));
232 : }
233 :
234 2979 : 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 2247 : CHIP_ERROR CodegenDataModelProvider::EventInfo(const ConcreteEventPath & path, DataModel::EventEntry & eventInfo)
258 : {
259 2247 : if (auto * cluster = mRegistry.Get(path); cluster != nullptr)
260 : {
261 3 : return cluster->EventInfo(path, eventInfo);
262 : }
263 :
264 2244 : eventInfo.readPrivilege = RequiredPrivilege::ForReadEvent(path);
265 2244 : return CHIP_NO_ERROR;
266 : }
267 :
268 8993 : CHIP_ERROR CodegenDataModelProvider::ServerClusters(EndpointId endpointId,
269 : ReadOnlyBufferBuilder<DataModel::ServerClusterEntry> & builder)
270 : {
271 8993 : const EmberAfEndpointType * endpoint = emberAfFindEndpointType(endpointId);
272 :
273 8993 : VerifyOrReturnValue(endpoint != nullptr, CHIP_ERROR_NOT_FOUND);
274 8978 : VerifyOrReturnValue(endpoint->clusterCount > 0, CHIP_NO_ERROR);
275 8978 : VerifyOrReturnValue(endpoint->cluster != nullptr, CHIP_NO_ERROR);
276 :
277 : // We build the cluster list by merging two lists:
278 : // - mRegistry items from ServerClusterInterfaces
279 : // - ember metadata clusters
280 : //
281 : // This is done because `ServerClusterInterface` allows full control for all its metadata,
282 : // in particular `data version` and `flags`.
283 : //
284 : // To allow cluster implementations to be incrementally converted to storing their own data versions,
285 : // instead of relying on the out-of-band emberAfDataVersionStorage, first check for clusters that are
286 : // using the new data version storage and are registered via SingleEndpointServerClusterRegistry, then fill
287 : // in the data versions for the rest via the out-of-band mechanism.
288 :
289 : // assume the clusters on endpoint does not change in between these two loops
290 8978 : auto clusters = mRegistry.ClustersOnEndpoint(endpointId);
291 8978 : size_t registryClusterCount = 0;
292 8980 : for ([[maybe_unused]] auto _ : clusters)
293 : {
294 2 : registryClusterCount++;
295 : }
296 :
297 8978 : ReturnErrorOnFailure(builder.EnsureAppendCapacity(registryClusterCount));
298 :
299 8978 : ReadOnlyBufferBuilder<ClusterId> knownClustersBuilder;
300 8978 : ReturnErrorOnFailure(knownClustersBuilder.EnsureAppendCapacity(registryClusterCount));
301 8980 : for (const auto clusterId : mRegistry.ClustersOnEndpoint(endpointId))
302 : {
303 2 : ConcreteClusterPath path(endpointId, clusterId);
304 2 : ServerClusterInterface * cluster = mRegistry.Get(path);
305 :
306 : // path MUST be valid: we just got it from iterating our registrations...
307 2 : VerifyOrReturnError(cluster != nullptr, CHIP_ERROR_INTERNAL);
308 :
309 2 : ReturnErrorOnFailure(builder.Append({
310 : .clusterId = path.mClusterId,
311 : .dataVersion = cluster->GetDataVersion(path),
312 : .flags = cluster->GetClusterFlags(path),
313 : }));
314 2 : ReturnErrorOnFailure(knownClustersBuilder.Append(path.mClusterId));
315 : }
316 :
317 8978 : ReadOnlyBuffer<ClusterId> knownClusters = knownClustersBuilder.TakeBuffer();
318 :
319 8978 : ReturnErrorOnFailure(builder.EnsureAppendCapacity(emberAfClusterCountForEndpointType(endpoint, /* server = */ true)));
320 :
321 8978 : const EmberAfCluster * begin = endpoint->cluster;
322 8978 : const EmberAfCluster * end = endpoint->cluster + endpoint->clusterCount;
323 25647 : for (const EmberAfCluster * cluster = begin; cluster != end; cluster++)
324 : {
325 16669 : if (!cluster->IsServer())
326 : {
327 1516 : continue;
328 : }
329 :
330 : // linear search as this is a somewhat compact number list, so performance is probably not too bad
331 : // This results in smaller code than some memory allocation + std::sort + std::binary_search
332 15153 : bool found = false;
333 15156 : for (ClusterId clusterId : knownClusters)
334 : {
335 5 : if (clusterId == cluster->clusterId)
336 : {
337 2 : found = true;
338 2 : break;
339 : }
340 : }
341 15153 : if (found)
342 : {
343 : // value already filled from the ServerClusterRegistry. That one has the correct/overriden
344 : // flags and data version
345 2 : continue;
346 : }
347 :
348 15151 : ReturnErrorOnFailure(builder.Append(ServerClusterEntryFrom(endpointId, *cluster)));
349 : }
350 :
351 8978 : return CHIP_NO_ERROR;
352 8978 : }
353 :
354 19002 : CHIP_ERROR CodegenDataModelProvider::Attributes(const ConcreteClusterPath & path,
355 : ReadOnlyBufferBuilder<DataModel::AttributeEntry> & builder)
356 : {
357 19002 : if (auto * cluster = mRegistry.Get(path); cluster != nullptr)
358 : {
359 1 : return cluster->Attributes(path, builder);
360 : }
361 :
362 19001 : const EmberAfCluster * cluster = FindServerCluster(path);
363 :
364 19001 : VerifyOrReturnValue(cluster != nullptr, CHIP_ERROR_NOT_FOUND);
365 18960 : VerifyOrReturnValue(cluster->attributeCount > 0, CHIP_NO_ERROR);
366 18960 : VerifyOrReturnValue(cluster->attributes != nullptr, CHIP_NO_ERROR);
367 :
368 : // TODO: if ember would encode data in AttributeEntry form, we could reference things directly (shorter code,
369 : // although still allocation overhead due to global attributes not in metadata)
370 : //
371 : // We have Attributes from ember + global attributes that are NOT in ember metadata.
372 : // We have to report them all
373 18960 : constexpr size_t kGlobalAttributeNotInMetadataCount = MATTER_ARRAY_SIZE(GlobalAttributesNotInMetadata);
374 :
375 18960 : ReturnErrorOnFailure(builder.EnsureAppendCapacity(cluster->attributeCount + kGlobalAttributeNotInMetadataCount));
376 :
377 18960 : Span<const EmberAfAttributeMetadata> attributeSpan(cluster->attributes, cluster->attributeCount);
378 :
379 99882 : for (auto & attribute : attributeSpan)
380 : {
381 80922 : ReturnErrorOnFailure(builder.Append(AttributeEntryFrom(path, attribute)));
382 : }
383 :
384 75840 : for (auto & attributeId : GlobalAttributesNotInMetadata)
385 : {
386 :
387 : // This "GlobalListEntry" is specific for metadata that ember does not include
388 : // in its attribute list metadata.
389 : //
390 : // By spec these Attribute/AcceptedCommands/GeneratedCommants lists are:
391 : // - lists of elements
392 : // - read-only, with read privilege view
393 : // - fixed value (no such flag exists, so this is not a quality flag we set/track)
394 : DataModel::AttributeEntry globalListEntry(attributeId, DataModel::AttributeQualityFlags::kListAttribute,
395 56880 : Access::Privilege::kView, std::nullopt);
396 :
397 56880 : ReturnErrorOnFailure(builder.Append(std::move(globalListEntry)));
398 : }
399 :
400 18960 : return CHIP_NO_ERROR;
401 : }
402 :
403 4 : CHIP_ERROR CodegenDataModelProvider::ClientClusters(EndpointId endpointId, ReadOnlyBufferBuilder<ClusterId> & builder)
404 : {
405 4 : const EmberAfEndpointType * endpoint = emberAfFindEndpointType(endpointId);
406 :
407 4 : VerifyOrReturnValue(endpoint != nullptr, CHIP_ERROR_NOT_FOUND);
408 2 : VerifyOrReturnValue(endpoint->clusterCount > 0, CHIP_NO_ERROR);
409 2 : VerifyOrReturnValue(endpoint->cluster != nullptr, CHIP_NO_ERROR);
410 :
411 2 : ReturnErrorOnFailure(builder.EnsureAppendCapacity(emberAfClusterCountForEndpointType(endpoint, /* server = */ false)));
412 :
413 2 : const EmberAfCluster * begin = endpoint->cluster;
414 2 : const EmberAfCluster * end = endpoint->cluster + endpoint->clusterCount;
415 10 : for (const EmberAfCluster * cluster = begin; cluster != end; cluster++)
416 : {
417 8 : if (!cluster->IsClient())
418 : {
419 4 : continue;
420 : }
421 4 : ReturnErrorOnFailure(builder.Append(cluster->clusterId));
422 : }
423 :
424 2 : return CHIP_NO_ERROR;
425 : }
426 :
427 19980 : const EmberAfCluster * CodegenDataModelProvider::FindServerCluster(const ConcreteClusterPath & path)
428 : {
429 39333 : if (mPreviouslyFoundCluster.has_value() && (mPreviouslyFoundCluster->path == path) &&
430 19353 : (mEmberMetadataStructureGeneration == emberAfMetadataStructureGeneration()))
431 :
432 : {
433 19243 : return mPreviouslyFoundCluster->cluster;
434 : }
435 :
436 737 : const EmberAfCluster * cluster = emberAfFindServerCluster(path.mEndpointId, path.mClusterId);
437 737 : if (cluster != nullptr)
438 : {
439 675 : mPreviouslyFoundCluster = std::make_optional<ClusterReference>(path, cluster);
440 675 : mEmberMetadataStructureGeneration = emberAfMetadataStructureGeneration();
441 : }
442 737 : return cluster;
443 : }
444 :
445 527 : CHIP_ERROR CodegenDataModelProvider::AcceptedCommands(const ConcreteClusterPath & path,
446 : ReadOnlyBufferBuilder<DataModel::AcceptedCommandEntry> & builder)
447 : {
448 527 : if (auto * cluster = mRegistry.Get(path); cluster != nullptr)
449 : {
450 1 : return cluster->AcceptedCommands(path, builder);
451 : }
452 :
453 : // Some CommandHandlerInterface instances are registered of ALL endpoints, so make sure first that
454 : // the cluster actually exists on this endpoint before asking the CommandHandlerInterface what commands
455 : // it claims to support.
456 526 : const EmberAfCluster * serverCluster = FindServerCluster(path);
457 526 : VerifyOrReturnError(serverCluster != nullptr, CHIP_ERROR_NOT_FOUND);
458 :
459 : CommandHandlerInterface * interface =
460 513 : CommandHandlerInterfaceRegistry::Instance().GetCommandHandler(path.mEndpointId, path.mClusterId);
461 513 : if (interface != nullptr)
462 : {
463 20 : CHIP_ERROR err = interface->RetrieveAcceptedCommands(path, builder);
464 : // If retrieving the accepted commands returns CHIP_ERROR_NOT_IMPLEMENTED then continue with normal processing.
465 : // Otherwise we finished.
466 40 : VerifyOrReturnError(err == CHIP_ERROR_NOT_IMPLEMENTED, err);
467 : }
468 503 : VerifyOrReturnError(serverCluster->acceptedCommandList != nullptr, CHIP_NO_ERROR);
469 :
470 43 : const chip::CommandId * endOfList = serverCluster->acceptedCommandList;
471 162 : while (*endOfList != kInvalidCommandId)
472 : {
473 119 : endOfList++;
474 : }
475 43 : const auto commandCount = static_cast<size_t>(endOfList - serverCluster->acceptedCommandList);
476 :
477 : // TODO: if ember would store command entries, we could simplify this code to use static data
478 43 : ReturnErrorOnFailure(builder.EnsureAppendCapacity(commandCount));
479 :
480 43 : ConcreteCommandPath commandPath = ConcreteCommandPath(path.mEndpointId, path.mClusterId, kInvalidCommandId);
481 162 : for (const chip::CommandId * p = serverCluster->acceptedCommandList; p != endOfList; p++)
482 : {
483 119 : commandPath.mCommandId = *p;
484 119 : ReturnErrorOnFailure(builder.Append(AcceptedCommandEntryFor(commandPath)));
485 : }
486 :
487 43 : return CHIP_NO_ERROR;
488 : }
489 :
490 454 : CHIP_ERROR CodegenDataModelProvider::GeneratedCommands(const ConcreteClusterPath & path, ReadOnlyBufferBuilder<CommandId> & builder)
491 : {
492 454 : if (auto * cluster = mRegistry.Get(path); cluster != nullptr)
493 : {
494 1 : return cluster->GeneratedCommands(path, builder);
495 : }
496 :
497 : // Some CommandHandlerInterface instances are registered of ALL endpoints, so make sure first that
498 : // the cluster actually exists on this endpoint before asking the CommandHandlerInterface what commands
499 : // it claims to support.
500 453 : const EmberAfCluster * serverCluster = FindServerCluster(path);
501 453 : VerifyOrReturnError(serverCluster != nullptr, CHIP_ERROR_NOT_FOUND);
502 :
503 : CommandHandlerInterface * interface =
504 445 : CommandHandlerInterfaceRegistry::Instance().GetCommandHandler(path.mEndpointId, path.mClusterId);
505 445 : if (interface != nullptr)
506 : {
507 8 : CHIP_ERROR err = interface->RetrieveGeneratedCommands(path, builder);
508 : // If retrieving generated commands returns CHIP_ERROR_NOT_IMPLEMENTED then continue with normal procesing.
509 : // Otherwise we finished.
510 16 : VerifyOrReturnError(err == CHIP_ERROR_NOT_IMPLEMENTED, err);
511 : }
512 :
513 441 : VerifyOrReturnError(serverCluster->generatedCommandList != nullptr, CHIP_NO_ERROR);
514 :
515 2 : const chip::CommandId * endOfList = serverCluster->generatedCommandList;
516 6 : while (*endOfList != kInvalidCommandId)
517 : {
518 4 : endOfList++;
519 : }
520 2 : const auto commandCount = static_cast<size_t>(endOfList - serverCluster->generatedCommandList);
521 2 : return builder.ReferenceExisting({ serverCluster->generatedCommandList, commandCount });
522 : }
523 :
524 81 : void CodegenDataModelProvider::InitDataModelForTesting()
525 : {
526 : // Call the Ember-specific InitDataModelHandler
527 81 : InitDataModelHandler();
528 81 : }
529 :
530 3 : CHIP_ERROR CodegenDataModelProvider::DeviceTypes(EndpointId endpointId, ReadOnlyBufferBuilder<DataModel::DeviceTypeEntry> & builder)
531 : {
532 3 : std::optional<unsigned> endpoint_index = TryFindEndpointIndex(endpointId);
533 3 : if (!endpoint_index.has_value())
534 : {
535 0 : return {};
536 : }
537 :
538 3 : CHIP_ERROR err = CHIP_NO_ERROR;
539 :
540 3 : return builder.ReferenceExisting(emberAfDeviceTypeListFromEndpointIndex(*endpoint_index, err));
541 : }
542 :
543 : #if CHIP_CONFIG_USE_ENDPOINT_UNIQUE_ID
544 : CHIP_ERROR CodegenDataModelProvider::EndpointUniqueID(EndpointId endpointId, MutableCharSpan & epUniqueId)
545 : {
546 : return emberAfGetEndpointUniqueIdForEndPoint(endpointId, epUniqueId);
547 : }
548 : #endif
549 :
550 : } // namespace app
551 : } // namespace chip
|