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