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 : #include <lib/support/SpanSearchValue.h>
49 :
50 : #include <cstdint>
51 : #include <optional>
52 :
53 : namespace chip {
54 : namespace app {
55 : namespace {
56 :
57 98 : DataModel::AcceptedCommandEntry AcceptedCommandEntryFor(const ConcreteCommandPath & path)
58 : {
59 98 : const CommandId commandId = path.mCommandId;
60 :
61 : DataModel::AcceptedCommandEntry entry(
62 98 : path.mCommandId,
63 98 : BitFlags<DataModel::CommandQualityFlags>{}
64 98 : .Set(DataModel::CommandQualityFlags::kTimed, CommandNeedsTimedInvoke(path.mClusterId, commandId))
65 98 : .Set(DataModel::CommandQualityFlags::kFabricScoped, CommandIsFabricScoped(path.mClusterId, commandId))
66 98 : .Set(DataModel::CommandQualityFlags::kLargeMessage, CommandHasLargePayload(path.mClusterId, commandId)),
67 196 : RequiredPrivilege::ForInvokeCommand(path));
68 :
69 98 : return entry;
70 : }
71 :
72 15156 : DataModel::ServerClusterEntry ServerClusterEntryFrom(EndpointId endpointId, const EmberAfCluster & cluster)
73 : {
74 15156 : DataModel::ServerClusterEntry entry;
75 :
76 15156 : entry.clusterId = cluster.clusterId;
77 :
78 15156 : DataVersion * versionPtr = emberAfDataVersionStorage(ConcreteClusterPath(endpointId, cluster.clusterId));
79 15156 : 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 15156 : entry.dataVersion = *versionPtr;
90 : }
91 :
92 : // TODO: set entry flags:
93 : // entry.flags.Set(ClusterQualityFlags::kDiagnosticsData)
94 :
95 15156 : return entry;
96 : }
97 :
98 80995 : DataModel::AttributeEntry AttributeEntryFrom(const ConcreteClusterPath & clusterPath, const EmberAfAttributeMetadata & attribute)
99 : {
100 80995 : const ConcreteAttributePath attributePath(clusterPath.mEndpointId, clusterPath.mClusterId, attribute.attributeId);
101 :
102 : using DataModel::AttributeQualityFlags;
103 :
104 : DataModel::AttributeEntry entry(
105 80995 : attribute.attributeId,
106 80995 : BitFlags<DataModel::AttributeQualityFlags>{}
107 80995 : .Set(AttributeQualityFlags::kListAttribute, (attribute.attributeType == ZCL_ARRAY_ATTRIBUTE_TYPE))
108 80995 : .Set(DataModel::AttributeQualityFlags::kTimed, attribute.MustUseTimedWrite()),
109 161990 : attribute.IsReadable() ? std::make_optional(RequiredPrivilege::ForReadAttribute(attributePath)) : std::nullopt,
110 242985 : 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 80995 : return entry;
122 : }
123 :
124 : DefaultAttributePersistenceProvider gDefaultAttributePersistence;
125 :
126 : } // namespace
127 :
128 164 : CHIP_ERROR CodegenDataModelProvider::Shutdown()
129 : {
130 164 : Reset();
131 164 : mContext.reset();
132 164 : mRegistry.ClearContext();
133 164 : return DataModel::Provider::Shutdown();
134 : }
135 :
136 413 : CHIP_ERROR CodegenDataModelProvider::Startup(DataModel::InteractionModelContext context)
137 : {
138 : // server clusters require a valid persistent storage delegate
139 413 : VerifyOrReturnError(mPersistentStorageDelegate != nullptr, CHIP_ERROR_INCORRECT_STATE);
140 152 : ReturnErrorOnFailure(DataModel::Provider::Startup(context));
141 :
142 152 : mContext.emplace(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 152 : if (GetAttributePersistenceProvider() == nullptr)
149 : {
150 : #if CHIP_CONFIG_DATA_MODEL_EXTRA_LOGGING
151 3 : ChipLogProgress(DataManagement, "Ember attribute persistence requires setting up");
152 : #endif
153 3 : ReturnErrorOnFailure(gDefaultAttributePersistence.Init(mPersistentStorageDelegate));
154 3 : SetAttributePersistenceProvider(&gDefaultAttributePersistence);
155 : }
156 :
157 152 : InitDataModelForTesting();
158 :
159 304 : return mRegistry.SetContext(ServerClusterContext{
160 : .provider = *this,
161 152 : .storage = *mPersistentStorageDelegate,
162 152 : .attributeStorage = *GetAttributePersistenceProvider(), // guaranteed set up by the above logic
163 152 : .interactionContext = *mContext, // NOLINT(bugprone-unchecked-optional-access): emplaced above
164 152 : });
165 : }
166 :
167 6 : std::optional<DataModel::ActionReturnStatus> CodegenDataModelProvider::InvokeCommand(const DataModel::InvokeRequest & request,
168 : TLV::TLVReader & input_arguments,
169 : CommandHandler * handler)
170 : {
171 6 : if (auto * cluster = mRegistry.Get(request.path); cluster != nullptr)
172 : {
173 1 : return cluster->InvokeCommand(request, input_arguments, handler);
174 : }
175 :
176 : CommandHandlerInterface * handler_interface =
177 5 : CommandHandlerInterfaceRegistry::Instance().GetCommandHandler(request.path.mEndpointId, request.path.mClusterId);
178 :
179 5 : if (handler_interface)
180 : {
181 3 : CommandHandlerInterface::HandlerContext context(*handler, request.path, input_arguments);
182 3 : handler_interface->InvokeCommand(context);
183 :
184 : // If the command was handled, don't proceed any further and return successfully.
185 3 : if (context.mCommandHandled)
186 : {
187 3 : return std::nullopt;
188 : }
189 : }
190 :
191 : // Ember always sets the return in the handler
192 2 : DispatchSingleClusterCommand(request.path, input_arguments, handler);
193 2 : return std::nullopt;
194 : }
195 :
196 2947 : CHIP_ERROR CodegenDataModelProvider::Endpoints(ReadOnlyBufferBuilder<DataModel::EndpointEntry> & builder)
197 : {
198 2947 : const uint16_t endpointCount = emberAfEndpointCount();
199 :
200 2947 : ReturnErrorOnFailure(builder.EnsureAppendCapacity(endpointCount));
201 :
202 17249 : for (uint16_t endpointIndex = 0; endpointIndex < endpointCount; endpointIndex++)
203 : {
204 14302 : if (!emberAfEndpointIndexIsEnabled(endpointIndex))
205 : {
206 4333 : continue;
207 : }
208 :
209 : DataModel::EndpointEntry entry;
210 9969 : entry.id = emberAfEndpointFromIndex(endpointIndex);
211 9969 : entry.parentId = emberAfParentEndpointFromIndex(endpointIndex);
212 :
213 9969 : switch (GetCompositionForEndpointIndex(endpointIndex))
214 : {
215 8488 : case EndpointComposition::kFullFamily:
216 8488 : entry.compositionPattern = DataModel::EndpointCompositionPattern::kFullFamily;
217 8488 : 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 9969 : ReturnErrorOnFailure(builder.Append(entry));
224 : }
225 :
226 2947 : 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 8994 : CHIP_ERROR CodegenDataModelProvider::ServerClusters(EndpointId endpointId,
261 : ReadOnlyBufferBuilder<DataModel::ServerClusterEntry> & builder)
262 : {
263 8994 : const EmberAfEndpointType * endpoint = emberAfFindEndpointType(endpointId);
264 :
265 8994 : VerifyOrReturnValue(endpoint != nullptr, CHIP_ERROR_NOT_FOUND);
266 8983 : VerifyOrReturnValue(endpoint->clusterCount > 0, CHIP_NO_ERROR);
267 8983 : 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 8983 : auto clusters = mRegistry.ClustersOnEndpoint(endpointId);
283 8983 : size_t registryClusterCount = 0;
284 8985 : for ([[maybe_unused]] auto _ : clusters)
285 : {
286 2 : registryClusterCount++;
287 : }
288 :
289 8983 : ReturnErrorOnFailure(builder.EnsureAppendCapacity(registryClusterCount));
290 :
291 8983 : ReadOnlyBufferBuilder<ClusterId> knownClustersBuilder;
292 8983 : ReturnErrorOnFailure(knownClustersBuilder.EnsureAppendCapacity(registryClusterCount));
293 8985 : 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 8983 : ReadOnlyBuffer<ClusterId> knownClusters = knownClustersBuilder.TakeBuffer();
310 :
311 8983 : ReturnErrorOnFailure(builder.EnsureAppendCapacity(emberAfClusterCountForEndpointType(endpoint, /* server = */ true)));
312 :
313 8983 : const EmberAfCluster * begin = endpoint->cluster;
314 8983 : const EmberAfCluster * end = endpoint->cluster + endpoint->clusterCount;
315 25644 : for (const EmberAfCluster * cluster = begin; cluster != end; cluster++)
316 : {
317 16661 : 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 15158 : bool found = false;
325 15161 : for (ClusterId clusterId : knownClusters)
326 : {
327 5 : if (clusterId == cluster->clusterId)
328 : {
329 2 : found = true;
330 2 : break;
331 : }
332 : }
333 15158 : 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 15156 : ReturnErrorOnFailure(builder.Append(ServerClusterEntryFrom(endpointId, *cluster)));
341 : }
342 :
343 8983 : return CHIP_NO_ERROR;
344 8983 : }
345 :
346 19010 : CHIP_ERROR CodegenDataModelProvider::Attributes(const ConcreteClusterPath & path,
347 : ReadOnlyBufferBuilder<DataModel::AttributeEntry> & builder)
348 : {
349 19010 : if (auto * cluster = mRegistry.Get(path); cluster != nullptr)
350 : {
351 1 : return cluster->Attributes(path, builder);
352 : }
353 :
354 19009 : const EmberAfCluster * cluster = FindServerCluster(path);
355 :
356 19009 : VerifyOrReturnValue(cluster != nullptr, CHIP_ERROR_NOT_FOUND);
357 18968 : VerifyOrReturnValue(cluster->attributeCount > 0, CHIP_NO_ERROR);
358 18968 : 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 18968 : constexpr size_t kGlobalAttributeNotInMetadataCount = MATTER_ARRAY_SIZE(GlobalAttributesNotInMetadata);
366 :
367 18968 : ReturnErrorOnFailure(builder.EnsureAppendCapacity(cluster->attributeCount + kGlobalAttributeNotInMetadataCount));
368 :
369 18968 : Span<const EmberAfAttributeMetadata> attributeSpan(cluster->attributes, cluster->attributeCount);
370 :
371 99963 : for (auto & attribute : attributeSpan)
372 : {
373 80995 : ReturnErrorOnFailure(builder.Append(AttributeEntryFrom(path, attribute)));
374 : }
375 :
376 75872 : 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 56904 : Access::Privilege::kView, std::nullopt);
388 :
389 56904 : ReturnErrorOnFailure(builder.Append(std::move(globalListEntry)));
390 : }
391 :
392 18968 : 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 19972 : const EmberAfCluster * CodegenDataModelProvider::FindServerCluster(const ConcreteClusterPath & path)
420 : {
421 39333 : if (mPreviouslyFoundCluster.has_value() && (mPreviouslyFoundCluster->path == path) &&
422 19361 : (mEmberMetadataStructureGeneration == emberAfMetadataStructureGeneration()))
423 :
424 : {
425 19253 : return mPreviouslyFoundCluster->cluster;
426 : }
427 :
428 719 : const EmberAfCluster * cluster = emberAfFindServerCluster(path.mEndpointId, path.mClusterId);
429 719 : if (cluster != nullptr)
430 : {
431 659 : mPreviouslyFoundCluster = std::make_optional<ClusterReference>(path, cluster);
432 659 : mEmberMetadataStructureGeneration = emberAfMetadataStructureGeneration();
433 : }
434 719 : return cluster;
435 : }
436 :
437 511 : CHIP_ERROR CodegenDataModelProvider::AcceptedCommands(const ConcreteClusterPath & path,
438 : ReadOnlyBufferBuilder<DataModel::AcceptedCommandEntry> & builder)
439 : {
440 511 : 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 510 : const EmberAfCluster * serverCluster = FindServerCluster(path);
449 510 : VerifyOrReturnError(serverCluster != nullptr, CHIP_ERROR_NOT_FOUND);
450 :
451 : CommandHandlerInterface * interface =
452 499 : CommandHandlerInterfaceRegistry::Instance().GetCommandHandler(path.mEndpointId, path.mClusterId);
453 499 : 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 procesing.
457 : // Otherwise we finished.
458 20 : VerifyOrReturnError(err == CHIP_ERROR_NOT_IMPLEMENTED, err);
459 : }
460 :
461 489 : VerifyOrReturnError(serverCluster->acceptedCommandList != nullptr, CHIP_NO_ERROR);
462 :
463 30 : const chip::CommandId * endOfList = serverCluster->acceptedCommandList;
464 128 : while (*endOfList != kInvalidCommandId)
465 : {
466 98 : endOfList++;
467 : }
468 30 : const auto commandCount = static_cast<size_t>(endOfList - serverCluster->acceptedCommandList);
469 :
470 : // TODO: if ember would store command entries, we could simplify this code to use static data
471 30 : ReturnErrorOnFailure(builder.EnsureAppendCapacity(commandCount));
472 :
473 30 : ConcreteCommandPath commandPath = ConcreteCommandPath(path.mEndpointId, path.mClusterId, kInvalidCommandId);
474 128 : for (const chip::CommandId * p = serverCluster->acceptedCommandList; p != endOfList; p++)
475 : {
476 98 : commandPath.mCommandId = *p;
477 98 : ReturnErrorOnFailure(builder.Append(AcceptedCommandEntryFor(commandPath)));
478 : }
479 :
480 30 : return CHIP_NO_ERROR;
481 : }
482 :
483 454 : CHIP_ERROR CodegenDataModelProvider::GeneratedCommands(const ConcreteClusterPath & path, ReadOnlyBufferBuilder<CommandId> & builder)
484 : {
485 454 : if (auto * cluster = mRegistry.Get(path); cluster != nullptr)
486 : {
487 1 : return cluster->GeneratedCommands(path, builder);
488 : }
489 :
490 : // Some CommandHandlerInterface instances are registered of ALL endpoints, so make sure first that
491 : // the cluster actually exists on this endpoint before asking the CommandHandlerInterface what commands
492 : // it claims to support.
493 453 : const EmberAfCluster * serverCluster = FindServerCluster(path);
494 453 : VerifyOrReturnError(serverCluster != nullptr, CHIP_ERROR_NOT_FOUND);
495 :
496 : CommandHandlerInterface * interface =
497 445 : CommandHandlerInterfaceRegistry::Instance().GetCommandHandler(path.mEndpointId, path.mClusterId);
498 445 : if (interface != nullptr)
499 : {
500 8 : CHIP_ERROR err = interface->RetrieveGeneratedCommands(path, builder);
501 : // If retrieving generated commands returns CHIP_ERROR_NOT_IMPLEMENTED then continue with normal procesing.
502 : // Otherwise we finished.
503 8 : VerifyOrReturnError(err == CHIP_ERROR_NOT_IMPLEMENTED, err);
504 : }
505 :
506 441 : VerifyOrReturnError(serverCluster->generatedCommandList != nullptr, CHIP_NO_ERROR);
507 :
508 2 : const chip::CommandId * endOfList = serverCluster->generatedCommandList;
509 6 : while (*endOfList != kInvalidCommandId)
510 : {
511 4 : endOfList++;
512 : }
513 2 : const auto commandCount = static_cast<size_t>(endOfList - serverCluster->generatedCommandList);
514 2 : return builder.ReferenceExisting({ serverCluster->generatedCommandList, commandCount });
515 : }
516 :
517 152 : void CodegenDataModelProvider::InitDataModelForTesting()
518 : {
519 : // Call the Ember-specific InitDataModelHandler
520 152 : InitDataModelHandler();
521 152 : }
522 :
523 3 : CHIP_ERROR CodegenDataModelProvider::DeviceTypes(EndpointId endpointId, ReadOnlyBufferBuilder<DataModel::DeviceTypeEntry> & builder)
524 : {
525 3 : std::optional<unsigned> endpoint_index = TryFindEndpointIndex(endpointId);
526 3 : if (!endpoint_index.has_value())
527 : {
528 0 : return {};
529 : }
530 :
531 3 : CHIP_ERROR err = CHIP_NO_ERROR;
532 :
533 3 : return builder.ReferenceExisting(emberAfDeviceTypeListFromEndpointIndex(*endpoint_index, err));
534 : }
535 :
536 2 : CHIP_ERROR CodegenDataModelProvider::SemanticTags(EndpointId endpointId, ReadOnlyBufferBuilder<SemanticTag> & builder)
537 : {
538 2 : DataModel::Provider::SemanticTag semanticTag;
539 2 : size_t count = 0;
540 :
541 5 : while (GetSemanticTagForEndpointAtIndex(endpointId, count, semanticTag) == CHIP_NO_ERROR)
542 : {
543 3 : count++;
544 : }
545 :
546 2 : ReturnErrorOnFailure(builder.EnsureAppendCapacity(count));
547 :
548 5 : for (size_t idx = 0; idx < count; idx++)
549 : {
550 3 : ReturnErrorOnFailure(GetSemanticTagForEndpointAtIndex(endpointId, idx, semanticTag));
551 3 : ReturnErrorOnFailure(builder.Append(semanticTag));
552 : }
553 :
554 2 : return CHIP_NO_ERROR;
555 : }
556 : #if CHIP_CONFIG_USE_ENDPOINT_UNIQUE_ID
557 : CHIP_ERROR CodegenDataModelProvider::EndpointUniqueID(EndpointId endpointId, MutableCharSpan & epUniqueId)
558 : {
559 : char buffer[Clusters::Descriptor::Attributes::EndpointUniqueID::TypeInfo::MaxLength()] = { 0 };
560 : MutableCharSpan epUniqueIdSpan(buffer);
561 : emberAfGetEndpointUniqueIdForEndPoint(endpointId, epUniqueIdSpan);
562 :
563 : memcpy(epUniqueId.data(), epUniqueIdSpan.data(), epUniqueIdSpan.size());
564 : return CHIP_NO_ERROR;
565 : }
566 : #endif
567 :
568 : } // namespace app
569 : } // namespace chip
|