From 2e48bd5edb89aa9ae94cde9e8d1b32c1fa50ef0c Mon Sep 17 00:00:00 2001 From: Lukas Zeller Date: Wed, 26 Jul 2023 19:41:45 +0200 Subject: [PATCH] [attribute storage] allow setting up dynamic endpoints from templates Instead of re-creating dynamic endpoint's cluster declarations using the DECLARE_DYNAMIC_xxx macros, the new `setupDynamicEndpointDeclaration()` function allows setting up a dynamic endpoint by using clusters defined in another endpoint as templates. Usually that is a disabled endpoint created in ZAP with all possibly dynamically used clusters assigned. In combination with dynamic endpoint attribute storage, this greatly simplifies implementing dynamic endpoints and allows configuring them using the ZAP tool to ensure specs conformance, much the same way as with statically defined endpoints. --- src/app/util/attribute-storage.cpp | 50 ++++++++++++++++++++++++++++-- src/app/util/attribute-storage.h | 14 ++++++++- 2 files changed, 61 insertions(+), 3 deletions(-) diff --git a/src/app/util/attribute-storage.cpp b/src/app/util/attribute-storage.cpp index be5252f4d11cd9..5f3dc13381ab49 100644 --- a/src/app/util/attribute-storage.cpp +++ b/src/app/util/attribute-storage.cpp @@ -236,6 +236,40 @@ uint16_t emberAfGetDynamicIndexFromEndpoint(EndpointId id) return kEmberInvalidEndpointIndex; } +const EmberAfCluster * getClusterTypeDefinition(EndpointId endpointId, ClusterId clusterId, EmberAfClusterMask mask) +{ + uint16_t index = emberAfIndexFromEndpointIncludingDisabledEndpoints(endpointId); + if (index != kEmberInvalidEndpointIndex) + { + return emberAfFindClusterInType(emAfEndpoints[index].endpointType, clusterId, mask, nullptr); + } + // not found + return nullptr; +} + +EmberAfStatus setupDynamicEndpointDeclaration(EmberAfEndpointType & endpointType, EndpointId templateEndpointId, + const Span & templateClusterIds) +{ + // allocate cluster list + endpointType.clusterCount = static_cast(templateClusterIds.size()); + endpointType.cluster = new EmberAfCluster[endpointType.clusterCount]; + endpointType.endpointSize = 0; + // get the actual cluster pointers and sum up memory size + for (size_t i = 0; i < templateClusterIds.size(); i++) + { + auto cluster = getClusterTypeDefinition(templateEndpointId, templateClusterIds.data()[i], 0); + VerifyOrDieWithMsg(cluster, Support, "cluster 0x%04x template in endpoint %d does not exist", templateClusterIds.data()[i], + templateEndpointId); + // for now, we need to copy the cluster definition, unfortunately. + // TODO: make endpointType use a pointer to a list of EmberAfCluster* instead, so we can re-use cluster definitions + // instead of duplicating them here once for every instance. + memcpy((void *) &endpointType.cluster[i], cluster, sizeof(EmberAfCluster)); + // sum up the needed storage + endpointType.endpointSize += cluster->clusterSize; + } + return EMBER_ZCL_STATUS_SUCCESS; +} + EmberAfStatus emberAfSetDynamicEndpoint(uint16_t index, EndpointId id, const EmberAfEndpointType * ep, const chip::Span & dataVersionStorage, chip::Span deviceTypeList, EndpointId parentEndpointId, @@ -289,6 +323,17 @@ EmberAfStatus emberAfSetDynamicEndpoint(uint16_t index, EndpointId id, const Emb } } + if (dynamicAttributeStorage != nullptr && ep->endpointSize > 0) + { + // Flag the endpoint as enabled here, because otherwise loading attributes cannot work + emAfEndpoints[index].bitmask = EMBER_AF_ENDPOINT_ENABLED; + // Load attributes from NVstorage or set defaults + emAfLoadAttributeDefaults(id, false); + // set disabled again, so enabling below will detect a transition + emAfEndpoints[index].bitmask = EMBER_AF_ENDPOINT_DISABLED; + } + // + // Now enable the endpoint. emberAfEndpointEnableDisable(id, true); @@ -631,8 +676,9 @@ EmberAfStatus emAfReadOrWriteAttribute(EmberAfAttributeSearchRecord * attRecord, (am->mask & ATTRIBUTE_MASK_SINGLETON ? singletonAttributeLocation(am) : (hasDynamicAttributeStorage ? emAfEndpoints[ep].dynamicAttributeStorage - : attributeData) + attributeStorageOffset); - ; + : attributeData) + + attributeStorageOffset); + uint8_t *src, *dst; if (write) diff --git a/src/app/util/attribute-storage.h b/src/app/util/attribute-storage.h index 227d85782d3d2b..325a76c82fda6a 100644 --- a/src/app/util/attribute-storage.h +++ b/src/app/util/attribute-storage.h @@ -17,7 +17,7 @@ #pragma once -//#include PLATFORM_HEADER +// #include PLATFORM_HEADER #include #include #include @@ -209,6 +209,18 @@ chip::Span emberAfDeviceTypeListFromEndpoint(chip::Endp // CHIP_ERROR emberAfSetDeviceTypeList(chip::EndpointId endpoint, chip::Span deviceTypeList); +// setup a dynamic endpoint's EmberAfEndpointType from a list of template clusters. +// +// This is a alternative to declare dynamic endpoint metadata using DECLARE_DYNAMIC_* macros. +// +// As clusters to be used in dynamic endpoint setup need to be defined in ZAP anyway +// (usually on a special endpoint which remains always disabled), the cluster's +// metadata including all attributes already exists and can be re-used this way, +// without error prone manual duplicating with DECLARE_DYNAMIC_* + +EmberAfStatus setupDynamicEndpointDeclaration(EmberAfEndpointType & endpointType, chip::EndpointId templateEndpointId, + const chip::Span & templateClusterIds); + // Register a dynamic endpoint. This involves registering descriptors that describe // the composition of the endpoint (encapsulated in the 'ep' argument) as well as providing // storage for data versions.