Line data Source code
1 : /**
2 : *
3 : * Copyright (c) 2020-2023 Project CHIP Authors
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 "lib/support/Span.h"
18 : #include <app/util/attribute-storage.h>
19 :
20 : #include <app/AttributeAccessInterfaceRegistry.h>
21 : #include <app/CommandHandlerInterfaceRegistry.h>
22 : #include <app/InteractionModelEngine.h>
23 : #include <app/persistence/AttributePersistenceProvider.h>
24 : #include <app/persistence/AttributePersistenceProviderInstance.h>
25 : #include <app/persistence/PascalString.h>
26 : #include <app/reporting/reporting.h>
27 : #include <app/util/attribute-metadata.h>
28 : #include <app/util/attribute-storage-detail.h>
29 : #include <app/util/config.h>
30 : #include <app/util/ember-io-storage.h>
31 : #include <app/util/ember-strings.h>
32 : #include <app/util/endpoint-config-api.h>
33 : #include <app/util/generic-callbacks.h>
34 : #include <lib/core/CHIPConfig.h>
35 : #include <lib/core/CHIPError.h>
36 : #include <lib/support/CodeUtils.h>
37 : #include <lib/support/logging/CHIPLogging.h>
38 : #include <platform/LockTracker.h>
39 : #include <protocols/interaction_model/StatusCode.h>
40 :
41 : using chip::Protocols::InteractionModel::Status;
42 :
43 : // Attribute storage depends on knowing the current layout/setup of attributes
44 : // and corresponding callbacks. Specifically:
45 : // - zap-generated/callback.h is needed because endpoint_config will call the
46 : // corresponding callbacks (via GENERATED_FUNCTION_ARRAYS) and the include
47 : // for it is:
48 : // util/config.h -> zap-generated/endpoint_config.h
49 : #include <app-common/zap-generated/callback.h>
50 :
51 : using namespace chip;
52 : using namespace chip::app;
53 :
54 : //------------------------------------------------------------------------------
55 : // Globals
56 : // This is not declared CONST in order to handle dynamic endpoint information
57 : // retrieved from tokens.
58 : EmberAfDefinedEndpoint emAfEndpoints[MAX_ENDPOINT_COUNT];
59 :
60 : #if (ATTRIBUTE_MAX_SIZE == 0)
61 : #define ACTUAL_ATTRIBUTE_SIZE 1
62 : #else
63 : #define ACTUAL_ATTRIBUTE_SIZE ATTRIBUTE_MAX_SIZE
64 : #endif
65 :
66 : uint8_t attributeData[ACTUAL_ATTRIBUTE_SIZE];
67 :
68 : // ----- internal-only methods, not part of the external API -----
69 :
70 : // Loads the attributes from built-in default and storage.
71 : static void emAfLoadAttributeDefaults(EndpointId endpoint, Optional<ClusterId> = NullOptional);
72 :
73 : static bool emAfMatchCluster(const EmberAfCluster * cluster, const EmberAfAttributeSearchRecord * attRecord);
74 : static bool emAfMatchAttribute(const EmberAfCluster * cluster, const EmberAfAttributeMetadata * am,
75 : const EmberAfAttributeSearchRecord * attRecord);
76 :
77 : // If server == true, returns the number of server clusters,
78 : // otherwise number of client clusters on the endpoint at the given index.
79 : static uint8_t emberAfClusterCountByIndex(uint16_t endpointIndex, bool server);
80 :
81 : // Check whether there is an endpoint defined with the given endpoint id that is
82 : // enabled.
83 : static bool emberAfEndpointIsEnabled(EndpointId endpoint);
84 :
85 : namespace {
86 :
87 : uint16_t emberEndpointCount = 0;
88 :
89 : /// Determines a incremental unique index for ember
90 : /// metadata that is increased whenever a structural change is made to the
91 : /// ember metadata (e.g. changing dynamic endpoints or enabling/disabling endpoints)
92 : unsigned emberMetadataStructureGeneration = 0;
93 :
94 : // If we have attributes that are more than 4 bytes, then
95 : // we need this data block for the defaults
96 : #if (defined(GENERATED_DEFAULTS) && GENERATED_DEFAULTS_COUNT)
97 : constexpr const uint8_t generatedDefaults[] = GENERATED_DEFAULTS;
98 : #define ZAP_LONG_DEFAULTS_INDEX(index) \
99 : { \
100 : &generatedDefaults[index] \
101 : }
102 : #endif // GENERATED_DEFAULTS
103 :
104 : #if (defined(GENERATED_MIN_MAX_DEFAULTS) && GENERATED_MIN_MAX_DEFAULT_COUNT)
105 : constexpr const EmberAfAttributeMinMaxValue minMaxDefaults[] = GENERATED_MIN_MAX_DEFAULTS;
106 : #define ZAP_MIN_MAX_DEFAULTS_INDEX(index) \
107 : { \
108 : &minMaxDefaults[index] \
109 : }
110 : #endif // GENERATED_MIN_MAX_DEFAULTS
111 :
112 : #ifdef GENERATED_FUNCTION_ARRAYS
113 : GENERATED_FUNCTION_ARRAYS
114 : #endif
115 :
116 : #ifdef GENERATED_COMMANDS
117 : constexpr const CommandId generatedCommands[] = GENERATED_COMMANDS;
118 : #define ZAP_GENERATED_COMMANDS_INDEX(index) (&generatedCommands[index])
119 : #endif // GENERATED_COMMANDS
120 :
121 : #if (defined(GENERATED_EVENTS) && (GENERATED_EVENT_COUNT > 0))
122 : constexpr const EventId generatedEvents[] = GENERATED_EVENTS;
123 : #define ZAP_GENERATED_EVENTS_INDEX(index) (&generatedEvents[index])
124 : #endif // GENERATED_EVENTS
125 :
126 : [[maybe_unused]] constexpr const EmberAfAttributeMetadata generatedAttributes[] = GENERATED_ATTRIBUTES;
127 : #define ZAP_ATTRIBUTE_INDEX(index) (&generatedAttributes[index])
128 :
129 : #ifdef GENERATED_CLUSTERS
130 : constexpr const EmberAfCluster generatedClusters[] = GENERATED_CLUSTERS;
131 : #define ZAP_CLUSTER_INDEX(index) (&generatedClusters[index])
132 : #endif
133 :
134 : #if FIXED_ENDPOINT_COUNT > 0
135 : constexpr const EmberAfEndpointType generatedEmberAfEndpointTypes[] = GENERATED_ENDPOINT_TYPES;
136 : constexpr const EmberAfDeviceType fixedDeviceTypeList[] = FIXED_DEVICE_TYPES;
137 :
138 : // Not const, because these need to mutate.
139 : DataVersion fixedEndpointDataVersions[ZAP_FIXED_ENDPOINT_DATA_VERSION_COUNT];
140 : #endif // FIXED_ENDPOINT_COUNT > 0
141 :
142 0 : bool emberAfIsThisDataTypeAListType(EmberAfAttributeType dataType)
143 : {
144 0 : return dataType == ZCL_ARRAY_ATTRIBUTE_TYPE;
145 : }
146 :
147 13919 : uint16_t findIndexFromEndpoint(EndpointId endpoint, bool ignoreDisabledEndpoints)
148 : {
149 13919 : if (endpoint == kInvalidEndpointId)
150 : {
151 0 : return kEmberInvalidEndpointIndex;
152 : }
153 :
154 : uint16_t epi;
155 28184 : for (epi = 0; epi < emberAfEndpointCount(); epi++)
156 : {
157 42009 : if (emAfEndpoints[epi].endpoint == endpoint &&
158 13878 : (!ignoreDisabledEndpoints || emAfEndpoints[epi].bitmask.Has(EmberAfEndpointOptions::isEnabled)))
159 : {
160 13866 : return epi;
161 : }
162 : }
163 53 : return kEmberInvalidEndpointIndex;
164 : }
165 :
166 : // Returns the index of a given endpoint. Considers disabled endpoints.
167 0 : uint16_t emberAfIndexFromEndpointIncludingDisabledEndpoints(EndpointId endpoint)
168 : {
169 0 : return findIndexFromEndpoint(endpoint, false /* ignoreDisabledEndpoints */);
170 : }
171 :
172 0 : CHIP_ERROR ValidateDataContent(ByteSpan span, const EmberAfAttributeMetadata * am)
173 : {
174 0 : if (emberAfIsStringAttributeType(am->attributeType))
175 : {
176 0 : VerifyOrReturnValue(Storage::ShortPascalBytes::IsValid(span), CHIP_ERROR_INCORRECT_STATE);
177 0 : return CHIP_NO_ERROR;
178 : }
179 :
180 0 : if (emberAfIsLongStringAttributeType(am->attributeType))
181 : {
182 0 : VerifyOrReturnValue(Storage::LongPascalBytes::IsValid(span), CHIP_ERROR_INCORRECT_STATE);
183 0 : return CHIP_NO_ERROR;
184 : }
185 :
186 0 : VerifyOrReturnValue(span.size() == am->size, CHIP_ERROR_INCORRECT_STATE);
187 0 : return CHIP_NO_ERROR;
188 : }
189 :
190 : } // anonymous namespace
191 :
192 : // Initial configuration
193 21 : void emberAfEndpointConfigure()
194 : {
195 : uint16_t ep;
196 :
197 : static_assert(FIXED_ENDPOINT_COUNT <= std::numeric_limits<decltype(ep)>::max(),
198 : "FIXED_ENDPOINT_COUNT must not exceed the size of the endpoint data type");
199 :
200 21 : emberEndpointCount = FIXED_ENDPOINT_COUNT;
201 :
202 : #if FIXED_ENDPOINT_COUNT > 0
203 :
204 21 : constexpr uint16_t fixedEndpoints[] = FIXED_ENDPOINT_ARRAY;
205 21 : constexpr uint16_t fixedDeviceTypeListLengths[] = FIXED_DEVICE_TYPE_LENGTHS;
206 21 : constexpr uint16_t fixedDeviceTypeListOffsets[] = FIXED_DEVICE_TYPE_OFFSETS;
207 21 : constexpr uint8_t fixedEmberAfEndpointTypes[] = FIXED_ENDPOINT_TYPES;
208 21 : constexpr EndpointId fixedParentEndpoints[] = FIXED_PARENT_ENDPOINTS;
209 :
210 : #if ZAP_FIXED_ENDPOINT_DATA_VERSION_COUNT > 0
211 : // Initialize our data version storage. If
212 : // ZAP_FIXED_ENDPOINT_DATA_VERSION_COUNT == 0, gcc complains about a memset
213 : // with size equal to number of elements without multiplication by element
214 : // size, because the sizeof() is also 0 in that case...
215 : if (Crypto::DRBG_get_bytes(reinterpret_cast<uint8_t *>(fixedEndpointDataVersions), sizeof(fixedEndpointDataVersions)) !=
216 : CHIP_NO_ERROR)
217 : {
218 : // Now what? At least 0-init it.
219 : memset(fixedEndpointDataVersions, 0, sizeof(fixedEndpointDataVersions));
220 : }
221 : #endif // ZAP_FIXED_ENDPOINT_DATA_VERSION_COUNT > 0
222 :
223 21 : DataVersion * currentDataVersions = fixedEndpointDataVersions;
224 42 : for (ep = 0; ep < FIXED_ENDPOINT_COUNT; ep++)
225 : {
226 21 : emAfEndpoints[ep].endpoint = fixedEndpoints[ep];
227 21 : emAfEndpoints[ep].deviceTypeList =
228 21 : Span<const EmberAfDeviceType>(&fixedDeviceTypeList[fixedDeviceTypeListOffsets[ep]], fixedDeviceTypeListLengths[ep]);
229 21 : emAfEndpoints[ep].endpointType = &generatedEmberAfEndpointTypes[fixedEmberAfEndpointTypes[ep]];
230 21 : emAfEndpoints[ep].dataVersions = currentDataVersions;
231 21 : emAfEndpoints[ep].parentEndpointId = fixedParentEndpoints[ep];
232 :
233 21 : constexpr const DeviceTypeId kRootnodeId = 0x0016;
234 21 : constexpr const DeviceTypeId kAggregatorId = 0x000E;
235 21 : constexpr const DeviceTypeId kBridgedNode = 0x0013;
236 21 : emAfEndpoints[ep].bitmask.Set(EmberAfEndpointOptions::isEnabled);
237 21 : for (const auto & deviceType : emAfEndpoints[ep].deviceTypeList)
238 : {
239 : // Default composition for all device types is set to tree. Except rootnode, aggregator and
240 : // bridgednode, which are full-family. Clients can manually override these defaults using
241 : // SetFlatCompositionForEndpoint / SetTreeCompositionForEndpoint at application init.
242 : // TODO: This information should come from the schema XML, not be hardcoded.
243 21 : if ((deviceType.deviceTypeId == kRootnodeId) || (deviceType.deviceTypeId == kAggregatorId) ||
244 0 : (deviceType.deviceTypeId == kBridgedNode))
245 : {
246 21 : emAfEndpoints[ep].bitmask.Set(EmberAfEndpointOptions::isFlatComposition);
247 21 : break;
248 : }
249 : }
250 :
251 : // Increment currentDataVersions by 1 (slot) for every server cluster
252 : // this endpoint has.
253 21 : currentDataVersions += emberAfClusterCountByIndex(ep, /* server = */ true);
254 : }
255 :
256 : #endif // FIXED_ENDPOINT_COUNT > 0
257 :
258 : #if CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT
259 : if (MAX_ENDPOINT_COUNT > FIXED_ENDPOINT_COUNT)
260 : {
261 : //
262 : // Reset instances tracking dynamic endpoints to safe defaults.
263 : //
264 105 : for (ep = FIXED_ENDPOINT_COUNT; ep < MAX_ENDPOINT_COUNT; ep++)
265 : {
266 84 : emAfEndpoints[ep] = EmberAfDefinedEndpoint();
267 : }
268 : }
269 : #endif
270 21 : }
271 :
272 28 : void emberAfSetDynamicEndpointCount(uint16_t dynamicEndpointCount)
273 : {
274 28 : emberEndpointCount = static_cast<uint16_t>(FIXED_ENDPOINT_COUNT + dynamicEndpointCount);
275 28 : }
276 :
277 0 : uint16_t emberAfGetDynamicIndexFromEndpoint(EndpointId id)
278 : {
279 0 : if (id == kInvalidEndpointId)
280 : {
281 0 : return kEmberInvalidEndpointIndex;
282 : }
283 :
284 : uint16_t index;
285 0 : for (index = FIXED_ENDPOINT_COUNT; index < MAX_ENDPOINT_COUNT; index++)
286 : {
287 0 : if (emAfEndpoints[index].endpoint == id)
288 : {
289 0 : return static_cast<uint16_t>(index - FIXED_ENDPOINT_COUNT);
290 : }
291 : }
292 0 : return kEmberInvalidEndpointIndex;
293 : }
294 :
295 28 : CHIP_ERROR emberAfSetDynamicEndpoint(uint16_t index, EndpointId id, const EmberAfEndpointType * ep,
296 : const Span<DataVersion> & dataVersionStorage, Span<const EmberAfDeviceType> deviceTypeList,
297 : EndpointId parentEndpointId)
298 : {
299 28 : return emberAfSetDynamicEndpointWithEpUniqueId(index, id, ep, dataVersionStorage, deviceTypeList, {}, parentEndpointId);
300 : }
301 :
302 28 : CHIP_ERROR emberAfSetDynamicEndpointWithEpUniqueId(uint16_t index, EndpointId id, const EmberAfEndpointType * ep,
303 : const Span<DataVersion> & dataVersionStorage,
304 : Span<const EmberAfDeviceType> deviceTypeList, CharSpan endpointUniqueId,
305 : EndpointId parentEndpointId)
306 : {
307 28 : auto realIndex = index + FIXED_ENDPOINT_COUNT;
308 :
309 28 : if (realIndex >= MAX_ENDPOINT_COUNT)
310 : {
311 0 : return CHIP_ERROR_NO_MEMORY;
312 : }
313 28 : if (id == kInvalidEndpointId)
314 : {
315 0 : return CHIP_ERROR_INVALID_ARGUMENT;
316 : }
317 :
318 28 : auto serverClusterCount = emberAfClusterCountForEndpointType(ep, /* server = */ true);
319 28 : if (dataVersionStorage.size() < serverClusterCount)
320 : {
321 0 : return CHIP_ERROR_NO_MEMORY;
322 : }
323 :
324 28 : index = static_cast<uint16_t>(realIndex);
325 140 : for (uint16_t i = FIXED_ENDPOINT_COUNT; i < MAX_ENDPOINT_COUNT; i++)
326 : {
327 112 : if (emAfEndpoints[i].endpoint == id)
328 : {
329 0 : return CHIP_ERROR_ENDPOINT_EXISTS;
330 : }
331 : }
332 :
333 28 : const size_t bufferSize = Compatibility::Internal::gEmberAttributeIOBufferSpan.size();
334 62 : for (uint8_t i = 0; i < ep->clusterCount; i++)
335 : {
336 34 : const EmberAfCluster * cluster = &(ep->cluster[i]);
337 34 : if (!cluster->attributes)
338 : {
339 0 : continue;
340 : }
341 :
342 146 : for (uint16_t j = 0; j < cluster->attributeCount; j++)
343 : {
344 112 : const EmberAfAttributeMetadata * attr = &(cluster->attributes[j]);
345 112 : uint16_t attrSize = emberAfAttributeSize(attr);
346 112 : if (attrSize > bufferSize)
347 : {
348 0 : ChipLogError(DataManagement,
349 : "Attribute size %u exceeds max size %lu, (attrId=" ChipLogFormatMEI ", clusterId=" ChipLogFormatMEI
350 : ")",
351 : attrSize, static_cast<unsigned long>(bufferSize), ChipLogValueMEI(attr->attributeId),
352 : ChipLogValueMEI(cluster->clusterId));
353 0 : return CHIP_ERROR_NO_MEMORY;
354 : }
355 : }
356 : }
357 28 : emAfEndpoints[index].endpoint = id;
358 28 : emAfEndpoints[index].deviceTypeList = deviceTypeList;
359 28 : emAfEndpoints[index].endpointType = ep;
360 28 : emAfEndpoints[index].dataVersions = dataVersionStorage.data();
361 : #if CHIP_CONFIG_USE_ENDPOINT_UNIQUE_ID
362 : MutableCharSpan targetSpan(emAfEndpoints[index].endpointUniqueId);
363 : if (CopyCharSpanToMutableCharSpan(endpointUniqueId, targetSpan) != CHIP_NO_ERROR)
364 : {
365 : return CHIP_ERROR_BUFFER_TOO_SMALL;
366 : }
367 :
368 : // Ensure that the size of emAfEndpoints[index].endpointUniqueId fits within uint8_t
369 : static_assert(sizeof(emAfEndpoints[0].endpointUniqueId) <= UINT8_MAX,
370 : "The size of emAfEndpoints[index].endpointUniqueId must fit within uint8_t");
371 :
372 : emAfEndpoints[index].endpointUniqueIdSize = static_cast<uint8_t>(targetSpan.size());
373 : #endif
374 : // Start the endpoint off as disabled.
375 28 : emAfEndpoints[index].bitmask.Clear(EmberAfEndpointOptions::isEnabled);
376 28 : emAfEndpoints[index].parentEndpointId = parentEndpointId;
377 :
378 28 : emberAfSetDynamicEndpointCount(MAX_ENDPOINT_COUNT - FIXED_ENDPOINT_COUNT);
379 :
380 : // Initialize the data versions.
381 28 : size_t dataSize = sizeof(DataVersion) * serverClusterCount;
382 28 : if (dataSize != 0)
383 : {
384 28 : if (Crypto::DRBG_get_bytes(reinterpret_cast<uint8_t *>(dataVersionStorage.data()), dataSize) != CHIP_NO_ERROR)
385 : {
386 : // Now what? At least 0-init it.
387 0 : memset(dataVersionStorage.data(), 0, dataSize);
388 : }
389 : }
390 :
391 : // Now enable the endpoint.
392 28 : emberAfEndpointEnableDisable(id, true);
393 :
394 28 : emberMetadataStructureGeneration++;
395 28 : return CHIP_NO_ERROR;
396 : }
397 :
398 27 : EndpointId emberAfClearDynamicEndpoint(uint16_t index)
399 : {
400 27 : EndpointId ep = 0;
401 :
402 27 : index = static_cast<uint16_t>(index + FIXED_ENDPOINT_COUNT);
403 :
404 54 : if ((index < MAX_ENDPOINT_COUNT) && (emAfEndpoints[index].endpoint != kInvalidEndpointId) &&
405 27 : (emberAfEndpointIndexIsEnabled(index)))
406 : {
407 27 : ep = emAfEndpoints[index].endpoint;
408 27 : emberAfEndpointEnableDisable(ep, false);
409 27 : emAfEndpoints[index].endpoint = kInvalidEndpointId;
410 : }
411 :
412 27 : emberMetadataStructureGeneration++;
413 27 : return ep;
414 : }
415 :
416 13853 : uint16_t emberAfFixedEndpointCount()
417 : {
418 13853 : return FIXED_ENDPOINT_COUNT;
419 : }
420 :
421 62604 : uint16_t emberAfEndpointCount()
422 : {
423 62604 : return emberEndpointCount;
424 : }
425 :
426 14212 : bool emberAfEndpointIndexIsEnabled(uint16_t index)
427 : {
428 14212 : return (emAfEndpoints[index].bitmask.Has(EmberAfEndpointOptions::isEnabled));
429 : }
430 :
431 : // This function is used to call the per-cluster attribute changed callback
432 0 : void emAfClusterAttributeChangedCallback(const ConcreteAttributePath & attributePath)
433 : {
434 0 : const EmberAfCluster * cluster = emberAfFindServerCluster(attributePath.mEndpointId, attributePath.mClusterId);
435 0 : if (cluster != nullptr)
436 : {
437 0 : EmberAfGenericClusterFunction f = emberAfFindClusterFunction(cluster, MATTER_CLUSTER_FLAG_ATTRIBUTE_CHANGED_FUNCTION);
438 0 : if (f != nullptr)
439 : {
440 0 : ((EmberAfClusterAttributeChangedCallback) f)(attributePath);
441 : }
442 : }
443 0 : }
444 :
445 : // This function is used to call the per-cluster pre-attribute changed callback
446 0 : Status emAfClusterPreAttributeChangedCallback(const ConcreteAttributePath & attributePath, EmberAfAttributeType attributeType,
447 : uint16_t size, uint8_t * value)
448 : {
449 0 : const EmberAfCluster * cluster = emberAfFindServerCluster(attributePath.mEndpointId, attributePath.mClusterId);
450 0 : if (cluster == nullptr)
451 : {
452 0 : if (!emberAfEndpointIsEnabled(attributePath.mEndpointId))
453 : {
454 0 : return Status::UnsupportedEndpoint;
455 : }
456 0 : return Status::UnsupportedCluster;
457 : }
458 :
459 0 : Status status = Status::Success;
460 : // Casting and calling a function pointer on the same line results in ignoring the return
461 : // of the call on gcc-arm-none-eabi-9-2019-q4-major
462 0 : EmberAfClusterPreAttributeChangedCallback f = (EmberAfClusterPreAttributeChangedCallback) (emberAfFindClusterFunction(
463 : cluster, MATTER_CLUSTER_FLAG_PRE_ATTRIBUTE_CHANGED_FUNCTION));
464 0 : if (f != nullptr)
465 : {
466 0 : status = f(attributePath, attributeType, size, value);
467 : }
468 0 : return status;
469 : }
470 :
471 50 : static void initializeEndpoint(EmberAfDefinedEndpoint * definedEndpoint)
472 : {
473 : uint8_t clusterIndex;
474 50 : const EmberAfEndpointType * epType = definedEndpoint->endpointType;
475 2500 : for (clusterIndex = 0; clusterIndex < epType->clusterCount; clusterIndex++)
476 : {
477 2450 : const EmberAfCluster * cluster = &(epType->cluster[clusterIndex]);
478 : EmberAfGenericClusterFunction f;
479 2450 : emberAfClusterInitCallback(definedEndpoint->endpoint, cluster->clusterId);
480 2450 : if (cluster->IsServer())
481 : {
482 35 : MatterClusterServerInitCallback(definedEndpoint->endpoint, cluster->clusterId);
483 : }
484 2450 : f = emberAfFindClusterFunction(cluster, MATTER_CLUSTER_FLAG_INIT_FUNCTION);
485 2450 : if (f != nullptr)
486 : {
487 0 : ((EmberAfInitFunction) f)(definedEndpoint->endpoint);
488 : }
489 : }
490 50 : }
491 :
492 28 : static void shutdownEndpoint(EmberAfDefinedEndpoint * definedEndpoint)
493 : {
494 : // Call shutdown callbacks from clusters, mainly for canceling pending timers
495 : uint8_t clusterIndex;
496 28 : const EmberAfEndpointType * epType = definedEndpoint->endpointType;
497 62 : for (clusterIndex = 0; clusterIndex < epType->clusterCount; clusterIndex++)
498 : {
499 34 : const EmberAfCluster * cluster = &(epType->cluster[clusterIndex]);
500 34 : if (cluster->IsServer())
501 : {
502 34 : MatterClusterServerShutdownCallback(definedEndpoint->endpoint, cluster->clusterId);
503 : }
504 34 : EmberAfGenericClusterFunction f = emberAfFindClusterFunction(cluster, MATTER_CLUSTER_FLAG_SHUTDOWN_FUNCTION);
505 34 : if (f != nullptr)
506 : {
507 0 : ((EmberAfShutdownFunction) f)(definedEndpoint->endpoint);
508 : }
509 : }
510 :
511 28 : CommandHandlerInterfaceRegistry::Instance().UnregisterAllCommandHandlersForEndpoint(definedEndpoint->endpoint);
512 28 : AttributeAccessInterfaceRegistry::Instance().UnregisterAllForEndpoint(definedEndpoint->endpoint);
513 28 : }
514 :
515 : // Calls the init functions.
516 21 : void emAfCallInits()
517 : {
518 : uint16_t index;
519 42 : for (index = 0; index < emberAfEndpointCount(); index++)
520 : {
521 21 : if (emberAfEndpointIndexIsEnabled(index))
522 : {
523 21 : initializeEndpoint(&(emAfEndpoints[index]));
524 : }
525 : }
526 21 : }
527 :
528 : // Returns the pointer to metadata, or null if it is not found
529 6914 : const EmberAfAttributeMetadata * emberAfLocateAttributeMetadata(EndpointId endpoint, ClusterId clusterId, AttributeId attributeId)
530 : {
531 6914 : const EmberAfAttributeMetadata * metadata = nullptr;
532 : EmberAfAttributeSearchRecord record;
533 6914 : record.endpoint = endpoint;
534 6914 : record.clusterId = clusterId;
535 6914 : record.attributeId = attributeId;
536 6914 : emAfReadOrWriteAttribute(&record, &metadata,
537 : nullptr, // buffer
538 : 0, // buffer size
539 : false); // write?
540 6914 : return metadata;
541 : }
542 :
543 : // This function does mem copy, but smartly, which means that if the type is a
544 : // string, it will copy as much as it can.
545 : // If src == NULL, then this method will set memory to zeroes
546 : // See documentation for emAfReadOrWriteAttribute for the semantics of
547 : // readLength when reading and writing.
548 0 : static Status typeSensitiveMemCopy(ClusterId clusterId, uint8_t * dest, uint8_t * src, const EmberAfAttributeMetadata * am,
549 : bool write, uint16_t readLength)
550 : {
551 0 : EmberAfAttributeType attributeType = am->attributeType;
552 : // readLength == 0 for a read indicates that we should just trust that the
553 : // caller has enough space for an attribute...
554 0 : bool ignoreReadLength = write || (readLength == 0);
555 0 : uint16_t bufferSize = ignoreReadLength ? am->size : readLength;
556 :
557 0 : if (emberAfIsStringAttributeType(attributeType))
558 : {
559 0 : if (bufferSize < 1)
560 : {
561 0 : return Status::ResourceExhausted;
562 : }
563 0 : emberAfCopyString(dest, src, bufferSize - 1);
564 : }
565 0 : else if (emberAfIsLongStringAttributeType(attributeType))
566 : {
567 0 : if (bufferSize < 2)
568 : {
569 0 : return Status::ResourceExhausted;
570 : }
571 0 : emberAfCopyLongString(dest, src, bufferSize - 2);
572 : }
573 0 : else if (emberAfIsThisDataTypeAListType(attributeType))
574 : {
575 0 : if (bufferSize < 2)
576 : {
577 0 : return Status::ResourceExhausted;
578 : }
579 :
580 : // Just copy the length.
581 0 : memmove(dest, src, 2);
582 : }
583 : else
584 : {
585 0 : if (!ignoreReadLength && readLength < am->size)
586 : {
587 0 : return Status::ResourceExhausted;
588 : }
589 0 : if (src == nullptr)
590 : {
591 0 : memset(dest, 0, am->size);
592 : }
593 : else
594 : {
595 0 : memmove(dest, src, am->size);
596 : }
597 : }
598 0 : return Status::Success;
599 : }
600 :
601 : /**
602 : * @brief Matches a cluster based on cluster id and direction.
603 : *
604 : * This function assumes that the passed cluster's endpoint already
605 : * matches the endpoint of the EmberAfAttributeSearchRecord.
606 : *
607 : * Clusters match if:
608 : * 1. Cluster ids match AND
609 : * 2. Cluster is a server cluster (because there are no client attributes).
610 : */
611 6914 : bool emAfMatchCluster(const EmberAfCluster * cluster, const EmberAfAttributeSearchRecord * attRecord)
612 : {
613 6914 : return (cluster->clusterId == attRecord->clusterId && (cluster->mask & MATTER_CLUSTER_FLAG_SERVER));
614 : }
615 :
616 : /**
617 : * @brief Matches an attribute based on attribute id.
618 : * This function assumes that the passed cluster already matches the
619 : * clusterId and direction of the passed EmberAfAttributeSearchRecord.
620 : *
621 : * Attributes match if attr ids match.
622 : */
623 10021 : bool emAfMatchAttribute(const EmberAfCluster * cluster, const EmberAfAttributeMetadata * am,
624 : const EmberAfAttributeSearchRecord * attRecord)
625 : {
626 10021 : return (am->attributeId == attRecord->attributeId);
627 : }
628 :
629 : // When reading non-string attributes, this function returns an error when destination
630 : // buffer isn't large enough to accommodate the attribute type. For strings, the
631 : // function will copy at most readLength bytes. This means the resulting string
632 : // may be truncated. The length byte(s) in the resulting string will reflect
633 : // any truncation. If readLength is zero, we are working with backwards-
634 : // compatibility wrapper functions and we just cross our fingers and hope for
635 : // the best.
636 : //
637 : // When writing attributes, readLength is ignored. For non-string attributes,
638 : // this function assumes the source buffer is the same size as the attribute
639 : // type. For strings, the function will copy as many bytes as will fit in the
640 : // attribute. This means the resulting string may be truncated. The length
641 : // byte(s) in the resulting string will reflect any truncated.
642 6914 : Status emAfReadOrWriteAttribute(const EmberAfAttributeSearchRecord * attRecord, const EmberAfAttributeMetadata ** metadata,
643 : uint8_t * buffer, uint16_t readLength, bool write)
644 : {
645 6914 : assertChipStackLockedByCurrentThread();
646 :
647 6914 : uint16_t attributeOffsetIndex = 0;
648 :
649 13853 : for (uint16_t ep = 0; ep < emberAfEndpointCount(); ep++)
650 : {
651 : // Is this a dynamic endpoint?
652 13853 : bool isDynamicEndpoint = (ep >= emberAfFixedEndpointCount());
653 :
654 13853 : if (emAfEndpoints[ep].endpoint == attRecord->endpoint)
655 : {
656 6914 : const EmberAfEndpointType * endpointType = emAfEndpoints[ep].endpointType;
657 : uint8_t clusterIndex;
658 6914 : if (!emberAfEndpointIndexIsEnabled(ep))
659 : {
660 0 : continue;
661 : }
662 6914 : for (clusterIndex = 0; clusterIndex < endpointType->clusterCount; clusterIndex++)
663 : {
664 6914 : const EmberAfCluster * cluster = &(endpointType->cluster[clusterIndex]);
665 6914 : if (emAfMatchCluster(cluster, attRecord))
666 : { // Got the cluster
667 : uint16_t attrIndex;
668 10021 : for (attrIndex = 0; attrIndex < cluster->attributeCount; attrIndex++)
669 : {
670 10021 : const EmberAfAttributeMetadata * am = &(cluster->attributes[attrIndex]);
671 10021 : if (emAfMatchAttribute(cluster, am, attRecord))
672 : { // Got the attribute
673 : // If passed metadata location is not null, populate
674 6914 : if (metadata != nullptr)
675 : {
676 6914 : *metadata = am;
677 : }
678 :
679 : {
680 6914 : uint8_t * attributeLocation = attributeData + attributeOffsetIndex;
681 : uint8_t *src, *dst;
682 6914 : if (write)
683 : {
684 0 : src = buffer;
685 0 : dst = attributeLocation;
686 0 : if (!emberAfAttributeWriteAccessCallback(attRecord->endpoint, attRecord->clusterId,
687 0 : am->attributeId))
688 : {
689 0 : return Status::UnsupportedAccess;
690 : }
691 : }
692 : else
693 : {
694 6914 : if (buffer == nullptr)
695 : {
696 6914 : return Status::Success;
697 : }
698 :
699 0 : src = attributeLocation;
700 0 : dst = buffer;
701 0 : if (!emberAfAttributeReadAccessCallback(attRecord->endpoint, attRecord->clusterId,
702 0 : am->attributeId))
703 : {
704 0 : return Status::UnsupportedAccess;
705 : }
706 : }
707 :
708 : // Is the attribute externally stored?
709 0 : if (am->mask & MATTER_ATTRIBUTE_FLAG_EXTERNAL_STORAGE)
710 : {
711 0 : if (write)
712 : {
713 0 : return emberAfExternalAttributeWriteCallback(attRecord->endpoint, attRecord->clusterId, am,
714 0 : buffer);
715 : }
716 :
717 0 : if (readLength < emberAfAttributeSize(am))
718 : {
719 : // Prevent a potential buffer overflow
720 0 : return Status::ResourceExhausted;
721 : }
722 :
723 0 : return emberAfExternalAttributeReadCallback(attRecord->endpoint, attRecord->clusterId, am,
724 0 : buffer, emberAfAttributeSize(am));
725 : }
726 :
727 : // Internal storage is only supported for fixed endpoints
728 0 : if (!isDynamicEndpoint)
729 : {
730 0 : return typeSensitiveMemCopy(attRecord->clusterId, dst, src, am, write, readLength);
731 : }
732 :
733 0 : return Status::Failure;
734 : }
735 : }
736 : else
737 : { // Not the attribute we are looking for
738 : // Increase the index if attribute is not externally stored
739 3107 : if (!(am->mask & MATTER_ATTRIBUTE_FLAG_EXTERNAL_STORAGE))
740 : {
741 0 : attributeOffsetIndex = static_cast<uint16_t>(attributeOffsetIndex + emberAfAttributeSize(am));
742 : }
743 : }
744 : }
745 :
746 : // Attribute is not in the cluster.
747 0 : return Status::UnsupportedAttribute;
748 : }
749 :
750 : // Not the cluster we are looking for
751 0 : attributeOffsetIndex = static_cast<uint16_t>(attributeOffsetIndex + cluster->clusterSize);
752 : }
753 :
754 : // Cluster is not in the endpoint.
755 0 : return Status::UnsupportedCluster;
756 : }
757 :
758 : // Not the endpoint we are looking for
759 : // Dynamic endpoints are external and don't factor into storage size
760 6939 : if (!isDynamicEndpoint)
761 : {
762 6914 : attributeOffsetIndex = static_cast<uint16_t>(attributeOffsetIndex + emAfEndpoints[ep].endpointType->endpointSize);
763 : }
764 : }
765 0 : return Status::UnsupportedEndpoint; // Sorry, endpoint was not found.
766 : }
767 :
768 4310 : const EmberAfEndpointType * emberAfFindEndpointType(EndpointId endpointId)
769 : {
770 4310 : uint16_t ep = emberAfIndexFromEndpoint(endpointId);
771 4310 : if (ep == kEmberInvalidEndpointIndex)
772 : {
773 5 : return nullptr;
774 : }
775 4305 : return emAfEndpoints[ep].endpointType;
776 : }
777 :
778 9504 : const EmberAfCluster * emberAfFindClusterInType(const EmberAfEndpointType * endpointType, ClusterId clusterId,
779 : EmberAfClusterMask mask, uint8_t * index)
780 : {
781 : uint8_t i;
782 9504 : uint8_t scopedIndex = 0;
783 :
784 9535 : for (i = 0; i < endpointType->clusterCount; i++)
785 : {
786 9525 : const EmberAfCluster * cluster = &(endpointType->cluster[i]);
787 :
788 9525 : if (mask == 0 || ((cluster->mask & mask) != 0))
789 : {
790 9525 : if (cluster->clusterId == clusterId)
791 : {
792 9494 : if (index)
793 : {
794 9459 : *index = scopedIndex;
795 : }
796 :
797 9494 : return cluster;
798 : }
799 :
800 31 : scopedIndex++;
801 : }
802 : }
803 :
804 10 : return nullptr;
805 : }
806 :
807 9469 : uint8_t emberAfClusterIndex(EndpointId endpoint, ClusterId clusterId, EmberAfClusterMask mask)
808 : {
809 19053 : for (uint16_t ep = 0; ep < emberAfEndpointCount(); ep++)
810 : {
811 : // Check the endpoint id first, because that way we avoid examining the
812 : // endpoint type for endpoints that are not actually defined.
813 19043 : if (emAfEndpoints[ep].endpoint == endpoint)
814 : {
815 9469 : const EmberAfEndpointType * endpointType = emAfEndpoints[ep].endpointType;
816 9469 : uint8_t index = 0xFF;
817 9469 : if (emberAfFindClusterInType(endpointType, clusterId, mask, &index) != nullptr)
818 : {
819 9459 : return index;
820 : }
821 : }
822 : }
823 10 : return 0xFF;
824 : }
825 :
826 : // Returns whether the given endpoint has the server of the given cluster on it.
827 0 : bool emberAfContainsServer(EndpointId endpoint, ClusterId clusterId)
828 : {
829 0 : return (emberAfFindServerCluster(endpoint, clusterId) != nullptr);
830 : }
831 :
832 : // Returns whether the given endpoint has the client of the given cluster on it.
833 0 : bool emberAfContainsClient(EndpointId endpoint, ClusterId clusterId)
834 : {
835 0 : uint16_t ep = emberAfIndexFromEndpoint(endpoint);
836 0 : if (ep == kEmberInvalidEndpointIndex)
837 : {
838 0 : return false;
839 : }
840 :
841 0 : return (emberAfFindClusterInType(emAfEndpoints[ep].endpointType, clusterId, MATTER_CLUSTER_FLAG_CLIENT) != nullptr);
842 : }
843 :
844 : // This will find the first server that has the clusterId given from the index of endpoint.
845 0 : bool emberAfContainsServerFromIndex(uint16_t index, ClusterId clusterId)
846 : {
847 0 : if (index == kEmberInvalidEndpointIndex)
848 : {
849 0 : return false;
850 : }
851 :
852 0 : return emberAfFindClusterInType(emAfEndpoints[index].endpointType, clusterId, MATTER_CLUSTER_FLAG_SERVER);
853 : }
854 :
855 : namespace chip {
856 : namespace app {
857 :
858 0 : EnabledEndpointsWithServerCluster::EnabledEndpointsWithServerCluster(ClusterId clusterId) :
859 0 : mEndpointCount(emberAfEndpointCount()), mClusterId(clusterId)
860 : {
861 0 : EnsureMatchingEndpoint();
862 0 : }
863 :
864 0 : EndpointId EnabledEndpointsWithServerCluster::operator*() const
865 : {
866 0 : return emberAfEndpointFromIndex(mEndpointIndex);
867 : }
868 :
869 0 : EnabledEndpointsWithServerCluster & EnabledEndpointsWithServerCluster::operator++()
870 : {
871 0 : ++mEndpointIndex;
872 0 : EnsureMatchingEndpoint();
873 0 : return *this;
874 : }
875 :
876 0 : void EnabledEndpointsWithServerCluster::EnsureMatchingEndpoint()
877 : {
878 0 : for (; mEndpointIndex < mEndpointCount; ++mEndpointIndex)
879 : {
880 0 : if (!emberAfEndpointIndexIsEnabled(mEndpointIndex))
881 : {
882 0 : continue;
883 : }
884 :
885 0 : if (emberAfContainsServerFromIndex(mEndpointIndex, mClusterId))
886 : {
887 0 : break;
888 : }
889 : }
890 0 : }
891 :
892 : } // namespace app
893 : } // namespace chip
894 :
895 : // Finds the cluster that matches endpoint, clusterId, direction.
896 36 : const EmberAfCluster * emberAfFindServerCluster(EndpointId endpoint, ClusterId clusterId)
897 : {
898 36 : uint16_t ep = emberAfIndexFromEndpoint(endpoint);
899 36 : if (ep == kEmberInvalidEndpointIndex)
900 : {
901 1 : return nullptr;
902 : }
903 :
904 35 : return emberAfFindClusterInType(emAfEndpoints[ep].endpointType, clusterId, MATTER_CLUSTER_FLAG_SERVER);
905 : }
906 :
907 : // Returns cluster within the endpoint; Does not ignore disabled endpoints
908 0 : const EmberAfCluster * emberAfFindClusterIncludingDisabledEndpoints(EndpointId endpoint, ClusterId clusterId,
909 : EmberAfClusterMask mask)
910 : {
911 0 : uint16_t ep = emberAfIndexFromEndpointIncludingDisabledEndpoints(endpoint);
912 0 : if (ep < MAX_ENDPOINT_COUNT)
913 : {
914 0 : return emberAfFindClusterInType(emAfEndpoints[ep].endpointType, clusterId, mask);
915 : }
916 0 : return nullptr;
917 : }
918 :
919 0 : uint16_t emberAfGetClusterServerEndpointIndex(EndpointId endpoint, ClusterId cluster, uint16_t fixedClusterServerEndpointCount)
920 : {
921 0 : VerifyOrDie(fixedClusterServerEndpointCount <= FIXED_ENDPOINT_COUNT);
922 0 : uint16_t epIndex = findIndexFromEndpoint(endpoint, true /*ignoreDisabledEndpoints*/);
923 :
924 : // Endpoint must be configured and enabled
925 0 : if (epIndex == kEmberInvalidEndpointIndex)
926 : {
927 0 : return kEmberInvalidEndpointIndex;
928 : }
929 :
930 0 : if (emberAfFindClusterInType(emAfEndpoints[epIndex].endpointType, cluster, MATTER_CLUSTER_FLAG_SERVER) == nullptr)
931 : {
932 : // The provided endpoint does not contain the given cluster server.
933 0 : return kEmberInvalidEndpointIndex;
934 : }
935 :
936 0 : if (epIndex < FIXED_ENDPOINT_COUNT)
937 : {
938 : // This endpoint is a fixed one.
939 : // Return the index of this endpoint in the list of fixed endpoints that support the given cluster.
940 0 : uint16_t adjustedEndpointIndex = 0;
941 0 : for (uint16_t i = 0; i < epIndex; i++)
942 : {
943 : // Increase adjustedEndpointIndex for every endpoint containing the cluster server
944 : // before our endpoint of interest
945 0 : if (emAfEndpoints[i].endpoint != kInvalidEndpointId &&
946 0 : (emberAfFindClusterInType(emAfEndpoints[i].endpointType, cluster, MATTER_CLUSTER_FLAG_SERVER) != nullptr))
947 : {
948 0 : adjustedEndpointIndex++;
949 : }
950 : }
951 :
952 : // If this asserts, the provided fixedClusterServerEndpointCount doesn't match the app data model.
953 0 : VerifyOrDie(adjustedEndpointIndex < fixedClusterServerEndpointCount);
954 0 : epIndex = adjustedEndpointIndex;
955 : }
956 : else
957 : {
958 : // This is a dynamic endpoint.
959 : // Its index is just its index in the dynamic endpoint list, offset by fixedClusterServerEndpointCount.
960 0 : epIndex = static_cast<uint16_t>(fixedClusterServerEndpointCount + (epIndex - FIXED_ENDPOINT_COUNT));
961 : }
962 :
963 0 : return epIndex;
964 : }
965 :
966 0 : bool emberAfEndpointIsEnabled(EndpointId endpoint)
967 : {
968 0 : uint16_t index = findIndexFromEndpoint(endpoint, false /* ignoreDisabledEndpoints */);
969 :
970 0 : if (kEmberInvalidEndpointIndex == index)
971 : {
972 0 : return false;
973 : }
974 :
975 0 : return emberAfEndpointIndexIsEnabled(index);
976 : }
977 :
978 57 : bool emberAfEndpointEnableDisable(EndpointId endpoint, bool enable)
979 : {
980 57 : uint16_t index = findIndexFromEndpoint(endpoint, false /* ignoreDisabledEndpoints */);
981 : bool currentlyEnabled;
982 :
983 57 : if (kEmberInvalidEndpointIndex == index)
984 : {
985 0 : return false;
986 : }
987 :
988 57 : currentlyEnabled = emAfEndpoints[index].bitmask.Has(EmberAfEndpointOptions::isEnabled);
989 :
990 57 : if (enable)
991 : {
992 29 : emAfEndpoints[index].bitmask.Set(EmberAfEndpointOptions::isEnabled);
993 : }
994 :
995 57 : if (currentlyEnabled != enable)
996 : {
997 57 : if (enable)
998 : {
999 29 : initializeEndpoint(&(emAfEndpoints[index]));
1000 29 : emberAfEndpointChanged(endpoint, emberAfGlobalInteractionModelAttributesChangedListener());
1001 : }
1002 : else
1003 : {
1004 28 : shutdownEndpoint(&(emAfEndpoints[index]));
1005 28 : emAfEndpoints[index].bitmask.Clear(EmberAfEndpointOptions::isEnabled);
1006 : }
1007 :
1008 57 : EndpointId parentEndpointId = emberAfParentEndpointFromIndex(index);
1009 57 : while (parentEndpointId != kInvalidEndpointId)
1010 : {
1011 0 : emberAfAttributeChanged(parentEndpointId, Clusters::Descriptor::Id, Clusters::Descriptor::Attributes::PartsList::Id,
1012 : emberAfGlobalInteractionModelAttributesChangedListener());
1013 0 : uint16_t parentIndex = emberAfIndexFromEndpoint(parentEndpointId);
1014 0 : if (parentIndex == kEmberInvalidEndpointIndex)
1015 : {
1016 : // Something has gone wrong.
1017 0 : break;
1018 : }
1019 0 : parentEndpointId = emberAfParentEndpointFromIndex(parentIndex);
1020 : }
1021 :
1022 57 : emberAfAttributeChanged(/* endpoint = */ 0, Clusters::Descriptor::Id, Clusters::Descriptor::Attributes::PartsList::Id,
1023 : emberAfGlobalInteractionModelAttributesChangedListener());
1024 : }
1025 :
1026 57 : emberMetadataStructureGeneration++;
1027 57 : return true;
1028 : }
1029 :
1030 12136 : unsigned emberAfMetadataStructureGeneration()
1031 : {
1032 12136 : return emberMetadataStructureGeneration;
1033 : }
1034 :
1035 : // Returns the index of a given endpoint. Does not consider disabled endpoints.
1036 13862 : uint16_t emberAfIndexFromEndpoint(EndpointId endpoint)
1037 : {
1038 13862 : return findIndexFromEndpoint(endpoint, true /* ignoreDisabledEndpoints */);
1039 : }
1040 :
1041 2917 : EndpointId emberAfEndpointFromIndex(uint16_t index)
1042 : {
1043 2917 : return emAfEndpoints[index].endpoint;
1044 : }
1045 :
1046 2974 : EndpointId emberAfParentEndpointFromIndex(uint16_t index)
1047 : {
1048 2974 : return emAfEndpoints[index].parentEndpointId;
1049 : }
1050 :
1051 : // If server == true, returns the number of server clusters,
1052 : // otherwise number of client clusters on this endpoint
1053 0 : uint8_t emberAfClusterCount(EndpointId endpoint, bool server)
1054 : {
1055 0 : uint16_t index = emberAfIndexFromEndpoint(endpoint);
1056 0 : if (index == kEmberInvalidEndpointIndex)
1057 : {
1058 0 : return 0;
1059 : }
1060 :
1061 0 : return emberAfClusterCountByIndex(index, server);
1062 : }
1063 :
1064 21 : uint8_t emberAfClusterCountByIndex(uint16_t endpointIndex, bool server)
1065 : {
1066 21 : const EmberAfDefinedEndpoint * de = &(emAfEndpoints[endpointIndex]);
1067 21 : if (de->endpointType == nullptr)
1068 : {
1069 0 : return 0;
1070 : }
1071 :
1072 21 : return emberAfClusterCountForEndpointType(de->endpointType, server);
1073 : }
1074 :
1075 4354 : uint8_t emberAfClusterCountForEndpointType(const EmberAfEndpointType * type, bool server)
1076 : {
1077 4354 : const EmberAfClusterMask cluster_mask = server ? MATTER_CLUSTER_FLAG_SERVER : MATTER_CLUSTER_FLAG_CLIENT;
1078 :
1079 4354 : return static_cast<uint8_t>(std::count_if(type->cluster, type->cluster + type->clusterCount,
1080 12611 : [=](const EmberAfCluster & cluster) { return (cluster.mask & cluster_mask) != 0; }));
1081 : }
1082 :
1083 0 : uint8_t emberAfGetClusterCountForEndpoint(EndpointId endpoint)
1084 : {
1085 0 : uint16_t index = emberAfIndexFromEndpoint(endpoint);
1086 0 : if (index == kEmberInvalidEndpointIndex)
1087 : {
1088 0 : return 0;
1089 : }
1090 0 : return emAfEndpoints[index].endpointType->clusterCount;
1091 : }
1092 :
1093 0 : Span<const EmberAfDeviceType> emberAfDeviceTypeListFromEndpoint(EndpointId endpoint, CHIP_ERROR & err)
1094 : {
1095 0 : return emberAfDeviceTypeListFromEndpointIndex(emberAfIndexFromEndpoint(endpoint), err);
1096 : }
1097 :
1098 0 : chip::Span<const EmberAfDeviceType> emberAfDeviceTypeListFromEndpointIndex(unsigned endpointIndex, CHIP_ERROR & err)
1099 : {
1100 0 : if (endpointIndex == 0xFFFF)
1101 : {
1102 0 : err = CHIP_ERROR_INVALID_ARGUMENT;
1103 0 : return Span<const EmberAfDeviceType>();
1104 : }
1105 :
1106 0 : err = CHIP_NO_ERROR;
1107 0 : return emAfEndpoints[endpointIndex].deviceTypeList;
1108 : }
1109 :
1110 0 : CHIP_ERROR GetSemanticTagForEndpointAtIndex(EndpointId endpoint, size_t index,
1111 : Clusters::Descriptor::Structs::SemanticTagStruct::Type & tag)
1112 : {
1113 0 : uint16_t endpointIndex = emberAfIndexFromEndpoint(endpoint);
1114 :
1115 0 : if (endpointIndex == 0xFFFF || index >= emAfEndpoints[endpointIndex].tagList.size())
1116 : {
1117 0 : return CHIP_ERROR_NOT_FOUND;
1118 : }
1119 0 : tag = emAfEndpoints[endpointIndex].tagList[index];
1120 0 : return CHIP_NO_ERROR;
1121 : }
1122 : #if CHIP_CONFIG_USE_ENDPOINT_UNIQUE_ID
1123 : CHIP_ERROR emberAfGetEndpointUniqueIdForEndPoint(EndpointId endpoint, MutableCharSpan & epUniqueIdMutSpan)
1124 : {
1125 : uint16_t endpointIndex = emberAfIndexFromEndpoint(endpoint);
1126 :
1127 : if (endpointIndex == 0xFFFF)
1128 : {
1129 : return CHIP_ERROR_NOT_FOUND;
1130 : }
1131 :
1132 : CharSpan epUniqueIdSpan(emAfEndpoints[endpointIndex].endpointUniqueId, emAfEndpoints[endpointIndex].endpointUniqueIdSize);
1133 : return CopyCharSpanToMutableCharSpan(epUniqueIdSpan, epUniqueIdMutSpan);
1134 : }
1135 : #endif
1136 0 : CHIP_ERROR emberAfSetDeviceTypeList(EndpointId endpoint, Span<const EmberAfDeviceType> deviceTypeList)
1137 : {
1138 0 : uint16_t endpointIndex = emberAfIndexFromEndpoint(endpoint);
1139 0 : if (endpointIndex == 0xFFFF)
1140 : {
1141 0 : return CHIP_ERROR_INVALID_ARGUMENT;
1142 : }
1143 :
1144 0 : emAfEndpoints[endpointIndex].deviceTypeList = deviceTypeList;
1145 0 : return CHIP_NO_ERROR;
1146 : }
1147 :
1148 0 : CHIP_ERROR SetTagList(EndpointId endpoint, Span<const Clusters::Descriptor::Structs::SemanticTagStruct::Type> tagList)
1149 : {
1150 0 : uint16_t endpointIndex = emberAfIndexFromEndpoint(endpoint);
1151 0 : if (endpointIndex == 0xFFFF)
1152 : {
1153 0 : return CHIP_ERROR_INVALID_ARGUMENT;
1154 : }
1155 :
1156 0 : emAfEndpoints[endpointIndex].tagList = tagList;
1157 0 : return CHIP_NO_ERROR;
1158 : }
1159 :
1160 : // Returns the cluster of Nth server or client cluster,
1161 : // depending on server toggle.
1162 0 : const EmberAfCluster * emberAfGetNthCluster(EndpointId endpoint, uint8_t n, bool server)
1163 : {
1164 0 : uint16_t index = emberAfIndexFromEndpoint(endpoint);
1165 0 : if (index == kEmberInvalidEndpointIndex)
1166 : {
1167 0 : return nullptr;
1168 : }
1169 :
1170 0 : const EmberAfEndpointType * endpointType = emAfEndpoints[index].endpointType;
1171 0 : const EmberAfClusterMask cluster_mask = server ? MATTER_CLUSTER_FLAG_SERVER : MATTER_CLUSTER_FLAG_CLIENT;
1172 0 : const uint8_t clusterCount = endpointType->clusterCount;
1173 :
1174 0 : uint8_t c = 0;
1175 0 : for (uint8_t i = 0; i < clusterCount; i++)
1176 : {
1177 0 : const EmberAfCluster * cluster = &(endpointType->cluster[i]);
1178 :
1179 0 : if ((cluster->mask & cluster_mask) == 0)
1180 : {
1181 0 : continue;
1182 : }
1183 :
1184 0 : if (c == n)
1185 : {
1186 0 : return cluster;
1187 : }
1188 :
1189 0 : c++;
1190 : }
1191 0 : return nullptr;
1192 : }
1193 :
1194 : // Returns the cluster id of Nth server or client cluster,
1195 : // depending on server toggle.
1196 : // Returns Optional<ClusterId>::Missing() if cluster does not exist.
1197 0 : Optional<ClusterId> emberAfGetNthClusterId(EndpointId endpoint, uint8_t n, bool server)
1198 : {
1199 0 : const EmberAfCluster * cluster = emberAfGetNthCluster(endpoint, n, server);
1200 0 : if (cluster == nullptr)
1201 : {
1202 0 : return Optional<ClusterId>::Missing();
1203 : }
1204 0 : return Optional<ClusterId>(cluster->clusterId);
1205 : }
1206 :
1207 : // Returns number of clusters put into the passed cluster list
1208 : // for the given endpoint and client/server polarity
1209 0 : uint8_t emberAfGetClustersFromEndpoint(EndpointId endpoint, ClusterId * clusterList, uint8_t listLen, bool server)
1210 : {
1211 0 : uint8_t clusterCount = emberAfClusterCount(endpoint, server);
1212 : uint8_t i;
1213 : const EmberAfCluster * cluster;
1214 0 : if (clusterCount > listLen)
1215 : {
1216 0 : clusterCount = listLen;
1217 : }
1218 0 : for (i = 0; i < clusterCount; i++)
1219 : {
1220 0 : cluster = emberAfGetNthCluster(endpoint, i, server);
1221 0 : clusterList[i] = (cluster == nullptr ? kEmberInvalidEndpointIndex : cluster->clusterId);
1222 : }
1223 0 : return clusterCount;
1224 : }
1225 :
1226 21 : void emberAfInitializeAttributes(EndpointId endpoint)
1227 : {
1228 21 : emAfLoadAttributeDefaults(endpoint);
1229 21 : }
1230 :
1231 21 : void emAfLoadAttributeDefaults(EndpointId endpoint, Optional<ClusterId> clusterId)
1232 : {
1233 : uint16_t ep;
1234 : uint8_t clusterI;
1235 : uint16_t attr;
1236 : uint8_t * ptr;
1237 21 : uint16_t epCount = emberAfEndpointCount();
1238 : uint8_t attrData[ATTRIBUTE_LARGEST];
1239 21 : auto * attrStorage = GetAttributePersistenceProvider();
1240 : // Don't check whether we actually have an attrStorage here, because it's OK
1241 : // to have one if none of our attributes have NVM storage.
1242 :
1243 42 : for (ep = 0; ep < epCount; ep++)
1244 : {
1245 : EmberAfDefinedEndpoint * de;
1246 21 : if (endpoint != kInvalidEndpointId)
1247 : {
1248 0 : ep = emberAfIndexFromEndpoint(endpoint);
1249 0 : if (ep == kEmberInvalidEndpointIndex)
1250 : {
1251 0 : return;
1252 : }
1253 : }
1254 21 : de = &(emAfEndpoints[ep]);
1255 :
1256 2436 : for (clusterI = 0; clusterI < de->endpointType->clusterCount; clusterI++)
1257 : {
1258 2415 : const EmberAfCluster * cluster = &(de->endpointType->cluster[clusterI]);
1259 2415 : if (clusterId.HasValue())
1260 : {
1261 0 : if (clusterId.Value() != cluster->clusterId)
1262 : {
1263 0 : continue;
1264 : }
1265 : }
1266 :
1267 : // when the attributeCount is high, the loop takes too long to run and a
1268 : // watchdog kicks in causing a reset. As a workaround, we'll
1269 : // conditionally manually reset the watchdog. 300 sounds like a good
1270 : // magic number for now.
1271 2415 : if (cluster->attributeCount > 300)
1272 : {
1273 : // halResetWatchdog();
1274 : }
1275 2415 : for (attr = 0; attr < cluster->attributeCount; attr++)
1276 : {
1277 0 : const EmberAfAttributeMetadata * am = &(cluster->attributes[attr]);
1278 0 : ptr = nullptr; // Will get set to the value to write, as needed.
1279 :
1280 : // First check for a persisted value.
1281 0 : if (am->IsAutomaticallyPersisted())
1282 : {
1283 0 : VerifyOrDieWithMsg(attrStorage != nullptr, Zcl, "Attribute persistence needs a persistence provider");
1284 0 : MutableByteSpan bytes(attrData);
1285 : CHIP_ERROR err =
1286 0 : attrStorage->ReadValue(ConcreteAttributePath(de->endpoint, cluster->clusterId, am->attributeId), bytes);
1287 0 : if (err == CHIP_NO_ERROR)
1288 : {
1289 0 : err = ValidateDataContent(bytes, am);
1290 : }
1291 :
1292 0 : if (err == CHIP_NO_ERROR)
1293 : {
1294 0 : ptr = attrData;
1295 : }
1296 : else
1297 : {
1298 0 : ChipLogDetail(
1299 : DataManagement,
1300 : "Failed to read stored attribute (%u, " ChipLogFormatMEI ", " ChipLogFormatMEI ": %" CHIP_ERROR_FORMAT,
1301 : de->endpoint, ChipLogValueMEI(cluster->clusterId), ChipLogValueMEI(am->attributeId), err.Format());
1302 : // Just fall back to default value.
1303 : }
1304 : }
1305 :
1306 0 : if (!am->IsExternal())
1307 : {
1308 : EmberAfAttributeSearchRecord record;
1309 0 : record.endpoint = de->endpoint;
1310 0 : record.clusterId = cluster->clusterId;
1311 0 : record.attributeId = am->attributeId;
1312 :
1313 0 : if (ptr == nullptr)
1314 : {
1315 0 : size_t defaultValueSizeForBigEndianNudger = 0;
1316 : // Bypasses compiler warning about unused variable for little endian platforms.
1317 : (void) defaultValueSizeForBigEndianNudger;
1318 0 : if ((am->mask & MATTER_ATTRIBUTE_FLAG_MIN_MAX) != 0U)
1319 : {
1320 : // This is intentionally 2 and not 4 bytes since defaultValue in min/max
1321 : // attributes is still uint16_t.
1322 0 : if (emberAfAttributeSize(am) <= 2)
1323 : {
1324 : static_assert(sizeof(am->defaultValue.ptrToMinMaxValue->defaultValue.defaultValue) == 2,
1325 : "if statement relies on size of max/min defaultValue being 2");
1326 0 : ptr = (uint8_t *) &(am->defaultValue.ptrToMinMaxValue->defaultValue.defaultValue);
1327 0 : defaultValueSizeForBigEndianNudger =
1328 : sizeof(am->defaultValue.ptrToMinMaxValue->defaultValue.defaultValue);
1329 : }
1330 : else
1331 : {
1332 0 : ptr = (uint8_t *) am->defaultValue.ptrToMinMaxValue->defaultValue.ptrToDefaultValue;
1333 : }
1334 : }
1335 : else
1336 : {
1337 0 : if ((emberAfAttributeSize(am) <= 4) && !emberAfIsStringAttributeType(am->attributeType))
1338 : {
1339 0 : ptr = (uint8_t *) &(am->defaultValue.defaultValue);
1340 0 : defaultValueSizeForBigEndianNudger = sizeof(am->defaultValue.defaultValue);
1341 : }
1342 : else
1343 : {
1344 0 : ptr = (uint8_t *) am->defaultValue.ptrToDefaultValue;
1345 : }
1346 : }
1347 : // At this point, ptr either points to a default value, or is NULL, in which case
1348 : // it should be treated as if it is pointing to an array of all zeroes.
1349 :
1350 : #if (CHIP_CONFIG_BIG_ENDIAN_TARGET)
1351 : // The default values for attributes that are less than or equal to
1352 : // defaultValueSizeForBigEndianNudger in bytes are stored in an
1353 : // uint32_t. On big-endian platforms, a pointer to the default value
1354 : // of size less than defaultValueSizeForBigEndianNudger will point to the wrong
1355 : // byte. So, for those cases, nudge the pointer forward so it points
1356 : // to the correct byte.
1357 : if (emberAfAttributeSize(am) < defaultValueSizeForBigEndianNudger && ptr != NULL)
1358 : {
1359 : ptr += (defaultValueSizeForBigEndianNudger - emberAfAttributeSize(am));
1360 : }
1361 : #endif // BIGENDIAN
1362 : }
1363 :
1364 0 : emAfReadOrWriteAttribute(&record,
1365 : nullptr, // metadata - unused
1366 : ptr,
1367 : 0, // buffer size - unused
1368 : true); // write?
1369 : }
1370 : }
1371 : }
1372 21 : if (endpoint != kInvalidEndpointId)
1373 : {
1374 0 : break;
1375 : }
1376 : }
1377 : }
1378 :
1379 : // 'data' argument may be null, since we changed the ptrToDefaultValue
1380 : // to be null instead of pointing to all zeroes.
1381 : // This function has to be able to deal with that.
1382 0 : void emAfSaveAttributeToStorageIfNeeded(uint8_t * data, EndpointId endpoint, ClusterId clusterId,
1383 : const EmberAfAttributeMetadata * metadata)
1384 : {
1385 : // Get out of here if this attribute isn't marked non-volatile.
1386 0 : if (!metadata->IsAutomaticallyPersisted())
1387 : {
1388 0 : return;
1389 : }
1390 :
1391 : // TODO: Maybe we should have a separate constant for the size of the
1392 : // largest non-volatile attribute?
1393 0 : uint8_t allZeroData[ATTRIBUTE_LARGEST] = { 0 };
1394 0 : if (data == nullptr)
1395 : {
1396 0 : data = allZeroData;
1397 : }
1398 :
1399 : size_t dataSize;
1400 0 : EmberAfAttributeType type = metadata->attributeType;
1401 0 : if (emberAfIsStringAttributeType(type))
1402 : {
1403 0 : dataSize = emberAfStringLength(data) + 1;
1404 : }
1405 0 : else if (emberAfIsLongStringAttributeType(type))
1406 : {
1407 0 : dataSize = emberAfLongStringLength(data) + 2;
1408 : }
1409 : else
1410 : {
1411 0 : dataSize = metadata->size;
1412 : }
1413 :
1414 0 : auto * attrStorage = GetAttributePersistenceProvider();
1415 0 : if (attrStorage)
1416 : {
1417 0 : attrStorage->WriteValue(ConcreteAttributePath(endpoint, clusterId, metadata->attributeId), ByteSpan(data, dataSize));
1418 : }
1419 : else
1420 : {
1421 0 : ChipLogProgress(DataManagement, "Can't store attribute value: no persistence provider");
1422 : }
1423 : }
1424 :
1425 : // This function returns the actual function point from the array,
1426 : // iterating over the function bits.
1427 2484 : EmberAfGenericClusterFunction emberAfFindClusterFunction(const EmberAfCluster * cluster, EmberAfClusterMask functionMask)
1428 : {
1429 2484 : EmberAfClusterMask mask = 0x01;
1430 2484 : uint8_t functionIndex = 0;
1431 :
1432 2484 : if ((cluster->mask & functionMask) == 0)
1433 : {
1434 2484 : return nullptr;
1435 : }
1436 :
1437 0 : while (mask < functionMask)
1438 : {
1439 0 : if ((cluster->mask & mask) != 0)
1440 : {
1441 0 : functionIndex++;
1442 : }
1443 0 : mask = static_cast<EmberAfClusterMask>(mask << 1);
1444 : }
1445 0 : return cluster->functions[functionIndex];
1446 : }
1447 :
1448 : namespace chip {
1449 : namespace app {
1450 :
1451 0 : CHIP_ERROR SetParentEndpointForEndpoint(EndpointId childEndpoint, EndpointId parentEndpoint)
1452 : {
1453 0 : uint16_t childIndex = emberAfIndexFromEndpoint(childEndpoint);
1454 0 : uint16_t parentIndex = emberAfIndexFromEndpoint(parentEndpoint);
1455 :
1456 0 : if (childIndex == kEmberInvalidEndpointIndex || parentIndex == kEmberInvalidEndpointIndex)
1457 : {
1458 0 : return CHIP_ERROR_INVALID_ARGUMENT;
1459 : }
1460 0 : emAfEndpoints[childIndex].parentEndpointId = parentEndpoint;
1461 0 : return CHIP_NO_ERROR;
1462 : }
1463 :
1464 0 : CHIP_ERROR SetFlatCompositionForEndpoint(EndpointId endpoint)
1465 : {
1466 0 : uint16_t index = emberAfIndexFromEndpoint(endpoint);
1467 0 : if (index == kEmberInvalidEndpointIndex)
1468 : {
1469 0 : return CHIP_ERROR_INVALID_ARGUMENT;
1470 : }
1471 0 : emAfEndpoints[index].bitmask.Set(EmberAfEndpointOptions::isFlatComposition);
1472 0 : return CHIP_NO_ERROR;
1473 : }
1474 :
1475 0 : CHIP_ERROR SetTreeCompositionForEndpoint(EndpointId endpoint)
1476 : {
1477 0 : uint16_t index = emberAfIndexFromEndpoint(endpoint);
1478 0 : if (index == kEmberInvalidEndpointIndex)
1479 : {
1480 0 : return CHIP_ERROR_INVALID_ARGUMENT;
1481 : }
1482 0 : emAfEndpoints[index].bitmask.Clear(EmberAfEndpointOptions::isFlatComposition);
1483 0 : return CHIP_NO_ERROR;
1484 : }
1485 :
1486 0 : bool IsFlatCompositionForEndpoint(EndpointId endpoint)
1487 : {
1488 0 : uint16_t index = emberAfIndexFromEndpoint(endpoint);
1489 0 : if (index == kEmberInvalidEndpointIndex)
1490 : {
1491 0 : return false;
1492 : }
1493 0 : return emAfEndpoints[index].bitmask.Has(EmberAfEndpointOptions::isFlatComposition);
1494 : }
1495 :
1496 0 : bool IsTreeCompositionForEndpoint(EndpointId endpoint)
1497 : {
1498 0 : uint16_t index = emberAfIndexFromEndpoint(endpoint);
1499 0 : if (index == kEmberInvalidEndpointIndex)
1500 : {
1501 0 : return false;
1502 : }
1503 0 : return !emAfEndpoints[index].bitmask.Has(EmberAfEndpointOptions::isFlatComposition);
1504 : }
1505 :
1506 2917 : EndpointComposition GetCompositionForEndpointIndex(uint16_t endpointIndex)
1507 : {
1508 2917 : VerifyOrReturnValue(endpointIndex < MATTER_ARRAY_SIZE(emAfEndpoints), EndpointComposition::kInvalid);
1509 2917 : if (emAfEndpoints[endpointIndex].bitmask.Has(EmberAfEndpointOptions::isFlatComposition))
1510 : {
1511 1438 : return EndpointComposition::kFullFamily;
1512 : }
1513 1479 : return EndpointComposition::kTree;
1514 : }
1515 :
1516 : } // namespace app
1517 : } // namespace chip
1518 :
1519 0 : uint16_t emberAfGetServerAttributeCount(EndpointId endpoint, ClusterId cluster)
1520 : {
1521 0 : const EmberAfCluster * clusterObj = emberAfFindServerCluster(endpoint, cluster);
1522 0 : VerifyOrReturnError(clusterObj != nullptr, 0);
1523 0 : return clusterObj->attributeCount;
1524 : }
1525 :
1526 0 : uint16_t emberAfGetServerAttributeIndexByAttributeId(EndpointId endpoint, ClusterId cluster, AttributeId attributeId)
1527 : {
1528 0 : const EmberAfCluster * clusterObj = emberAfFindServerCluster(endpoint, cluster);
1529 0 : VerifyOrReturnError(clusterObj != nullptr, UINT16_MAX);
1530 :
1531 0 : for (uint16_t i = 0; i < clusterObj->attributeCount; i++)
1532 : {
1533 0 : if (clusterObj->attributes[i].attributeId == attributeId)
1534 : {
1535 0 : return i;
1536 : }
1537 : }
1538 0 : return UINT16_MAX;
1539 : }
1540 :
1541 0 : Optional<AttributeId> emberAfGetServerAttributeIdByIndex(EndpointId endpoint, ClusterId cluster, uint16_t attributeIndex)
1542 : {
1543 0 : const EmberAfCluster * clusterObj = emberAfFindServerCluster(endpoint, cluster);
1544 0 : if (clusterObj == nullptr || clusterObj->attributeCount <= attributeIndex)
1545 : {
1546 0 : return Optional<AttributeId>::Missing();
1547 : }
1548 0 : return Optional<AttributeId>(clusterObj->attributes[attributeIndex].attributeId);
1549 : }
1550 :
1551 9516 : DataVersion * emberAfDataVersionStorage(const ConcreteClusterPath & aConcreteClusterPath)
1552 : {
1553 9516 : uint16_t index = emberAfIndexFromEndpoint(aConcreteClusterPath.mEndpointId);
1554 9516 : if (index == kEmberInvalidEndpointIndex)
1555 : {
1556 : // Unknown endpoint.
1557 47 : return nullptr;
1558 : }
1559 9469 : const EmberAfDefinedEndpoint & ep = emAfEndpoints[index];
1560 9469 : if (!ep.dataVersions)
1561 : {
1562 : // No storage provided.
1563 0 : return nullptr;
1564 : }
1565 :
1566 : // This does a second walk over endpoints to find the right one, but
1567 : // probably worth it to avoid duplicating code.
1568 : auto clusterIndex =
1569 9469 : emberAfClusterIndex(aConcreteClusterPath.mEndpointId, aConcreteClusterPath.mClusterId, MATTER_CLUSTER_FLAG_SERVER);
1570 9469 : if (clusterIndex == 0xFF)
1571 : {
1572 : // No such cluster on this endpoint.
1573 10 : return nullptr;
1574 : }
1575 :
1576 9459 : return ep.dataVersions + clusterIndex;
1577 : }
1578 :
1579 : namespace {
1580 : class GlobalInteractionModelEngineChangedpathListener : public AttributesChangedListener
1581 : {
1582 : public:
1583 6 : ~GlobalInteractionModelEngineChangedpathListener() = default;
1584 :
1585 86 : void MarkDirty(const AttributePathParams & path) override
1586 : {
1587 86 : InteractionModelEngine::GetInstance()->GetReportingEngine().SetDirty(path);
1588 86 : }
1589 : };
1590 :
1591 : } // namespace
1592 :
1593 86 : AttributesChangedListener * emberAfGlobalInteractionModelAttributesChangedListener()
1594 : {
1595 86 : static GlobalInteractionModelEngineChangedpathListener listener;
1596 86 : return &listener;
1597 : }
1598 :
1599 5203 : void emberAfAttributeChanged(EndpointId endpoint, ClusterId clusterId, AttributeId attributeId,
1600 : AttributesChangedListener * listener)
1601 : {
1602 : // Increase cluster data path
1603 5203 : DataVersion * version = emberAfDataVersionStorage(ConcreteClusterPath(endpoint, clusterId));
1604 5203 : if (version == nullptr)
1605 : {
1606 57 : ChipLogError(DataManagement, "Endpoint %x, Cluster " ChipLogFormatMEI " not found in IncreaseClusterDataVersion!", endpoint,
1607 : ChipLogValueMEI(clusterId));
1608 : }
1609 : else
1610 : {
1611 5146 : (*(version))++;
1612 5146 : ChipLogDetail(DataManagement, "Endpoint %x, Cluster " ChipLogFormatMEI " update version to %" PRIx32, endpoint,
1613 : ChipLogValueMEI(clusterId), *(version));
1614 : }
1615 :
1616 5203 : listener->MarkDirty(AttributePathParams(endpoint, clusterId, attributeId));
1617 5203 : }
1618 :
1619 29 : void emberAfEndpointChanged(EndpointId endpoint, AttributesChangedListener * listener)
1620 : {
1621 29 : listener->MarkDirty(AttributePathParams(endpoint));
1622 29 : }
|