From cad43a1de5787175e00a4ddd86488a46c61aa1c8 Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Fri, 28 Jan 2022 23:42:42 -0500 Subject: [PATCH] Add data version storage to endpoints. (#14471) * Add data version storage to endpoints. For fixed endpoints we codegen the storage. For dynamic endpoints consumers are responsible for providing some. * Address review comments. --- examples/bridge-app/esp32/main/main.cpp | 20 ++-- examples/bridge-app/linux/main.cpp | 27 ++++-- examples/tv-app/linux/AppImpl.cpp | 13 ++- src/app/app-platform/ContentAppPlatform.cpp | 6 +- src/app/app-platform/ContentAppPlatform.h | 7 +- src/app/util/af-types.h | 5 + src/app/util/af.h | 10 ++ src/app/util/attribute-storage.cpp | 93 +++++++++++++++++-- src/app/util/attribute-storage.h | 16 +++- .../templates/app/endpoint_config.zapt | 1 + src/app/zap-templates/templates/app/helper.js | 20 ++++ src/controller/tests/TestReadChunking.cpp | 34 +++---- .../tests/TestServerCommandDispatch.cpp | 5 +- .../zap-generated/endpoint_config.h | 2 + .../zap-generated/endpoint_config.h | 2 + .../zap-generated/endpoint_config.h | 2 + .../zap-generated/endpoint_config.h | 2 + .../zap-generated/endpoint_config.h | 2 + .../zap-generated/endpoint_config.h | 2 + .../lock-app/zap-generated/endpoint_config.h | 2 + .../zap-generated/endpoint_config.h | 2 + .../zap-generated/endpoint_config.h | 2 + .../zap-generated/endpoint_config.h | 2 + .../app1/zap-generated/endpoint_config.h | 2 + .../app2/zap-generated/endpoint_config.h | 2 + .../pump-app/zap-generated/endpoint_config.h | 2 + .../zap-generated/endpoint_config.h | 2 + .../zap-generated/endpoint_config.h | 2 + .../zap-generated/endpoint_config.h | 2 + .../tv-app/zap-generated/endpoint_config.h | 2 + .../zap-generated/endpoint_config.h | 2 + .../zap-generated/endpoint_config.h | 2 + 32 files changed, 247 insertions(+), 48 deletions(-) diff --git a/examples/bridge-app/esp32/main/main.cpp b/examples/bridge-app/esp32/main/main.cpp index 779d0636772bf6..be17e3fa11466a 100644 --- a/examples/bridge-app/esp32/main/main.cpp +++ b/examples/bridge-app/esp32/main/main.cpp @@ -105,6 +105,11 @@ DECLARE_DYNAMIC_CLUSTER(ZCL_ON_OFF_CLUSTER_ID, onOffAttrs), DECLARE_DYNAMIC_CLUS // Declare Bridged Light endpoint DECLARE_DYNAMIC_ENDPOINT(bridgedLightEndpoint, bridgedLightClusters); +DataVersion gLight1DataVersions[ArraySize(bridgedLightClusters)]; +DataVersion gLight2DataVersions[ArraySize(bridgedLightClusters)]; +DataVersion gLight3DataVersions[ArraySize(bridgedLightClusters)]; +DataVersion gLight4DataVersions[ArraySize(bridgedLightClusters)]; + /* REVISION definitions: */ @@ -113,7 +118,8 @@ DECLARE_DYNAMIC_ENDPOINT(bridgedLightEndpoint, bridgedLightClusters); #define ZCL_FIXED_LABEL_CLUSTER_REVISION (1u) #define ZCL_ON_OFF_CLUSTER_REVISION (4u) -CHIP_ERROR AddDeviceEndpoint(Device * dev, EmberAfEndpointType * ep, uint16_t deviceType) +CHIP_ERROR AddDeviceEndpoint(Device * dev, EmberAfEndpointType * ep, uint16_t deviceType, + const Span & dataVersionStorage) { uint8_t index = 0; while (index < CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT) @@ -122,7 +128,7 @@ CHIP_ERROR AddDeviceEndpoint(Device * dev, EmberAfEndpointType * ep, uint16_t de { gDevices[index] = dev; EmberAfStatus ret; - ret = emberAfSetDynamicEndpoint(index, gCurrentEndpointId, ep, deviceType, DEVICE_VERSION_DEFAULT); + ret = emberAfSetDynamicEndpoint(index, gCurrentEndpointId, ep, deviceType, DEVICE_VERSION_DEFAULT, dataVersionStorage); if (ret == EMBER_ZCL_STATUS_SUCCESS) { ChipLogProgress(DeviceLayer, "Added device %s to dynamic endpoint %d (index=%d)", dev->GetName(), @@ -360,18 +366,18 @@ static void InitServer(intptr_t context) emberAfEndpointEnableDisable(emberAfEndpointFromIndex(static_cast(emberAfFixedEndpointCount() - 1)), false); // Add lights 1..3 --> will be mapped to ZCL endpoints 2, 3, 4 - AddDeviceEndpoint(&gLight1, &bridgedLightEndpoint, DEVICE_TYPE_LO_ON_OFF_LIGHT); - AddDeviceEndpoint(&gLight2, &bridgedLightEndpoint, DEVICE_TYPE_LO_ON_OFF_LIGHT); - AddDeviceEndpoint(&gLight3, &bridgedLightEndpoint, DEVICE_TYPE_LO_ON_OFF_LIGHT); + AddDeviceEndpoint(&gLight1, &bridgedLightEndpoint, DEVICE_TYPE_LO_ON_OFF_LIGHT, Span(gLight1DataVersions)); + AddDeviceEndpoint(&gLight2, &bridgedLightEndpoint, DEVICE_TYPE_LO_ON_OFF_LIGHT, Span(gLight2DataVersions)); + AddDeviceEndpoint(&gLight3, &bridgedLightEndpoint, DEVICE_TYPE_LO_ON_OFF_LIGHT, Span(gLight3DataVersions)); // Remove Light 2 -- Lights 1 & 3 will remain mapped to endpoints 2 & 4 RemoveDeviceEndpoint(&gLight2); // Add Light 4 -- > will be mapped to ZCL endpoint 5 - AddDeviceEndpoint(&gLight4, &bridgedLightEndpoint, DEVICE_TYPE_LO_ON_OFF_LIGHT); + AddDeviceEndpoint(&gLight4, &bridgedLightEndpoint, DEVICE_TYPE_LO_ON_OFF_LIGHT, Span(gLight4DataVersions)); // Re-add Light 2 -- > will be mapped to ZCL endpoint 6 - AddDeviceEndpoint(&gLight2, &bridgedLightEndpoint, DEVICE_TYPE_LO_ON_OFF_LIGHT); + AddDeviceEndpoint(&gLight2, &bridgedLightEndpoint, DEVICE_TYPE_LO_ON_OFF_LIGHT, Span(gLight2DataVersions)); } extern "C" void app_main() diff --git a/examples/bridge-app/linux/main.cpp b/examples/bridge-app/linux/main.cpp index 5af2a42f341c9e..bf4627f8109d5a 100644 --- a/examples/bridge-app/linux/main.cpp +++ b/examples/bridge-app/linux/main.cpp @@ -124,6 +124,10 @@ DECLARE_DYNAMIC_CLUSTER(ZCL_ON_OFF_CLUSTER_ID, onOffAttrs), DECLARE_DYNAMIC_CLUS // Declare Bridged Light endpoint DECLARE_DYNAMIC_ENDPOINT(bridgedLightEndpoint, bridgedLightClusters); +DataVersion gLight1DataVersions[ArraySize(bridgedLightClusters)]; +DataVersion gLight2DataVersions[ArraySize(bridgedLightClusters)]; +DataVersion gLight3DataVersions[ArraySize(bridgedLightClusters)]; +DataVersion gLight4DataVersions[ArraySize(bridgedLightClusters)]; // --------------------------------------------------------------------------- // @@ -169,6 +173,8 @@ DECLARE_DYNAMIC_CLUSTER(ZCL_SWITCH_CLUSTER_ID, switchAttrs), // Declare Bridged Switch endpoint DECLARE_DYNAMIC_ENDPOINT(bridgedSwitchEndpoint, bridgedSwitchClusters); +DataVersion gSwitch1DataVersions[ArraySize(bridgedSwitchClusters)]; +DataVersion gSwitch2DataVersions[ArraySize(bridgedSwitchClusters)]; // REVISION DEFINITIONS: // ================================================================================= @@ -181,7 +187,7 @@ DECLARE_DYNAMIC_ENDPOINT(bridgedSwitchEndpoint, bridgedSwitchClusters); // --------------------------------------------------------------------------- -int AddDeviceEndpoint(Device * dev, EmberAfEndpointType * ep, uint16_t deviceType) +int AddDeviceEndpoint(Device * dev, EmberAfEndpointType * ep, uint16_t deviceType, const Span & dataVersionStorage) { uint8_t index = 0; while (index < CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT) @@ -192,7 +198,8 @@ int AddDeviceEndpoint(Device * dev, EmberAfEndpointType * ep, uint16_t deviceTyp EmberAfStatus ret; while (1) { - ret = emberAfSetDynamicEndpoint(index, gCurrentEndpointId, ep, deviceType, DEVICE_VERSION_DEFAULT); + ret = emberAfSetDynamicEndpoint(index, gCurrentEndpointId, ep, deviceType, DEVICE_VERSION_DEFAULT, + dataVersionStorage); if (ret == EMBER_ZCL_STATUS_SUCCESS) { ChipLogProgress(DeviceLayer, "Added device %s to dynamic endpoint %d (index=%d)", dev->GetName(), @@ -648,22 +655,24 @@ int main(int argc, char * argv[]) emberAfEndpointEnableDisable(emberAfEndpointFromIndex(static_cast(emberAfFixedEndpointCount() - 1)), false); // Add lights 1..3 --> will be mapped to ZCL endpoints 2, 3, 4 - AddDeviceEndpoint(&Light1, &bridgedLightEndpoint, DEVICE_TYPE_LO_ON_OFF_LIGHT); - AddDeviceEndpoint(&Light2, &bridgedLightEndpoint, DEVICE_TYPE_LO_ON_OFF_LIGHT); - AddDeviceEndpoint(&Light3, &bridgedLightEndpoint, DEVICE_TYPE_LO_ON_OFF_LIGHT); + AddDeviceEndpoint(&Light1, &bridgedLightEndpoint, DEVICE_TYPE_LO_ON_OFF_LIGHT, Span(gLight1DataVersions)); + AddDeviceEndpoint(&Light2, &bridgedLightEndpoint, DEVICE_TYPE_LO_ON_OFF_LIGHT, Span(gLight2DataVersions)); + AddDeviceEndpoint(&Light3, &bridgedLightEndpoint, DEVICE_TYPE_LO_ON_OFF_LIGHT, Span(gLight3DataVersions)); // Remove Light 2 -- Lights 1 & 3 will remain mapped to endpoints 2 & 4 RemoveDeviceEndpoint(&Light2); // Add Light 4 -- > will be mapped to ZCL endpoint 5 - AddDeviceEndpoint(&Light4, &bridgedLightEndpoint, DEVICE_TYPE_LO_ON_OFF_LIGHT); + AddDeviceEndpoint(&Light4, &bridgedLightEndpoint, DEVICE_TYPE_LO_ON_OFF_LIGHT, Span(gLight4DataVersions)); // Re-add Light 2 -- > will be mapped to ZCL endpoint 6 - AddDeviceEndpoint(&Light2, &bridgedLightEndpoint, DEVICE_TYPE_LO_ON_OFF_LIGHT); + AddDeviceEndpoint(&Light2, &bridgedLightEndpoint, DEVICE_TYPE_LO_ON_OFF_LIGHT, Span(gLight2DataVersions)); // Add switch 1..2 --> will be mapped to ZCL endpoints 7,8 - AddDeviceEndpoint(&Switch1, &bridgedSwitchEndpoint, DEVICE_TYPE_LO_ON_OFF_LIGHT_SWITCH); - AddDeviceEndpoint(&Switch2, &bridgedSwitchEndpoint, DEVICE_TYPE_LO_ON_OFF_LIGHT_SWITCH); + AddDeviceEndpoint(&Switch1, &bridgedSwitchEndpoint, DEVICE_TYPE_LO_ON_OFF_LIGHT_SWITCH, + Span(gSwitch1DataVersions)); + AddDeviceEndpoint(&Switch2, &bridgedSwitchEndpoint, DEVICE_TYPE_LO_ON_OFF_LIGHT_SWITCH, + Span(gSwitch2DataVersions)); // Run CHIP diff --git a/examples/tv-app/linux/AppImpl.cpp b/examples/tv-app/linux/AppImpl.cpp index 00ad81b37086e2..6099c8fb48edba 100644 --- a/examples/tv-app/linux/AppImpl.cpp +++ b/examples/tv-app/linux/AppImpl.cpp @@ -147,6 +147,12 @@ DECLARE_DYNAMIC_CLUSTER(ZCL_DESCRIPTOR_CLUSTER_ID, descriptorAttrs), // Declare Content App endpoint DECLARE_DYNAMIC_ENDPOINT(contentAppEndpoint, contentAppClusters); +namespace { + +DataVersion gDataVersions[APP_LIBRARY_SIZE][ArraySize(contentAppClusters)]; + +} // anonymous namespace + ContentAppFactoryImpl::ContentAppFactoryImpl() {} uint16_t ContentAppFactoryImpl::GetPlatformCatalogVendorId() @@ -184,12 +190,15 @@ ContentApp * ContentAppFactoryImpl::LoadContentApp(CatalogVendorApp vendorApp) ChipLogProgress(DeviceLayer, "ContentAppFactoryImpl: LoadContentAppByAppId catalogVendorId=%d applicationId=%s ", vendorApp.catalogVendorId, vendorApp.applicationId); - for (auto & app : mContentApps) + for (size_t i = 0; i < ArraySize(mContentApps); ++i) { + auto & app = mContentApps[i]; + ChipLogProgress(DeviceLayer, " Looking next=%s ", app.GetApplicationBasicDelegate()->GetCatalogVendorApp()->applicationId); if (app.GetApplicationBasicDelegate()->GetCatalogVendorApp()->Matches(vendorApp)) { - ContentAppPlatform::GetInstance().AddContentApp(&app, &contentAppEndpoint, DEVICE_TYPE_CONTENT_APP); + ContentAppPlatform::GetInstance().AddContentApp(&app, &contentAppEndpoint, DEVICE_TYPE_CONTENT_APP, + Span(gDataVersions[i])); return &app; } } diff --git a/src/app/app-platform/ContentAppPlatform.cpp b/src/app/app-platform/ContentAppPlatform.cpp index db566d2fac3bb1..033c1b9ed6e08d 100644 --- a/src/app/app-platform/ContentAppPlatform.cpp +++ b/src/app/app-platform/ContentAppPlatform.cpp @@ -85,7 +85,8 @@ EmberAfStatus emberAfExternalAttributeWriteCallback(EndpointId endpoint, Cluster namespace chip { namespace AppPlatform { -EndpointId ContentAppPlatform::AddContentApp(ContentApp * app, EmberAfEndpointType * ep, uint16_t deviceType) +EndpointId ContentAppPlatform::AddContentApp(ContentApp * app, EmberAfEndpointType * ep, uint16_t deviceType, + const Span & dataVersionStorage) { CatalogVendorApp vendorApp = app->GetApplicationBasicDelegate()->GetCatalogVendorApp(); @@ -111,7 +112,8 @@ EndpointId ContentAppPlatform::AddContentApp(ContentApp * app, EmberAfEndpointTy EmberAfStatus ret; while (1) { - ret = emberAfSetDynamicEndpoint(index, mCurrentEndpointId, ep, deviceType, DEVICE_VERSION_DEFAULT); + ret = emberAfSetDynamicEndpoint(index, mCurrentEndpointId, ep, deviceType, DEVICE_VERSION_DEFAULT, + dataVersionStorage); if (ret == EMBER_ZCL_STATUS_SUCCESS) { ChipLogProgress(DeviceLayer, "Added ContentApp %s to dynamic endpoint %d (index=%d)", vendorApp.applicationId, diff --git a/src/app/app-platform/ContentAppPlatform.h b/src/app/app-platform/ContentAppPlatform.h index addd06d78424bb..87620f5bb6b5f4 100644 --- a/src/app/app-platform/ContentAppPlatform.h +++ b/src/app/app-platform/ContentAppPlatform.h @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -72,7 +73,11 @@ class DLL_EXPORT ContentAppPlatform // add apps to the platform. // This will assign the app to an endpoint (if it is not already added) and make it accessible via Matter // returns the global endpoint for this app, or 0 if an error occurred - EndpointId AddContentApp(ContentApp * app, EmberAfEndpointType * ep, uint16_t deviceType); + // + // dataVersionStorage.size() needs to be at least as big as the number of + // server clusters in the EmberAfEndpointType passed in. + EndpointId AddContentApp(ContentApp * app, EmberAfEndpointType * ep, uint16_t deviceType, + const Span & dataVersionStorage); // remove app from the platform. // returns the endpoint id where the app was, or 0 if app was not loaded diff --git a/src/app/util/af-types.h b/src/app/util/af-types.h index 5e4cadaed1a311..ed5a5fd59c29e2 100644 --- a/src/app/util/af-types.h +++ b/src/app/util/af-types.h @@ -386,6 +386,11 @@ typedef struct * Endpoint type for this endpoint. */ const EmberAfEndpointType * endpointType; + /** + * Pointer to the DataVersion storage for the server clusters on this + * endpoint + */ + chip::DataVersion * dataVersions; /** * Network index for this endpoint. */ diff --git a/src/app/util/af.h b/src/app/util/af.h index 54da99437cb96d..efd1c17901be49 100644 --- a/src/app/util/af.h +++ b/src/app/util/af.h @@ -1655,6 +1655,16 @@ int emberAfMain(MAIN_FUNCTION_PARAMETERS); */ EmberAfStatus emberAfClusterSpecificCommandParse(EmberAfClusterCommand * cmd); +/** + * Returns the pointer to the data version storage for the given endpoint and + * cluster. Can return null in the following cases: + * + * 1) There is no such endpoint. + * 2) There is no such server cluster on the given endpoint. + * 3) No storage for a data version was provided for the endpoint. + */ +chip::DataVersion * emberAfDataVersionStorage(chip::EndpointId endpointId, chip::ClusterId clusterId); + namespace chip { namespace app { diff --git a/src/app/util/attribute-storage.cpp b/src/app/util/attribute-storage.cpp index c3057e22d21f8a..395726129b81fa 100644 --- a/src/app/util/attribute-storage.cpp +++ b/src/app/util/attribute-storage.cpp @@ -97,6 +97,8 @@ GENERATED_FUNCTION_ARRAYS constexpr const EmberAfAttributeMetadata generatedAttributes[] = GENERATED_ATTRIBUTES; constexpr const EmberAfCluster generatedClusters[] = GENERATED_CLUSTERS; constexpr const EmberAfEndpointType generatedEmberAfEndpointTypes[] = GENERATED_ENDPOINT_TYPES; +// Not const, because these need to mutate. +DataVersion fixedEndpointDataVersions[ZAP_FIXED_ENDPOINT_DATA_VERSION_COUNT]; #if !defined(EMBER_SCRIPTED_TEST) #define endpointNumber(x) fixedEndpoints[x] @@ -131,15 +133,34 @@ void emberAfEndpointConfigure(void) uint8_t fixedNetworks[] = FIXED_NETWORKS; #endif - emberEndpointCount = FIXED_ENDPOINT_COUNT; +#if ZAP_FIXED_ENDPOINT_DATA_VERSION_COUNT > 0 + // Initialize our data version storage. If + // ZAP_FIXED_ENDPOINT_DATA_VERSION_COUNT == 0, gcc complains about a memset + // with size equal to number of elements without multiplication by element + // size, because the sizeof() is also 0 in that case... + if (Crypto::DRBG_get_bytes(reinterpret_cast(fixedEndpointDataVersions), sizeof(fixedEndpointDataVersions)) != + CHIP_NO_ERROR) + { + // Now what? At least 0-init it. + memset(fixedEndpointDataVersions, 0, sizeof(fixedEndpointDataVersions)); + } +#endif // ZAP_FIXED_ENDPOINT_DATA_VERSION_COUNT > 0 + + emberEndpointCount = FIXED_ENDPOINT_COUNT; + DataVersion * currentDataVersions = fixedEndpointDataVersions; for (ep = 0; ep < FIXED_ENDPOINT_COUNT; ep++) { emAfEndpoints[ep].endpoint = endpointNumber(ep); emAfEndpoints[ep].deviceId = endpointDeviceId(ep); emAfEndpoints[ep].deviceVersion = endpointDeviceVersion(ep); emAfEndpoints[ep].endpointType = endpointTypeMacro(ep); + emAfEndpoints[ep].dataVersions = currentDataVersions; emAfEndpoints[ep].networkIndex = endpointNetworkIndex(ep); emAfEndpoints[ep].bitmask = EMBER_AF_ENDPOINT_ENABLED; + + // Increment currentDataVersions by 1 (slot) for every server cluster + // this endpoint has. + currentDataVersions += emberAfClusterCountByIndex(ep, /* server = */ true); } #if CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT @@ -176,7 +197,7 @@ uint16_t emberAfGetDynamicIndexFromEndpoint(EndpointId id) } EmberAfStatus emberAfSetDynamicEndpoint(uint16_t index, EndpointId id, EmberAfEndpointType * ep, uint16_t deviceId, - uint8_t deviceVersion) + uint8_t deviceVersion, const Span & dataVersionStorage) { auto realIndex = index + FIXED_ENDPOINT_COUNT; @@ -189,6 +210,12 @@ EmberAfStatus emberAfSetDynamicEndpoint(uint16_t index, EndpointId id, EmberAfEn return EMBER_ZCL_STATUS_CONSTRAINT_ERROR; } + auto serverClusterCount = emberAfClusterCountForEndpointType(ep, /* server = */ true); + if (dataVersionStorage.size() < serverClusterCount) + { + return EMBER_ZCL_STATUS_INSUFFICIENT_SPACE; + } + index = static_cast(realIndex); for (uint16_t i = FIXED_ENDPOINT_COUNT; i < MAX_ENDPOINT_COUNT; i++) { @@ -202,12 +229,24 @@ EmberAfStatus emberAfSetDynamicEndpoint(uint16_t index, EndpointId id, EmberAfEn emAfEndpoints[index].deviceId = deviceId; emAfEndpoints[index].deviceVersion = deviceVersion; emAfEndpoints[index].endpointType = ep; + emAfEndpoints[index].dataVersions = dataVersionStorage.data(); emAfEndpoints[index].networkIndex = 0; // Start the endpoint off as disabled. emAfEndpoints[index].bitmask = EMBER_AF_ENDPOINT_DISABLED; emberAfSetDynamicEndpointCount(MAX_ENDPOINT_COUNT - FIXED_ENDPOINT_COUNT); + // Initialize the data versions. + size_t dataSize = sizeof(DataVersion) * serverClusterCount; + if (dataSize != 0) + { + if (Crypto::DRBG_get_bytes(reinterpret_cast(dataVersionStorage.data()), dataSize) != CHIP_NO_ERROR) + { + // Now what? At least 0-init it. + memset(dataVersionStorage.data(), 0, dataSize); + } + } + // Now enable the endpoint. emberAfEndpointEnableDisable(id, true); emberAfSetDeviceEnabled(id, true); @@ -971,22 +1010,31 @@ EndpointId emberAfEndpointFromIndex(uint16_t index) uint8_t emberAfClusterCount(EndpointId endpoint, bool server) { uint16_t index = emberAfIndexFromEndpoint(endpoint); - uint8_t i, c = 0; - EmberAfDefinedEndpoint * de; - const EmberAfCluster * cluster; - if (index == 0xFFFF) { return 0; } - de = &(emAfEndpoints[index]); + + return emberAfClusterCountByIndex(index, server); +} + +uint8_t emberAfClusterCountByIndex(uint16_t endpointIndex, bool server) +{ + const EmberAfDefinedEndpoint * de = &(emAfEndpoints[endpointIndex]); if (de->endpointType == NULL) { return 0; } - for (i = 0; i < de->endpointType->clusterCount; i++) + + return emberAfClusterCountForEndpointType(de->endpointType, server); +} + +uint8_t emberAfClusterCountForEndpointType(const EmberAfEndpointType * type, bool server) +{ + uint8_t c = 0; + for (uint8_t i = 0; i < type->clusterCount; i++) { - cluster = &(de->endpointType->cluster[i]); + auto * cluster = &(type->cluster[i]); if (server && emberAfClusterIsServer(cluster)) { c++; @@ -1381,3 +1429,30 @@ Optional emberAfGetServerAttributeIdByIndex(EndpointId endpoint, Cl } return Optional(clusterObj->attributes[attributeIndex].attributeId); } + +DataVersion * emberAfDataVersionStorage(chip::EndpointId endpointId, chip::ClusterId clusterId) +{ + uint16_t index = emberAfIndexFromEndpoint(endpointId); + if (index == 0xFFFF) + { + // Unknown endpoint. + return nullptr; + } + const EmberAfDefinedEndpoint & ep = emAfEndpoints[index]; + if (!ep.dataVersions) + { + // No storage provided. + return nullptr; + } + + // This does a second walk over endpoints to find the right one, but + // probably worth it to avoid duplicating code. + auto clusterIndex = emberAfClusterIndex(endpointId, clusterId, CLUSTER_MASK_SERVER); + if (clusterIndex == 0xFF) + { + // No such cluster on this endpoint. + return nullptr; + } + + return ep.dataVersions + clusterIndex; +} diff --git a/src/app/util/attribute-storage.h b/src/app/util/attribute-storage.h index 208d6dcffb38d7..1ae15547871cb1 100644 --- a/src/app/util/attribute-storage.h +++ b/src/app/util/attribute-storage.h @@ -165,6 +165,14 @@ uint8_t emberAfClusterIndex(chip::EndpointId endpoint, chip::ClusterId clusterId // otherwise number of client clusters on this endpoint uint8_t emberAfClusterCount(chip::EndpointId endpoint, bool server); +// If server == true, returns the number of server clusters, +// otherwise number of client clusters on the endpoint at the given index. +uint8_t emberAfClusterCountByIndex(uint16_t endpointIndex, bool server); + +// If server == true, returns the number of server clusters, +// otherwise number of client clusters on the endpoint at the given index. +uint8_t emberAfClusterCountForEndpointType(const EmberAfEndpointType * endpointType, bool server); + // Returns the cluster of Nth server or client cluster, // depending on server toggle. const EmberAfCluster * emberAfGetNthCluster(chip::EndpointId endpoint, uint8_t n, bool server); @@ -241,8 +249,14 @@ uint8_t emberAfGetClusterCountForEndpoint(chip::EndpointId endpoint); const EmberAfCluster * emberAfGetClusterByIndex(chip::EndpointId endpoint, uint8_t clusterIndex); uint16_t emberAfGetDeviceIdForEndpoint(chip::EndpointId endpoint); +// dataVersionStorage.size() needs to be at least as large as the number of +// server clusters on this endpoint. If it's not, the endpoint will not be able +// to store data versions, which may break consumers. +// +// The memory backing dataVersionStorage needs to stay alive until this dynamic +// endpoint is cleared. EmberAfStatus emberAfSetDynamicEndpoint(uint16_t index, chip::EndpointId id, EmberAfEndpointType * ep, uint16_t deviceId, - uint8_t deviceVersion); + uint8_t deviceVersion, const chip::Span & dataVersionStorage); chip::EndpointId emberAfClearDynamicEndpoint(uint16_t index); uint16_t emberAfGetDynamicIndexFromEndpoint(chip::EndpointId id); diff --git a/src/app/zap-templates/templates/app/endpoint_config.zapt b/src/app/zap-templates/templates/app/endpoint_config.zapt index ecb02f21053725..343b74865eab0a 100644 --- a/src/app/zap-templates/templates/app/endpoint_config.zapt +++ b/src/app/zap-templates/templates/app/endpoint_config.zapt @@ -45,6 +45,7 @@ #define ZAP_CLUSTER_INDEX(index) (&generatedClusters[index]) +#define ZAP_FIXED_ENDPOINT_DATA_VERSION_COUNT {{chip_endpoint_data_version_count}} // This is an array of EmberAfEndpointType structures. #define GENERATED_ENDPOINT_TYPES {{endpoint_types_list}} diff --git a/src/app/zap-templates/templates/app/helper.js b/src/app/zap-templates/templates/app/helper.js index 0e472b7544a213..fb64cd415660db 100644 --- a/src/app/zap-templates/templates/app/helper.js +++ b/src/app/zap-templates/templates/app/helper.js @@ -230,6 +230,25 @@ function chip_endpoint_cluster_list() return ret.concat('}\n'); } +/** + * Return the number of data versions we need for our fixed endpoints. + * + * This is just the count of server clusters on those endpoints. + */ +function chip_endpoint_data_version_count() +{ + let serverCount = 0; + for (const ep of this.endpoints) { + let epType = this.endpointTypes.find(type => type.id == ep.endpointTypeRef); + for (const cluster of epType.clusters) { + if (cluster.side == "server") { + ++serverCount; + } + } + } + return serverCount; +} + // End of Endpoint-config specific helpers function asPrintFormat(type) @@ -750,6 +769,7 @@ exports.asPrintFormat = asPrintFormat; exports.asReadType = asReadType; exports.chip_endpoint_generated_functions = chip_endpoint_generated_functions exports.chip_endpoint_cluster_list = chip_endpoint_cluster_list +exports.chip_endpoint_data_version_count = chip_endpoint_data_version_count; exports.asTypedLiteral = asTypedLiteral; exports.asLowerCamelCase = asLowerCamelCase; exports.asUpperCamelCase = asUpperCamelCase; diff --git a/src/controller/tests/TestReadChunking.cpp b/src/controller/tests/TestReadChunking.cpp index daffe607b8aed9..ba3e0d2687c69c 100644 --- a/src/controller/tests/TestReadChunking.cpp +++ b/src/controller/tests/TestReadChunking.cpp @@ -140,12 +140,17 @@ class TestAttrAccess : public app::AttributeAccessInterface { public: // Register for the Test Cluster cluster on all endpoints. - TestAttrAccess() : AttributeAccessInterface(Optional::Missing(), TestCluster::Id) {} + TestAttrAccess() : AttributeAccessInterface(Optional::Missing(), TestCluster::Id) + { + registerAttributeAccessOverride(this); + } CHIP_ERROR Read(const app::ConcreteReadAttributePath & aPath, app::AttributeValueEncoder & aEncoder) override; CHIP_ERROR Write(const app::ConcreteDataAttributePath & aPath, app::AttributeValueDecoder & aDecoder) override; }; +TestAttrAccess gAttrAccess; + CHIP_ERROR TestAttrAccess::Read(const app::ConcreteReadAttributePath & aPath, app::AttributeValueEncoder & aEncoder) { switch (aPath.mAttributeId) @@ -196,16 +201,13 @@ void TestCommandInteraction::TestChunking(nlTestSuite * apSuite, void * apContex TestContext & ctx = *static_cast(apContext); auto sessionHandle = ctx.GetSessionBobToAlice(); app::InteractionModelEngine * engine = app::InteractionModelEngine::GetInstance(); - TestAttrAccess testServer; // Initialize the ember side server logic InitDataModelHandler(&ctx.GetExchangeManager()); // Register our fake dynamic endpoint. - emberAfSetDynamicEndpoint(0, kTestEndpointId, &testEndpoint, 0, 0); - - // Register our fake attribute access interface. - registerAttributeAccessOverride(&testServer); + DataVersion dataVersionStorage[ArraySize(testEndpointClusters)]; + emberAfSetDynamicEndpoint(0, kTestEndpointId, &testEndpoint, 0, 0, Span(dataVersionStorage)); app::AttributePathParams attributePath(kTestEndpointId, app::Clusters::TestCluster::Id); app::ReadPrepareParams readParams(sessionHandle); @@ -261,6 +263,8 @@ void TestCommandInteraction::TestChunking(nlTestSuite * apSuite, void * apContex break; } } + + emberAfClearDynamicEndpoint(0); } // Similar to the test above, but for the list chunking feature. @@ -269,16 +273,13 @@ void TestCommandInteraction::TestListChunking(nlTestSuite * apSuite, void * apCo TestContext & ctx = *static_cast(apContext); auto sessionHandle = ctx.GetSessionBobToAlice(); app::InteractionModelEngine * engine = app::InteractionModelEngine::GetInstance(); - TestAttrAccess testServer; // Initialize the ember side server logic InitDataModelHandler(&ctx.GetExchangeManager()); // Register our fake dynamic endpoint. - emberAfSetDynamicEndpoint(0, kTestEndpointId3, &testEndpoint3, 0, 0); - - // Register our fake attribute access interface. - registerAttributeAccessOverride(&testServer); + DataVersion dataVersionStorage[ArraySize(testEndpoint3Clusters)]; + emberAfSetDynamicEndpoint(0, kTestEndpointId3, &testEndpoint3, 0, 0, Span(dataVersionStorage)); app::AttributePathParams attributePath(kTestEndpointId3, app::Clusters::TestCluster::Id, kTestListAttribute); app::ReadPrepareParams readParams(sessionHandle); @@ -335,6 +336,8 @@ void TestCommandInteraction::TestListChunking(nlTestSuite * apSuite, void * apCo break; } } + + emberAfClearDynamicEndpoint(0); } // Read an attribute that can never fit into the buffer. Result in an empty report, server should shutdown the transaction. @@ -343,16 +346,13 @@ void TestCommandInteraction::TestBadChunking(nlTestSuite * apSuite, void * apCon TestContext & ctx = *static_cast(apContext); auto sessionHandle = ctx.GetSessionBobToAlice(); app::InteractionModelEngine * engine = app::InteractionModelEngine::GetInstance(); - TestAttrAccess testServer; // Initialize the ember side server logic InitDataModelHandler(&ctx.GetExchangeManager()); // Register our fake dynamic endpoint. - emberAfSetDynamicEndpoint(0, kTestEndpointId3, &testEndpoint3, 0, 0); - - // Register our fake attribute access interface. - registerAttributeAccessOverride(&testServer); + DataVersion dataVersionStorage[ArraySize(testEndpoint3Clusters)]; + emberAfSetDynamicEndpoint(0, kTestEndpointId3, &testEndpoint3, 0, 0, Span(dataVersionStorage)); app::AttributePathParams attributePath(kTestEndpointId3, app::Clusters::TestCluster::Id, kTestBadAttribute); app::ReadPrepareParams readParams(sessionHandle); @@ -389,6 +389,8 @@ void TestCommandInteraction::TestBadChunking(nlTestSuite * apSuite, void * apCon // Sanity check NL_TEST_ASSERT(apSuite, ctx.GetExchangeManager().GetNumActiveExchanges() == 0); + + emberAfClearDynamicEndpoint(0); } // clang-format off diff --git a/src/controller/tests/TestServerCommandDispatch.cpp b/src/controller/tests/TestServerCommandDispatch.cpp index a7393625bc23e7..f094f717b65ac7 100644 --- a/src/controller/tests/TestServerCommandDispatch.cpp +++ b/src/controller/tests/TestServerCommandDispatch.cpp @@ -198,7 +198,8 @@ void TestCommandInteraction::TestDataResponse(nlTestSuite * apSuite, void * apCo // Register descriptors for this endpoint since they are needed // at command validation time to ensure the command actually exists on that endpoint. // - emberAfSetDynamicEndpoint(0, kTestEndpointId, &testEndpoint, 0, 0); + DataVersion dataVersionStorage[ArraySize(testEndpointClusters)]; + emberAfSetDynamicEndpoint(0, kTestEndpointId, &testEndpoint, 0, 0, Span(dataVersionStorage)); // Passing of stack variables by reference is only safe because of synchronous completion of the interaction. Otherwise, it's // not safe to do so. @@ -236,6 +237,8 @@ void TestCommandInteraction::TestDataResponse(nlTestSuite * apSuite, void * apCo NL_TEST_ASSERT(apSuite, onSuccessWasCalled && !onFailureWasCalled); NL_TEST_ASSERT(apSuite, ctx.GetExchangeManager().GetNumActiveExchanges() == 0); + + emberAfClearDynamicEndpoint(0); } // clang-format off diff --git a/zzz_generated/all-clusters-app/zap-generated/endpoint_config.h b/zzz_generated/all-clusters-app/zap-generated/endpoint_config.h index 49f42cdce4b983..6583881ccb0857 100644 --- a/zzz_generated/all-clusters-app/zap-generated/endpoint_config.h +++ b/zzz_generated/all-clusters-app/zap-generated/endpoint_config.h @@ -2798,6 +2798,8 @@ #define ZAP_CLUSTER_INDEX(index) (&generatedClusters[index]) +#define ZAP_FIXED_ENDPOINT_DATA_VERSION_COUNT 75 + // This is an array of EmberAfEndpointType structures. #define GENERATED_ENDPOINT_TYPES \ { \ diff --git a/zzz_generated/bridge-app/zap-generated/endpoint_config.h b/zzz_generated/bridge-app/zap-generated/endpoint_config.h index 105c9ba6916974..60919816456355 100644 --- a/zzz_generated/bridge-app/zap-generated/endpoint_config.h +++ b/zzz_generated/bridge-app/zap-generated/endpoint_config.h @@ -1032,6 +1032,8 @@ #define ZAP_CLUSTER_INDEX(index) (&generatedClusters[index]) +#define ZAP_FIXED_ENDPOINT_DATA_VERSION_COUNT 22 + // This is an array of EmberAfEndpointType structures. #define GENERATED_ENDPOINT_TYPES \ { \ diff --git a/zzz_generated/controller-clusters/zap-generated/endpoint_config.h b/zzz_generated/controller-clusters/zap-generated/endpoint_config.h index 1837076f78fabd..cb16d7a70607af 100644 --- a/zzz_generated/controller-clusters/zap-generated/endpoint_config.h +++ b/zzz_generated/controller-clusters/zap-generated/endpoint_config.h @@ -525,6 +525,8 @@ #define ZAP_CLUSTER_INDEX(index) (&generatedClusters[index]) +#define ZAP_FIXED_ENDPOINT_DATA_VERSION_COUNT 0 + // This is an array of EmberAfEndpointType structures. #define GENERATED_ENDPOINT_TYPES \ { \ diff --git a/zzz_generated/door-lock-app/zap-generated/endpoint_config.h b/zzz_generated/door-lock-app/zap-generated/endpoint_config.h index f91030ee5d0ec9..d071426def74d9 100644 --- a/zzz_generated/door-lock-app/zap-generated/endpoint_config.h +++ b/zzz_generated/door-lock-app/zap-generated/endpoint_config.h @@ -1028,6 +1028,8 @@ #define ZAP_CLUSTER_INDEX(index) (&generatedClusters[index]) +#define ZAP_FIXED_ENDPOINT_DATA_VERSION_COUNT 21 + // This is an array of EmberAfEndpointType structures. #define GENERATED_ENDPOINT_TYPES \ { \ diff --git a/zzz_generated/light-switch-app/zap-generated/endpoint_config.h b/zzz_generated/light-switch-app/zap-generated/endpoint_config.h index 2bdb1d049b8afe..6bd4c144e73785 100644 --- a/zzz_generated/light-switch-app/zap-generated/endpoint_config.h +++ b/zzz_generated/light-switch-app/zap-generated/endpoint_config.h @@ -896,6 +896,8 @@ #define ZAP_CLUSTER_INDEX(index) (&generatedClusters[index]) +#define ZAP_FIXED_ENDPOINT_DATA_VERSION_COUNT 22 + // This is an array of EmberAfEndpointType structures. #define GENERATED_ENDPOINT_TYPES \ { \ diff --git a/zzz_generated/lighting-app/zap-generated/endpoint_config.h b/zzz_generated/lighting-app/zap-generated/endpoint_config.h index a42b79f47c832f..1ac852f05ff647 100644 --- a/zzz_generated/lighting-app/zap-generated/endpoint_config.h +++ b/zzz_generated/lighting-app/zap-generated/endpoint_config.h @@ -1096,6 +1096,8 @@ #define ZAP_CLUSTER_INDEX(index) (&generatedClusters[index]) +#define ZAP_FIXED_ENDPOINT_DATA_VERSION_COUNT 27 + // This is an array of EmberAfEndpointType structures. #define GENERATED_ENDPOINT_TYPES \ { \ diff --git a/zzz_generated/lock-app/zap-generated/endpoint_config.h b/zzz_generated/lock-app/zap-generated/endpoint_config.h index f2e05092334b30..942a23abe17f16 100644 --- a/zzz_generated/lock-app/zap-generated/endpoint_config.h +++ b/zzz_generated/lock-app/zap-generated/endpoint_config.h @@ -984,6 +984,8 @@ #define ZAP_CLUSTER_INDEX(index) (&generatedClusters[index]) +#define ZAP_FIXED_ENDPOINT_DATA_VERSION_COUNT 21 + // This is an array of EmberAfEndpointType structures. #define GENERATED_ENDPOINT_TYPES \ { \ diff --git a/zzz_generated/log-source-app/zap-generated/endpoint_config.h b/zzz_generated/log-source-app/zap-generated/endpoint_config.h index b6f2b1d90683b7..0173b0ac77e99e 100644 --- a/zzz_generated/log-source-app/zap-generated/endpoint_config.h +++ b/zzz_generated/log-source-app/zap-generated/endpoint_config.h @@ -244,6 +244,8 @@ #define ZAP_CLUSTER_INDEX(index) (&generatedClusters[index]) +#define ZAP_FIXED_ENDPOINT_DATA_VERSION_COUNT 4 + // This is an array of EmberAfEndpointType structures. #define GENERATED_ENDPOINT_TYPES \ { \ diff --git a/zzz_generated/ota-provider-app/zap-generated/endpoint_config.h b/zzz_generated/ota-provider-app/zap-generated/endpoint_config.h index bb8b515d47ac01..68546318e6ae2b 100644 --- a/zzz_generated/ota-provider-app/zap-generated/endpoint_config.h +++ b/zzz_generated/ota-provider-app/zap-generated/endpoint_config.h @@ -264,6 +264,8 @@ #define ZAP_CLUSTER_INDEX(index) (&generatedClusters[index]) +#define ZAP_FIXED_ENDPOINT_DATA_VERSION_COUNT 8 + // This is an array of EmberAfEndpointType structures. #define GENERATED_ENDPOINT_TYPES \ { \ diff --git a/zzz_generated/ota-requestor-app/zap-generated/endpoint_config.h b/zzz_generated/ota-requestor-app/zap-generated/endpoint_config.h index fd1b72131fa854..a7fc1db67f7978 100644 --- a/zzz_generated/ota-requestor-app/zap-generated/endpoint_config.h +++ b/zzz_generated/ota-requestor-app/zap-generated/endpoint_config.h @@ -324,6 +324,8 @@ #define ZAP_CLUSTER_INDEX(index) (&generatedClusters[index]) +#define ZAP_FIXED_ENDPOINT_DATA_VERSION_COUNT 9 + // This is an array of EmberAfEndpointType structures. #define GENERATED_ENDPOINT_TYPES \ { \ diff --git a/zzz_generated/placeholder/app1/zap-generated/endpoint_config.h b/zzz_generated/placeholder/app1/zap-generated/endpoint_config.h index 40be28c27fd470..a62f4004f86ec9 100644 --- a/zzz_generated/placeholder/app1/zap-generated/endpoint_config.h +++ b/zzz_generated/placeholder/app1/zap-generated/endpoint_config.h @@ -457,6 +457,8 @@ #define ZAP_CLUSTER_INDEX(index) (&generatedClusters[index]) +#define ZAP_FIXED_ENDPOINT_DATA_VERSION_COUNT 13 + // This is an array of EmberAfEndpointType structures. #define GENERATED_ENDPOINT_TYPES \ { \ diff --git a/zzz_generated/placeholder/app2/zap-generated/endpoint_config.h b/zzz_generated/placeholder/app2/zap-generated/endpoint_config.h index 40be28c27fd470..a62f4004f86ec9 100644 --- a/zzz_generated/placeholder/app2/zap-generated/endpoint_config.h +++ b/zzz_generated/placeholder/app2/zap-generated/endpoint_config.h @@ -457,6 +457,8 @@ #define ZAP_CLUSTER_INDEX(index) (&generatedClusters[index]) +#define ZAP_FIXED_ENDPOINT_DATA_VERSION_COUNT 13 + // This is an array of EmberAfEndpointType structures. #define GENERATED_ENDPOINT_TYPES \ { \ diff --git a/zzz_generated/pump-app/zap-generated/endpoint_config.h b/zzz_generated/pump-app/zap-generated/endpoint_config.h index b264c8326156d3..a9eb435b1ddab0 100644 --- a/zzz_generated/pump-app/zap-generated/endpoint_config.h +++ b/zzz_generated/pump-app/zap-generated/endpoint_config.h @@ -918,6 +918,8 @@ #define ZAP_CLUSTER_INDEX(index) (&generatedClusters[index]) +#define ZAP_FIXED_ENDPOINT_DATA_VERSION_COUNT 22 + // This is an array of EmberAfEndpointType structures. #define GENERATED_ENDPOINT_TYPES \ { \ diff --git a/zzz_generated/pump-controller-app/zap-generated/endpoint_config.h b/zzz_generated/pump-controller-app/zap-generated/endpoint_config.h index 6cc245d3d6c2fb..a21901b10f4b09 100644 --- a/zzz_generated/pump-controller-app/zap-generated/endpoint_config.h +++ b/zzz_generated/pump-controller-app/zap-generated/endpoint_config.h @@ -947,6 +947,8 @@ #define ZAP_CLUSTER_INDEX(index) (&generatedClusters[index]) +#define ZAP_FIXED_ENDPOINT_DATA_VERSION_COUNT 18 + // This is an array of EmberAfEndpointType structures. #define GENERATED_ENDPOINT_TYPES \ { \ diff --git a/zzz_generated/temperature-measurement-app/zap-generated/endpoint_config.h b/zzz_generated/temperature-measurement-app/zap-generated/endpoint_config.h index 09011b013fdb86..1ed4bf68299606 100644 --- a/zzz_generated/temperature-measurement-app/zap-generated/endpoint_config.h +++ b/zzz_generated/temperature-measurement-app/zap-generated/endpoint_config.h @@ -571,6 +571,8 @@ #define ZAP_CLUSTER_INDEX(index) (&generatedClusters[index]) +#define ZAP_FIXED_ENDPOINT_DATA_VERSION_COUNT 18 + // This is an array of EmberAfEndpointType structures. #define GENERATED_ENDPOINT_TYPES \ { \ diff --git a/zzz_generated/thermostat/zap-generated/endpoint_config.h b/zzz_generated/thermostat/zap-generated/endpoint_config.h index 303f70f423d4c7..4ca3694161a3c4 100644 --- a/zzz_generated/thermostat/zap-generated/endpoint_config.h +++ b/zzz_generated/thermostat/zap-generated/endpoint_config.h @@ -1163,6 +1163,8 @@ #define ZAP_CLUSTER_INDEX(index) (&generatedClusters[index]) +#define ZAP_FIXED_ENDPOINT_DATA_VERSION_COUNT 26 + // This is an array of EmberAfEndpointType structures. #define GENERATED_ENDPOINT_TYPES \ { \ diff --git a/zzz_generated/tv-app/zap-generated/endpoint_config.h b/zzz_generated/tv-app/zap-generated/endpoint_config.h index 4bdfc8fdfe534e..687557986e6d16 100644 --- a/zzz_generated/tv-app/zap-generated/endpoint_config.h +++ b/zzz_generated/tv-app/zap-generated/endpoint_config.h @@ -1669,6 +1669,8 @@ #define ZAP_CLUSTER_INDEX(index) (&generatedClusters[index]) +#define ZAP_FIXED_ENDPOINT_DATA_VERSION_COUNT 45 + // This is an array of EmberAfEndpointType structures. #define GENERATED_ENDPOINT_TYPES \ { \ diff --git a/zzz_generated/tv-casting-app/zap-generated/endpoint_config.h b/zzz_generated/tv-casting-app/zap-generated/endpoint_config.h index 47597009559921..bddc4c32c8b0c5 100644 --- a/zzz_generated/tv-casting-app/zap-generated/endpoint_config.h +++ b/zzz_generated/tv-casting-app/zap-generated/endpoint_config.h @@ -1783,6 +1783,8 @@ #define ZAP_CLUSTER_INDEX(index) (&generatedClusters[index]) +#define ZAP_FIXED_ENDPOINT_DATA_VERSION_COUNT 47 + // This is an array of EmberAfEndpointType structures. #define GENERATED_ENDPOINT_TYPES \ { \ diff --git a/zzz_generated/window-app/zap-generated/endpoint_config.h b/zzz_generated/window-app/zap-generated/endpoint_config.h index ff7bc1a6cf95b7..5766c56845b60f 100644 --- a/zzz_generated/window-app/zap-generated/endpoint_config.h +++ b/zzz_generated/window-app/zap-generated/endpoint_config.h @@ -1057,6 +1057,8 @@ #define ZAP_CLUSTER_INDEX(index) (&generatedClusters[index]) +#define ZAP_FIXED_ENDPOINT_DATA_VERSION_COUNT 21 + // This is an array of EmberAfEndpointType structures. #define GENERATED_ENDPOINT_TYPES \ { \