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