LCOV - code coverage report
Current view: top level - app/util - attribute-storage.cpp (source / functions) Hit Total Coverage
Test: lcov_final.info Lines: 263 560 47.0 %
Date: 2024-02-15 08:20:41 Functions: 40 73 54.8 %

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

Generated by: LCOV version 1.14