diff --git a/src/app/util/af-types.h b/src/app/util/af-types.h index 7dd054efbda2d1..be08c792621046 100644 --- a/src/app/util/af-types.h +++ b/src/app/util/af-types.h @@ -221,7 +221,8 @@ struct EmberAfDefinedEndpoint /** * Pointer to the memory block to be used for automatic attribute storage if * this is a dynamic endpoint. If set, the memory block pointed at - * must have the sum of all endpointType->clusters[*].clustersize. + * must have a size equal to endpointType->endpointSize (which is + * the sum of all endpointType->clusters[*].clustersize). */ uint8_t * dynamicAttributeStorage = nullptr; diff --git a/src/app/util/attribute-storage.cpp b/src/app/util/attribute-storage.cpp index 3ef1d5901e985d..d5ae8ec3b8134e 100644 --- a/src/app/util/attribute-storage.cpp +++ b/src/app/util/attribute-storage.cpp @@ -238,6 +238,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 = (uint16_t) (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, @@ -275,7 +309,7 @@ EmberAfStatus emberAfSetDynamicEndpoint(uint16_t index, EndpointId id, const Emb emAfEndpoints[index].dataVersions = dataVersionStorage.data(); // Start the endpoint off as disabled. emAfEndpoints[index].bitmask.Clear(EmberAfEndpointOptions::isEnabled); - emAfEndpoints[index].parentEndpointId = parentEndpointId; + emAfEndpoints[index].parentEndpointId = parentEndpointId; emAfEndpoints[index].dynamicAttributeStorage = dynamicAttributeStorage; emberAfSetDynamicEndpointCount(MAX_ENDPOINT_COUNT - FIXED_ENDPOINT_COUNT); @@ -291,6 +325,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); @@ -633,8 +678,8 @@ 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.