| /** |
| * |
| * Copyright (c) 2020 Project CHIP Authors |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| /** |
| * |
| * Copyright (c) 2020 Silicon Labs |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| /***************************************************************************/ |
| /** |
| * @file |
| * @brief Contains the per-endpoint configuration of |
| *attribute tables. |
| ******************************************************************************* |
| ******************************************************************************/ |
| |
| #include "app/util/common.h" |
| #include <app/util/af.h> |
| #include <app/util/attribute-storage.h> |
| #include <lib/support/logging/CHIPLogging.h> |
| |
| #include <app-common/zap-generated/attribute-type.h> |
| #include <app-common/zap-generated/callback.h> |
| |
| using namespace chip; |
| |
| //------------------------------------------------------------------------------ |
| // Globals |
| // This is not declared CONST in order to handle dynamic endpoint information |
| // retrieved from tokens. |
| EmberAfDefinedEndpoint emAfEndpoints[MAX_ENDPOINT_COUNT]; |
| |
| #if (ATTRIBUTE_MAX_SIZE == 0) |
| #define ACTUAL_ATTRIBUTE_SIZE 1 |
| #else |
| #define ACTUAL_ATTRIBUTE_SIZE ATTRIBUTE_MAX_SIZE |
| #endif |
| |
| uint8_t attributeData[ACTUAL_ATTRIBUTE_SIZE]; |
| |
| #if (!defined(ATTRIBUTE_SINGLETONS_SIZE)) || (ATTRIBUTE_SINGLETONS_SIZE == 0) |
| #define ACTUAL_SINGLETONS_SIZE 1 |
| #else |
| #define ACTUAL_SINGLETONS_SIZE ATTRIBUTE_SINGLETONS_SIZE |
| #endif |
| uint8_t singletonAttributeData[ACTUAL_SINGLETONS_SIZE]; |
| |
| uint16_t emberEndpointCount = 0; |
| |
| // If we have attributes that are more than 2 bytes, then |
| // we need this data block for the defaults |
| #if (defined(GENERATED_DEFAULTS) && GENERATED_DEFAULTS_COUNT) |
| const uint8_t generatedDefaults[] = GENERATED_DEFAULTS; |
| #endif // GENERATED_DEFAULTS |
| |
| #if (defined(GENERATED_MIN_MAX_DEFAULTS) && GENERATED_MIN_MAX_DEFAULT_COUNT) |
| const EmberAfAttributeMinMaxValue minMaxDefaults[] = GENERATED_MIN_MAX_DEFAULTS; |
| #endif // GENERATED_MIN_MAX_DEFAULTS |
| |
| #ifdef GENERATED_FUNCTION_ARRAYS |
| GENERATED_FUNCTION_ARRAYS |
| #endif |
| |
| #ifdef EMBER_AF_SUPPORT_COMMAND_DISCOVERY |
| const EmberAfCommandMetadata generatedCommands[] = GENERATED_COMMANDS; |
| const EmberAfManufacturerCodeEntry commandManufacturerCodes[] = GENERATED_COMMAND_MANUFACTURER_CODES; |
| const uint16_t commandManufacturerCodeCount = GENERATED_COMMAND_MANUFACTURER_CODE_COUNT; |
| #endif |
| |
| const EmberAfAttributeMetadata generatedAttributes[] = GENERATED_ATTRIBUTES; |
| const EmberAfCluster generatedClusters[] = GENERATED_CLUSTERS; |
| const EmberAfEndpointType generatedEmberAfEndpointTypes[] = GENERATED_ENDPOINT_TYPES; |
| |
| const EmberAfManufacturerCodeEntry clusterManufacturerCodes[] = GENERATED_CLUSTER_MANUFACTURER_CODES; |
| const uint16_t clusterManufacturerCodeCount = GENERATED_CLUSTER_MANUFACTURER_CODE_COUNT; |
| const EmberAfManufacturerCodeEntry attributeManufacturerCodes[] = GENERATED_ATTRIBUTE_MANUFACTURER_CODES; |
| const uint16_t attributeManufacturerCodeCount = GENERATED_ATTRIBUTE_MANUFACTURER_CODE_COUNT; |
| |
| #if !defined(EMBER_SCRIPTED_TEST) |
| #define endpointNumber(x) fixedEndpoints[x] |
| #define endpointDeviceId(x) fixedDeviceIds[x] |
| #define endpointDeviceVersion(x) fixedDeviceVersions[x] |
| // Added 'Macro' to silence MISRA warning about conflict with synonymous vars. |
| #define endpointTypeMacro(x) (EmberAfEndpointType *) &(generatedEmberAfEndpointTypes[fixedEmberAfEndpointTypes[x]]) |
| #define endpointNetworkIndex(x) fixedNetworks[x] |
| #endif |
| |
| namespace { |
| app::AttributeAccessInterface * gAttributeAccessOverrides = nullptr; |
| } // anonymous namespace |
| |
| //------------------------------------------------------------------------------ |
| // Forward declarations |
| |
| // Returns endpoint index within a given cluster |
| static uint16_t findClusterEndpointIndex(EndpointId endpoint, ClusterId clusterId, uint8_t mask, uint16_t manufacturerCode); |
| |
| #ifdef ZCL_USING_DESCRIPTOR_CLUSTER_SERVER |
| void emberAfPluginDescriptorServerInitCallback(void); |
| #endif |
| |
| //------------------------------------------------------------------------------ |
| |
| // Initial configuration |
| void emberAfEndpointConfigure(void) |
| { |
| uint8_t ep; |
| |
| #if !defined(EMBER_SCRIPTED_TEST) |
| uint16_t fixedEndpoints[] = FIXED_ENDPOINT_ARRAY; |
| uint16_t fixedDeviceIds[] = FIXED_DEVICE_IDS; |
| uint8_t fixedDeviceVersions[] = FIXED_DEVICE_VERSIONS; |
| uint8_t fixedEmberAfEndpointTypes[] = FIXED_ENDPOINT_TYPES; |
| uint8_t fixedNetworks[] = FIXED_NETWORKS; |
| #endif |
| |
| emberEndpointCount = FIXED_ENDPOINT_COUNT; |
| 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].networkIndex = endpointNetworkIndex(ep); |
| emAfEndpoints[ep].bitmask = EMBER_AF_ENDPOINT_ENABLED; |
| } |
| |
| #ifdef DYNAMIC_ENDPOINT_COUNT |
| if (MAX_ENDPOINT_COUNT > FIXED_ENDPOINT_COUNT) |
| { |
| // This is assuming that EMBER_AF_ENDPOINT_DISABLED is 0 |
| static_assert(EMBER_AF_ENDPOINT_DISABLED == 0, "We are creating enabled dynamic endpoints!"); |
| memset(&emAfEndpoints[FIXED_ENDPOINT_COUNT], 0, |
| sizeof(EmberAfDefinedEndpoint) * (MAX_ENDPOINT_COUNT - FIXED_ENDPOINT_COUNT)); |
| } |
| #endif |
| } |
| |
| void emberAfSetDynamicEndpointCount(uint16_t dynamicEndpointCount) |
| { |
| emberEndpointCount = static_cast<uint16_t>(FIXED_ENDPOINT_COUNT + dynamicEndpointCount); |
| } |
| |
| uint16_t emberAfGetDynamicIndexFromEndpoint(EndpointId id) |
| { |
| uint16_t index; |
| for (index = FIXED_ENDPOINT_COUNT; index < MAX_ENDPOINT_COUNT; index++) |
| { |
| if (emAfEndpoints[index].endpoint == id) |
| { |
| return static_cast<uint8_t>(index - FIXED_ENDPOINT_COUNT); |
| } |
| } |
| return 0xFFFF; |
| } |
| |
| EmberAfStatus emberAfSetDynamicEndpoint(uint16_t index, EndpointId id, EmberAfEndpointType * ep, uint16_t deviceId, |
| uint8_t deviceVersion) |
| { |
| auto realIndex = index + FIXED_ENDPOINT_COUNT; |
| |
| if (realIndex >= MAX_ENDPOINT_COUNT) |
| { |
| return EMBER_ZCL_STATUS_INSUFFICIENT_SPACE; |
| } |
| |
| index = static_cast<uint16_t>(realIndex); |
| for (uint16_t i = FIXED_ENDPOINT_COUNT; i < MAX_ENDPOINT_COUNT; i++) |
| { |
| if (emAfEndpoints[i].endpoint == id) |
| { |
| return EMBER_ZCL_STATUS_DUPLICATE_EXISTS; |
| } |
| } |
| |
| emAfEndpoints[index].endpoint = id; |
| emAfEndpoints[index].deviceId = deviceId; |
| emAfEndpoints[index].deviceVersion = deviceVersion; |
| emAfEndpoints[index].endpointType = ep; |
| emAfEndpoints[index].networkIndex = 0; |
| // Start the endpoint off as disabled. |
| emAfEndpoints[index].bitmask = EMBER_AF_ENDPOINT_DISABLED; |
| |
| emberAfSetDynamicEndpointCount(MAX_ENDPOINT_COUNT - FIXED_ENDPOINT_COUNT); |
| |
| // Now enable the endpoint. |
| emberAfEndpointEnableDisable(id, true); |
| emberAfSetDeviceEnabled(id, true); |
| |
| return EMBER_ZCL_STATUS_SUCCESS; |
| } |
| |
| EndpointId emberAfClearDynamicEndpoint(uint16_t index) |
| { |
| EndpointId ep = 0; |
| |
| index = static_cast<uint8_t>(index + FIXED_ENDPOINT_COUNT); |
| |
| if ((index < MAX_ENDPOINT_COUNT) && (emAfEndpoints[index].endpoint != 0) && (emberAfEndpointIndexIsEnabled(index))) |
| { |
| ep = emAfEndpoints[index].endpoint; |
| if (ep) |
| { |
| emberAfSetDeviceEnabled(ep, false); |
| emberAfEndpointEnableDisable(ep, false); |
| emAfEndpoints[index].endpoint = 0; |
| } |
| } |
| |
| return ep; |
| } |
| |
| uint16_t emberAfFixedEndpointCount(void) |
| { |
| return FIXED_ENDPOINT_COUNT; |
| } |
| |
| uint16_t emberAfEndpointCount(void) |
| { |
| return emberEndpointCount; |
| } |
| |
| bool emberAfEndpointIndexIsEnabled(uint16_t index) |
| { |
| return (emAfEndpoints[index].bitmask & EMBER_AF_ENDPOINT_ENABLED); |
| } |
| |
| // some data types (like strings) are sent OTA in human readable order |
| // (how they are read) instead of little endian as the data types are. |
| bool emberAfIsThisDataTypeAStringType(EmberAfAttributeType dataType) |
| { |
| return (dataType == ZCL_OCTET_STRING_ATTRIBUTE_TYPE || dataType == ZCL_CHAR_STRING_ATTRIBUTE_TYPE || |
| dataType == ZCL_LONG_OCTET_STRING_ATTRIBUTE_TYPE || dataType == ZCL_LONG_CHAR_STRING_ATTRIBUTE_TYPE); |
| } |
| |
| bool emberAfIsStringAttributeType(EmberAfAttributeType attributeType) |
| { |
| return (attributeType == ZCL_OCTET_STRING_ATTRIBUTE_TYPE || attributeType == ZCL_CHAR_STRING_ATTRIBUTE_TYPE); |
| } |
| |
| bool emberAfIsLongStringAttributeType(EmberAfAttributeType attributeType) |
| { |
| return (attributeType == ZCL_LONG_OCTET_STRING_ATTRIBUTE_TYPE || attributeType == ZCL_LONG_CHAR_STRING_ATTRIBUTE_TYPE); |
| } |
| |
| bool emberAfIsThisDataTypeAListType(EmberAfAttributeType dataType) |
| { |
| return dataType == ZCL_ARRAY_ATTRIBUTE_TYPE; |
| } |
| |
| // This function is used to call the per-cluster default response callback |
| void emberAfClusterDefaultResponseWithMfgCodeCallback(EndpointId endpoint, ClusterId clusterId, CommandId commandId, |
| EmberAfStatus status, uint8_t clientServerMask, uint16_t manufacturerCode) |
| { |
| EmberAfCluster * cluster = emberAfFindClusterWithMfgCode(endpoint, clusterId, clientServerMask, manufacturerCode); |
| if (cluster != NULL) |
| { |
| EmberAfGenericClusterFunction f = emberAfFindClusterFunction(cluster, CLUSTER_MASK_DEFAULT_RESPONSE_FUNCTION); |
| if (f != NULL) |
| { |
| // emberAfPushEndpointNetworkIndex(endpoint); |
| ((EmberAfDefaultResponseFunction) f)(endpoint, commandId, status); |
| // emberAfPopNetworkIndex(); |
| } |
| } |
| } |
| |
| // This function is used to call the per-cluster default response callback, and |
| // wraps the emberAfClusterDefaultResponseWithMfgCodeCallback with a |
| // EMBER_AF_NULL_MANUFACTURER_CODE. |
| void emberAfClusterDefaultResponseCallback(EndpointId endpoint, ClusterId clusterId, CommandId commandId, EmberAfStatus status, |
| uint8_t clientServerMask) |
| { |
| emberAfClusterDefaultResponseWithMfgCodeCallback(endpoint, clusterId, commandId, status, clientServerMask, |
| EMBER_AF_NULL_MANUFACTURER_CODE); |
| } |
| |
| // This function is used to call the per-cluster message sent callback |
| void emberAfClusterMessageSentWithMfgCodeCallback(const MessageSendDestination & destination, EmberApsFrame * apsFrame, |
| uint16_t msgLen, uint8_t * message, EmberStatus status, uint16_t mfgCode) |
| { |
| if (apsFrame != NULL && message != NULL && msgLen != 0) |
| { |
| EmberAfCluster * cluster = emberAfFindClusterWithMfgCode( |
| apsFrame->sourceEndpoint, apsFrame->clusterId, |
| (((message[0] & ZCL_FRAME_CONTROL_DIRECTION_MASK) == ZCL_FRAME_CONTROL_SERVER_TO_CLIENT) ? CLUSTER_MASK_SERVER |
| : CLUSTER_MASK_CLIENT), |
| mfgCode); |
| if (cluster != NULL) |
| { |
| EmberAfGenericClusterFunction f = emberAfFindClusterFunction(cluster, CLUSTER_MASK_MESSAGE_SENT_FUNCTION); |
| if (f != NULL) |
| { |
| // emberAfPushEndpointNetworkIndex(apsFrame->sourceEndpoint); |
| ((EmberAfMessageSentFunction) f)(destination, apsFrame, msgLen, message, status); |
| // emberAfPopNetworkIndex(); |
| } |
| } |
| } |
| } |
| |
| // This function is used to call the per-cluster message sent callback, and |
| // wraps the emberAfClusterMessageSentWithMfgCodeCallback with a |
| // EMBER_AF_NULL_MANUFACTURER_CODE. |
| void emberAfClusterMessageSentCallback(const MessageSendDestination & destination, EmberApsFrame * apsFrame, uint16_t msgLen, |
| uint8_t * message, EmberStatus status) |
| { |
| emberAfClusterMessageSentWithMfgCodeCallback(destination, apsFrame, msgLen, message, status, EMBER_AF_NULL_MANUFACTURER_CODE); |
| } |
| |
| // This function is used to call the per-cluster attribute changed callback |
| void emAfClusterAttributeChangedCallback(EndpointId endpoint, ClusterId clusterId, AttributeId attributeId, |
| uint8_t clientServerMask, uint16_t manufacturerCode) |
| { |
| EmberAfCluster * cluster = emberAfFindClusterWithMfgCode(endpoint, clusterId, clientServerMask, manufacturerCode); |
| if (cluster != NULL) |
| { |
| if (manufacturerCode == EMBER_AF_NULL_MANUFACTURER_CODE) |
| { |
| EmberAfGenericClusterFunction f = emberAfFindClusterFunction(cluster, CLUSTER_MASK_ATTRIBUTE_CHANGED_FUNCTION); |
| if (f != NULL) |
| { |
| // emberAfPushEndpointNetworkIndex(endpoint); |
| ((EmberAfClusterAttributeChangedCallback) f)(endpoint, attributeId); |
| // emberAfPopNetworkIndex(); |
| } |
| } |
| else |
| { |
| EmberAfGenericClusterFunction f = |
| emberAfFindClusterFunction(cluster, CLUSTER_MASK_MANUFACTURER_SPECIFIC_ATTRIBUTE_CHANGED_FUNCTION); |
| if (f != NULL) |
| { |
| // emberAfPushEndpointNetworkIndex(endpoint); |
| ((EmberAfManufacturerSpecificClusterAttributeChangedCallback) f)(endpoint, attributeId, manufacturerCode); |
| // emberAfPopNetworkIndex(); |
| } |
| } |
| } |
| } |
| |
| // This function is used to call the per-cluster pre-attribute changed callback |
| EmberAfStatus emAfClusterPreAttributeChangedCallback(EndpointId endpoint, ClusterId clusterId, AttributeId attributeId, |
| uint8_t clientServerMask, uint16_t manufacturerCode, |
| EmberAfAttributeType attributeType, uint16_t size, uint8_t * value) |
| { |
| EmberAfCluster * cluster = emberAfFindClusterWithMfgCode(endpoint, clusterId, clientServerMask, manufacturerCode); |
| if (cluster == NULL) |
| { |
| return EMBER_ZCL_STATUS_UNSUPPORTED_ATTRIBUTE; |
| } |
| else |
| { |
| EmberAfStatus status = EMBER_ZCL_STATUS_SUCCESS; |
| if (manufacturerCode == EMBER_AF_NULL_MANUFACTURER_CODE) |
| { |
| EmberAfGenericClusterFunction f = emberAfFindClusterFunction(cluster, CLUSTER_MASK_PRE_ATTRIBUTE_CHANGED_FUNCTION); |
| if (f != NULL) |
| { |
| // emberAfPushEndpointNetworkIndex(endpoint); |
| status = ((EmberAfClusterPreAttributeChangedCallback) f)(endpoint, attributeId, attributeType, size, value); |
| // emberAfPopNetworkIndex(); |
| } |
| } |
| return status; |
| } |
| } |
| |
| static void initializeEndpoint(EmberAfDefinedEndpoint * definedEndpoint) |
| { |
| uint8_t clusterIndex; |
| EmberAfEndpointType * epType = definedEndpoint->endpointType; |
| // emberAfPushEndpointNetworkIndex(definedEndpoint->endpoint); |
| for (clusterIndex = 0; clusterIndex < epType->clusterCount; clusterIndex++) |
| { |
| EmberAfCluster * cluster = &(epType->cluster[clusterIndex]); |
| EmberAfGenericClusterFunction f; |
| emberAfClusterInitCallback(definedEndpoint->endpoint, cluster->clusterId); |
| f = emberAfFindClusterFunction(cluster, CLUSTER_MASK_INIT_FUNCTION); |
| if (f != NULL) |
| { |
| ((EmberAfInitFunction) f)(definedEndpoint->endpoint); |
| } |
| } |
| // emberAfPopNetworkIndex(); |
| } |
| |
| // Calls the init functions. |
| void emAfCallInits(void) |
| { |
| uint8_t index; |
| for (index = 0; index < emberAfEndpointCount(); index++) |
| { |
| if (emberAfEndpointIndexIsEnabled(index)) |
| { |
| initializeEndpoint(&(emAfEndpoints[index])); |
| } |
| } |
| } |
| |
| // Returns the pointer to metadata, or null if it is not found |
| EmberAfAttributeMetadata * emberAfLocateAttributeMetadata(EndpointId endpoint, ClusterId clusterId, AttributeId attributeId, |
| uint8_t mask, uint16_t manufacturerCode) |
| { |
| EmberAfAttributeMetadata * metadata = NULL; |
| EmberAfAttributeSearchRecord record; |
| record.endpoint = endpoint; |
| record.clusterId = clusterId; |
| record.clusterMask = mask; |
| record.attributeId = attributeId; |
| record.manufacturerCode = manufacturerCode; |
| emAfReadOrWriteAttribute(&record, &metadata, |
| NULL, // buffer |
| 0, // buffer size |
| false); // write? |
| return metadata; |
| } |
| |
| static uint8_t * singletonAttributeLocation(EmberAfAttributeMetadata * am) |
| { |
| EmberAfAttributeMetadata * m = (EmberAfAttributeMetadata *) &(generatedAttributes[0]); |
| uint16_t index = 0; |
| while (m < am) |
| { |
| if ((m->mask & ATTRIBUTE_MASK_SINGLETON) != 0U) |
| { |
| index = static_cast<uint16_t>(index + m->size); |
| } |
| m++; |
| } |
| return (uint8_t *) (singletonAttributeData + index); |
| } |
| |
| // This function does mem copy, but smartly, which means that if the type is a |
| // string, it will copy as much as it can. |
| // If src == NULL, then this method will set memory to zeroes |
| // See documentation for emAfReadOrWriteAttribute for the semantics of |
| // readLength when reading and writing. |
| // |
| // The index argument is used exclusively for List. When reading or writing a List attribute, it could take 3 types of values: |
| // -1: Read/Write the whole list content, including the number of elements in the list |
| // 0: Read/Write the number of elements in the list, represented as a uint16_t |
| // n: Read/Write the nth element of the list |
| static EmberAfStatus typeSensitiveMemCopy(ClusterId clusterId, uint8_t * dest, uint8_t * src, EmberAfAttributeMetadata * am, |
| bool write, uint16_t readLength, int32_t index) |
| { |
| EmberAfAttributeType attributeType = am->attributeType; |
| // readLength == 0 for a read indicates that we should just trust that the |
| // caller has enough space for an attribute... |
| bool ignoreReadLength = write || (readLength == 0); |
| uint16_t bufferSize = ignoreReadLength ? am->size : readLength; |
| |
| if (emberAfIsStringAttributeType(attributeType)) |
| { |
| if (bufferSize < 1) |
| { |
| return EMBER_ZCL_STATUS_INSUFFICIENT_SPACE; |
| } |
| emberAfCopyString(dest, src, bufferSize - 1); |
| } |
| else if (emberAfIsLongStringAttributeType(attributeType)) |
| { |
| if (bufferSize < 2) |
| { |
| return EMBER_ZCL_STATUS_INSUFFICIENT_SPACE; |
| } |
| emberAfCopyLongString(dest, src, bufferSize - 2); |
| } |
| else if (emberAfIsThisDataTypeAListType(attributeType)) |
| { |
| if (bufferSize < 2) |
| { |
| return EMBER_ZCL_STATUS_INSUFFICIENT_SPACE; |
| } |
| |
| emberAfCopyList(clusterId, am, write, dest, src, index); |
| } |
| else |
| { |
| if (!ignoreReadLength && readLength < am->size) |
| { |
| return EMBER_ZCL_STATUS_INSUFFICIENT_SPACE; |
| } |
| if (src == NULL) |
| { |
| memset(dest, 0, am->size); |
| } |
| else |
| { |
| memmove(dest, src, am->size); |
| } |
| } |
| return EMBER_ZCL_STATUS_SUCCESS; |
| } |
| |
| // Returns the manufacturer code or ::EMBER_AF_NULL_MANUFACTURER_CODE if none |
| // could be found. |
| static uint16_t getManufacturerCode(EmberAfManufacturerCodeEntry * codes, uint16_t codeTableSize, uint16_t tableIndex) |
| { |
| uint16_t i; |
| for (i = 0; i < codeTableSize; i++) |
| { |
| if (codes->index == tableIndex) |
| { |
| return codes->manufacturerCode; |
| } |
| codes++; |
| } |
| return EMBER_AF_NULL_MANUFACTURER_CODE; |
| } |
| |
| // This function basically wraps getManufacturerCode with the parameters |
| // associating an attributes metadata with its code. |
| uint16_t emberAfGetMfgCode(EmberAfAttributeMetadata * metadata) |
| { |
| return getManufacturerCode((EmberAfManufacturerCodeEntry *) attributeManufacturerCodes, attributeManufacturerCodeCount, |
| static_cast<uint16_t>((metadata - generatedAttributes))); |
| } |
| |
| uint16_t emAfGetManufacturerCodeForAttribute(EmberAfCluster * cluster, EmberAfAttributeMetadata * attMetaData) |
| { |
| return (emberAfClusterIsManufacturerSpecific(cluster) ? emAfGetManufacturerCodeForCluster(cluster) |
| : emberAfGetMfgCode(attMetaData)); |
| } |
| |
| uint16_t emAfGetManufacturerCodeForCluster(EmberAfCluster * cluster) |
| { |
| return getManufacturerCode((EmberAfManufacturerCodeEntry *) clusterManufacturerCodes, clusterManufacturerCodeCount, |
| static_cast<uint16_t>(cluster - generatedClusters)); |
| } |
| |
| /** |
| * @brief Matches a cluster based on cluster id, direction and manufacturer code. |
| * This function assumes that the passed cluster's endpoint already |
| * matches the endpoint of the EmberAfAttributeSearchRecord. |
| * |
| * Cluster's match if: |
| * 1. Cluster ids match AND |
| * 2. Cluster directions match as defined by cluster->mask |
| * and attRecord->clusterMask AND |
| * 3. If the clusters are mf specific, their mfg codes match. |
| */ |
| bool emAfMatchCluster(EmberAfCluster * cluster, EmberAfAttributeSearchRecord * attRecord) |
| { |
| return (cluster->clusterId == attRecord->clusterId && cluster->mask & attRecord->clusterMask && |
| (!emberAfClusterIsManufacturerSpecific(cluster) || |
| (emAfGetManufacturerCodeForCluster(cluster) == attRecord->manufacturerCode))); |
| } |
| |
| /** |
| * @brief Matches an attribute based on attribute id and manufacturer code. |
| * This function assumes that the passed cluster already matches the |
| * clusterId, direction and mf specificity of the passed |
| * EmberAfAttributeSearchRecord. |
| * |
| * Note: If both the attribute and cluster are manufacturer specific, |
| * the cluster's mf code gets precedence. |
| * |
| * Attributes match if: |
| * 1. Att ids match AND |
| * a. cluster IS mf specific OR |
| * b. both stored and saught attributes are NOT mf specific OR |
| * c. stored att IS mf specific AND mfg codes match. |
| */ |
| bool emAfMatchAttribute(EmberAfCluster * cluster, EmberAfAttributeMetadata * am, EmberAfAttributeSearchRecord * attRecord) |
| { |
| return (am->attributeId == attRecord->attributeId && |
| (emberAfClusterIsManufacturerSpecific(cluster) || |
| (emAfGetManufacturerCodeForAttribute(cluster, am) == attRecord->manufacturerCode))); |
| } |
| |
| // When reading non-string attributes, this function returns an error when destination |
| // buffer isn't large enough to accommodate the attribute type. For strings, the |
| // function will copy at most readLength bytes. This means the resulting string |
| // may be truncated. The length byte(s) in the resulting string will reflect |
| // any truncation. If readLength is zero, we are working with backwards- |
| // compatibility wrapper functions and we just cross our fingers and hope for |
| // the best. |
| // |
| // When writing attributes, readLength is ignored. For non-string attributes, |
| // this function assumes the source buffer is the same size as the attribute |
| // type. For strings, the function will copy as many bytes as will fit in the |
| // attribute. This means the resulting string may be truncated. The length |
| // byte(s) in the resulting string will reflect any truncated. |
| EmberAfStatus emAfReadOrWriteAttribute(EmberAfAttributeSearchRecord * attRecord, EmberAfAttributeMetadata ** metadata, |
| uint8_t * buffer, uint16_t readLength, bool write, int32_t index) |
| { |
| uint8_t i; |
| uint16_t attributeOffsetIndex = 0; |
| |
| for (i = 0; i < emberAfEndpointCount(); i++) |
| { |
| if (emAfEndpoints[i].endpoint == attRecord->endpoint) |
| { |
| EmberAfEndpointType * endpointType = emAfEndpoints[i].endpointType; |
| uint8_t clusterIndex; |
| if (!emberAfEndpointIndexIsEnabled(i)) |
| { |
| continue; |
| } |
| for (clusterIndex = 0; clusterIndex < endpointType->clusterCount; clusterIndex++) |
| { |
| EmberAfCluster * cluster = &(endpointType->cluster[clusterIndex]); |
| if (emAfMatchCluster(cluster, attRecord)) |
| { // Got the cluster |
| uint16_t attrIndex; |
| for (attrIndex = 0; attrIndex < cluster->attributeCount; attrIndex++) |
| { |
| EmberAfAttributeMetadata * am = &(cluster->attributes[attrIndex]); |
| if (emAfMatchAttribute(cluster, am, attRecord)) |
| { // Got the attribute |
| // If passed metadata location is not null, populate |
| if (metadata != NULL) |
| { |
| *metadata = am; |
| } |
| |
| { |
| uint8_t * attributeLocation = |
| (am->mask & ATTRIBUTE_MASK_SINGLETON ? singletonAttributeLocation(am) |
| : attributeData + attributeOffsetIndex); |
| uint8_t *src, *dst; |
| if (write) |
| { |
| src = buffer; |
| dst = attributeLocation; |
| if (!emberAfAttributeWriteAccessCallback(attRecord->endpoint, attRecord->clusterId, |
| emAfGetManufacturerCodeForAttribute(cluster, am), |
| am->attributeId)) |
| { |
| return EMBER_ZCL_STATUS_NOT_AUTHORIZED; |
| } |
| } |
| else |
| { |
| if (buffer == NULL) |
| { |
| return EMBER_ZCL_STATUS_SUCCESS; |
| } |
| |
| src = attributeLocation; |
| dst = buffer; |
| if (!emberAfAttributeReadAccessCallback(attRecord->endpoint, attRecord->clusterId, |
| emAfGetManufacturerCodeForAttribute(cluster, am), |
| am->attributeId)) |
| { |
| return EMBER_ZCL_STATUS_NOT_AUTHORIZED; |
| } |
| } |
| |
| return (am->mask & ATTRIBUTE_MASK_EXTERNAL_STORAGE |
| ? (write) ? emberAfExternalAttributeWriteCallback( |
| attRecord->endpoint, attRecord->clusterId, am, |
| emAfGetManufacturerCodeForAttribute(cluster, am), buffer, index) |
| : emberAfExternalAttributeReadCallback( |
| attRecord->endpoint, attRecord->clusterId, am, |
| emAfGetManufacturerCodeForAttribute(cluster, am), buffer, |
| emberAfAttributeSize(am), index) |
| : typeSensitiveMemCopy(attRecord->clusterId, dst, src, am, write, readLength, index)); |
| } |
| } |
| else |
| { // Not the attribute we are looking for |
| // Increase the index if attribute is not externally stored |
| if (!(am->mask & ATTRIBUTE_MASK_EXTERNAL_STORAGE) && !(am->mask & ATTRIBUTE_MASK_SINGLETON)) |
| { |
| attributeOffsetIndex = static_cast<uint16_t>(attributeOffsetIndex + emberAfAttributeSize(am)); |
| } |
| } |
| } |
| } |
| else |
| { // Not the cluster we are looking for |
| attributeOffsetIndex = static_cast<uint16_t>(attributeOffsetIndex + cluster->clusterSize); |
| } |
| } |
| } |
| else |
| { // Not the endpoint we are looking for |
| // Dynamic endpoints are external and don't factor into storage size |
| if (i < emberAfFixedEndpointCount()) |
| { |
| attributeOffsetIndex = static_cast<uint16_t>(attributeOffsetIndex + emAfEndpoints[i].endpointType->endpointSize); |
| } |
| } |
| } |
| return EMBER_ZCL_STATUS_UNSUPPORTED_ATTRIBUTE; // Sorry, attribute was not found. |
| } |
| |
| // Check if a cluster is implemented or not. If yes, the cluster is returned. |
| // If the cluster is not manufacturerSpecific [ClusterId < FC00] then |
| // manufacturerCode argument is ignored otherwise checked. |
| // |
| // mask = 0 -> find either client or server |
| // mask = CLUSTER_MASK_CLIENT -> find client |
| // mask = CLUSTER_MASK_SERVER -> find server |
| EmberAfCluster * emberAfFindClusterInTypeWithMfgCode(EmberAfEndpointType * endpointType, ClusterId clusterId, |
| EmberAfClusterMask mask, uint16_t manufacturerCode) |
| { |
| uint8_t i; |
| for (i = 0; i < endpointType->clusterCount; i++) |
| { |
| EmberAfCluster * cluster = &(endpointType->cluster[i]); |
| if (cluster->clusterId == clusterId && |
| (mask == 0 || (mask == CLUSTER_MASK_CLIENT && emberAfClusterIsClient(cluster)) || |
| (mask == CLUSTER_MASK_SERVER && emberAfClusterIsServer(cluster))) && |
| (!emberAfClusterIsManufacturerSpecific(cluster) || |
| (emAfGetManufacturerCodeForCluster(cluster) == manufacturerCode) |
| // For compatibility with older stack api, we ignore manufacturer code here |
| // if the manufacturerCode == EMBER_AF_NULL_MANUFACTURER_CODE |
| || manufacturerCode == EMBER_AF_NULL_MANUFACTURER_CODE)) |
| { |
| return cluster; |
| } |
| } |
| return NULL; |
| } |
| |
| // This functions wraps emberAfFindClusterInTypeWithMfgCode with |
| // a manufacturerCode of EMBER_AF_NULL_MANUFACTURER_CODE. |
| EmberAfCluster * emberAfFindClusterInType(EmberAfEndpointType * endpointType, ClusterId clusterId, EmberAfClusterMask mask) |
| { |
| return emberAfFindClusterInTypeWithMfgCode(endpointType, clusterId, mask, EMBER_AF_NULL_MANUFACTURER_CODE); |
| } |
| |
| // This code is used during unit tests for clusters that do not involve manufacturer code. |
| // Should this code be used in other locations, manufacturerCode should be added. |
| uint8_t emberAfClusterIndex(EndpointId endpoint, ClusterId clusterId, EmberAfClusterMask mask) |
| { |
| uint8_t ep; |
| uint8_t index = 0xFF; |
| for (ep = 0; ep < emberAfEndpointCount(); ep++) |
| { |
| EmberAfEndpointType * endpointType = emAfEndpoints[ep].endpointType; |
| if (emberAfFindClusterInTypeWithMfgCode(endpointType, clusterId, mask, EMBER_AF_NULL_MANUFACTURER_CODE) != NULL) |
| { |
| index++; |
| if (emAfEndpoints[ep].endpoint == endpoint) |
| { |
| return index; |
| } |
| } |
| } |
| return 0xFF; |
| } |
| |
| // Returns true uf endpoint contains passed cluster |
| bool emberAfContainsClusterWithMfgCode(EndpointId endpoint, ClusterId clusterId, uint16_t manufacturerCode) |
| { |
| return (emberAfFindClusterWithMfgCode(endpoint, clusterId, 0, manufacturerCode) != NULL); |
| } |
| |
| // Returns true if endpoint contains passed cluster as a server |
| bool emberAfContainsServerWithMfgCode(EndpointId endpoint, ClusterId clusterId, uint16_t manufacturerCode) |
| { |
| return (emberAfFindClusterWithMfgCode(endpoint, clusterId, CLUSTER_MASK_SERVER, manufacturerCode) != NULL); |
| } |
| |
| // Returns true if endpoint contains passed cluster as a client |
| bool emberAfContainsClientWithMfgCode(EndpointId endpoint, ClusterId clusterId, uint16_t manufacturerCode) |
| { |
| return (emberAfFindClusterWithMfgCode(endpoint, clusterId, CLUSTER_MASK_CLIENT, manufacturerCode) != NULL); |
| } |
| |
| // Wraps emberAfContainsClusterWithMfgCode with EMBER_AF_NULL_MANUFACTURER_CODE |
| // This will find the first cluster that has the clusterId given, regardless of mfgCode. |
| bool emberAfContainsCluster(EndpointId endpoint, ClusterId clusterId) |
| { |
| return (emberAfFindClusterWithMfgCode(endpoint, clusterId, 0, EMBER_AF_NULL_MANUFACTURER_CODE) != NULL); |
| } |
| |
| // Wraps emberAfContainsServerWithMfgCode with EMBER_AF_NULL_MANUFACTURER_CODE |
| // This will find the first server that has the clusterId given, regardless of mfgCode. |
| bool emberAfContainsServer(EndpointId endpoint, ClusterId clusterId) |
| { |
| return (emberAfFindClusterWithMfgCode(endpoint, clusterId, CLUSTER_MASK_SERVER, EMBER_AF_NULL_MANUFACTURER_CODE) != NULL); |
| } |
| |
| // Wraps emberAfContainsClientWithMfgCode with EMBER_AF_NULL_MANUFACTURER_CODE |
| // This will find the first client that has the clusterId given, regardless of mfgCode. |
| bool emberAfContainsClient(EndpointId endpoint, ClusterId clusterId) |
| { |
| return (emberAfFindClusterWithMfgCode(endpoint, clusterId, CLUSTER_MASK_CLIENT, EMBER_AF_NULL_MANUFACTURER_CODE) != NULL); |
| } |
| |
| // Finds the cluster that matches endpoint, clusterId, direction, and manufacturerCode. |
| EmberAfCluster * emberAfFindClusterWithMfgCode(EndpointId endpoint, ClusterId clusterId, EmberAfClusterMask mask, |
| uint16_t manufacturerCode) |
| { |
| uint16_t ep = emberAfIndexFromEndpoint(endpoint); |
| if (ep == 0xFFFF) |
| { |
| return NULL; |
| } |
| else |
| { |
| return emberAfFindClusterInTypeWithMfgCode(emAfEndpoints[ep].endpointType, clusterId, mask, manufacturerCode); |
| } |
| } |
| |
| // This function wraps emberAfFindClusterWithMfgCode with EMBER_AF_NULL_MANUFACTURER_CODE |
| // and will ignore the manufacturerCode when trying to find clusters. |
| // This will return the first cluster in the cluster table that matches the parameters given. |
| EmberAfCluster * emberAfFindCluster(EndpointId endpoint, ClusterId clusterId, EmberAfClusterMask mask) |
| { |
| return emberAfFindClusterWithMfgCode(endpoint, clusterId, mask, EMBER_AF_NULL_MANUFACTURER_CODE); |
| } |
| |
| // Returns cluster within the endpoint; Does not ignore disabled endpoints |
| EmberAfCluster * emberAfFindClusterIncludingDisabledEndpointsWithMfgCode(EndpointId endpoint, ClusterId clusterId, |
| EmberAfClusterMask mask, uint16_t manufacturerCode) |
| { |
| uint16_t ep = emberAfIndexFromEndpointIncludingDisabledEndpoints(endpoint); |
| if (ep < MAX_ENDPOINT_COUNT) |
| { |
| return emberAfFindClusterInTypeWithMfgCode(emAfEndpoints[ep].endpointType, clusterId, mask, manufacturerCode); |
| } |
| return NULL; |
| } |
| |
| // Returns cluster within the endpoint; Does not ignore disabled endpoints |
| // This will ignore manufacturerCode. |
| EmberAfCluster * emberAfFindClusterIncludingDisabledEndpoints(EndpointId endpoint, ClusterId clusterId, EmberAfClusterMask mask) |
| { |
| return emberAfFindClusterIncludingDisabledEndpointsWithMfgCode(endpoint, clusterId, mask, EMBER_AF_NULL_MANUFACTURER_CODE); |
| } |
| |
| // Server wrapper for findClusterEndpointIndex. |
| static uint16_t emberAfFindClusterServerEndpointIndexWithMfgCode(EndpointId endpoint, ClusterId clusterId, |
| uint16_t manufacturerCode) |
| { |
| return findClusterEndpointIndex(endpoint, clusterId, CLUSTER_MASK_SERVER, manufacturerCode); |
| } |
| |
| // Client wrapper for findClusterEndpointIndex. |
| uint16_t emberAfFindClusterClientEndpointIndexWithMfgCode(EndpointId endpoint, ClusterId clusterId, uint16_t manufacturerCode) |
| { |
| return findClusterEndpointIndex(endpoint, clusterId, CLUSTER_MASK_CLIENT, manufacturerCode); |
| } |
| |
| // Server wrapper for findClusterEndpointIndex |
| // This will ignore manufacturerCode, and return the index for the first server that matches on clusterId |
| uint16_t emberAfFindClusterServerEndpointIndex(EndpointId endpoint, ClusterId clusterId) |
| { |
| return emberAfFindClusterServerEndpointIndexWithMfgCode(endpoint, clusterId, EMBER_AF_NULL_MANUFACTURER_CODE); |
| } |
| |
| // Client wrapper for findClusterEndpointIndex |
| // This will ignore manufacturerCode, and return the index for the first client that matches on clusterId |
| uint16_t emberAfFindClusterClientEndpointIndex(EndpointId endpoint, ClusterId clusterId) |
| { |
| return emberAfFindClusterClientEndpointIndexWithMfgCode(endpoint, clusterId, EMBER_AF_NULL_MANUFACTURER_CODE); |
| } |
| |
| // Returns the endpoint index within a given cluster |
| static uint16_t findClusterEndpointIndex(EndpointId endpoint, ClusterId clusterId, uint8_t mask, uint16_t manufacturerCode) |
| { |
| uint16_t i, epi = 0; |
| |
| if (emberAfFindClusterWithMfgCode(endpoint, clusterId, mask, manufacturerCode) == NULL) |
| { |
| return 0xFFFF; |
| } |
| |
| for (i = 0; i < emberAfEndpointCount(); i++) |
| { |
| if (emAfEndpoints[i].endpoint == endpoint) |
| { |
| break; |
| } |
| epi = static_cast<uint16_t>(epi + |
| ((emberAfFindClusterIncludingDisabledEndpointsWithMfgCode(emAfEndpoints[i].endpoint, clusterId, |
| mask, manufacturerCode) != NULL) |
| ? 1 |
| : 0)); |
| } |
| |
| return epi; |
| } |
| |
| static uint16_t findIndexFromEndpoint(EndpointId endpoint, bool ignoreDisabledEndpoints) |
| { |
| uint16_t epi; |
| for (epi = 0; epi < emberAfEndpointCount(); epi++) |
| { |
| if (emAfEndpoints[epi].endpoint == endpoint && |
| (!ignoreDisabledEndpoints || emAfEndpoints[epi].bitmask & EMBER_AF_ENDPOINT_ENABLED)) |
| { |
| return epi; |
| } |
| } |
| return 0xFFFF; |
| } |
| |
| bool emberAfEndpointIsEnabled(EndpointId endpoint) |
| { |
| uint16_t index = findIndexFromEndpoint(endpoint, |
| false); // ignore disabled endpoints? |
| |
| EMBER_TEST_ASSERT(0xFFFF != index); |
| |
| if (0xFFFF == index) |
| { |
| return false; |
| } |
| |
| return emberAfEndpointIndexIsEnabled(index); |
| } |
| |
| bool emberAfEndpointEnableDisable(EndpointId endpoint, bool enable) |
| { |
| uint16_t index = findIndexFromEndpoint(endpoint, |
| false); // ignore disabled endpoints? |
| bool currentlyEnabled; |
| |
| if (0xFFFF == index) |
| { |
| return false; |
| } |
| |
| currentlyEnabled = emAfEndpoints[index].bitmask & EMBER_AF_ENDPOINT_ENABLED; |
| |
| if (enable) |
| { |
| emAfEndpoints[index].bitmask |= EMBER_AF_ENDPOINT_ENABLED; |
| } |
| else |
| { |
| emAfEndpoints[index].bitmask &= EMBER_AF_ENDPOINT_DISABLED; |
| } |
| |
| #if defined(EZSP_HOST) |
| ezspSetEndpointFlags(endpoint, (enable ? EZSP_ENDPOINT_ENABLED : EZSP_ENDPOINT_DISABLED)); |
| #endif |
| |
| if (currentlyEnabled != enable) |
| { |
| if (enable) |
| { |
| initializeEndpoint(&(emAfEndpoints[index])); |
| } |
| else |
| { |
| uint8_t i; |
| for (i = 0; i < emAfEndpoints[index].endpointType->clusterCount; i++) |
| { |
| EmberAfCluster * cluster = &((emAfEndpoints[index].endpointType->cluster)[i]); |
| // emberAfCorePrintln("Disabling cluster tick for ep:%d, cluster:0x%2X, %p", |
| // endpoint, |
| // cluster->clusterId, |
| // ((cluster->mask & CLUSTER_MASK_CLIENT) |
| // ? "client" |
| // : "server")); |
| // emberAfCoreFlush(); |
| emberAfDeactivateClusterTick( |
| endpoint, cluster->clusterId, |
| (cluster->mask & CLUSTER_MASK_CLIENT ? EMBER_AF_CLIENT_CLUSTER_TICK : EMBER_AF_SERVER_CLUSTER_TICK)); |
| } |
| |
| // Clear out any attribute access overrides registered for this |
| // endpoint. |
| app::AttributeAccessInterface * prev = nullptr; |
| app::AttributeAccessInterface * cur = gAttributeAccessOverrides; |
| while (cur) |
| { |
| app::AttributeAccessInterface * next = cur->GetNext(); |
| if (cur->MatchesExactly(endpoint)) |
| { |
| // Remove it from the list |
| if (prev) |
| { |
| prev->SetNext(next); |
| } |
| else |
| { |
| gAttributeAccessOverrides = next; |
| } |
| |
| // Do not change prev in this case. |
| } |
| else |
| { |
| prev = cur; |
| } |
| cur = next; |
| } |
| } |
| |
| #ifdef ZCL_USING_DESCRIPTOR_CLUSTER_SERVER |
| // Rebuild descriptor attributes on all endpoints |
| emberAfPluginDescriptorServerInitCallback(); |
| #endif |
| } |
| |
| return true; |
| } |
| |
| // Returns the index of a given endpoint. Does not consider disabled endpoints. |
| uint16_t emberAfIndexFromEndpoint(EndpointId endpoint) |
| { |
| return findIndexFromEndpoint(endpoint, |
| true); // ignore disabled endpoints? |
| } |
| |
| // Returns the index of a given endpoint. Considers disabled endpoints. |
| uint16_t emberAfIndexFromEndpointIncludingDisabledEndpoints(EndpointId endpoint) |
| { |
| return findIndexFromEndpoint(endpoint, |
| false); // ignore disabled endpoints? |
| } |
| |
| EndpointId emberAfEndpointFromIndex(uint16_t index) |
| { |
| return emAfEndpoints[index].endpoint; |
| } |
| |
| // If server == true, returns the number of server clusters, |
| // otherwise number of client clusters on this endpoint |
| uint8_t emberAfClusterCount(EndpointId endpoint, bool server) |
| { |
| uint16_t index = emberAfIndexFromEndpoint(endpoint); |
| uint8_t i, c = 0; |
| EmberAfDefinedEndpoint * de; |
| EmberAfCluster * cluster; |
| |
| if (index == 0xFFFF) |
| { |
| return 0; |
| } |
| de = &(emAfEndpoints[index]); |
| if (de->endpointType == NULL) |
| { |
| return 0; |
| } |
| for (i = 0; i < de->endpointType->clusterCount; i++) |
| { |
| cluster = &(de->endpointType->cluster[i]); |
| if (server && emberAfClusterIsServer(cluster)) |
| { |
| c++; |
| } |
| if ((!server) && emberAfClusterIsClient(cluster)) |
| { |
| c++; |
| } |
| } |
| return c; |
| } |
| |
| uint8_t emberAfGetClusterCountForEndpoint(EndpointId endpoint) |
| { |
| uint16_t index = emberAfIndexFromEndpoint(endpoint); |
| if (index == 0xFFFF) |
| { |
| return 0; |
| } |
| return emAfEndpoints[index].endpointType->clusterCount; |
| } |
| |
| // Note the difference in implementation from emberAfGetNthCluster(). |
| // emberAfGetClusterByIndex() retrieves the cluster by index regardless of server/client |
| // and those indexes may be DIFFERENT than the indexes returned from |
| // emberAfGetNthCluster(). In other words: |
| // |
| // - Use emberAfGetClustersFromEndpoint() with emberAfGetNthCluster() |
| // - Use emberAfGetClusterCountForEndpoint() with emberAfGetClusterByIndex() |
| // |
| // Don't mix them. |
| EmberAfCluster * emberAfGetClusterByIndex(EndpointId endpoint, uint8_t clusterIndex) |
| { |
| uint16_t endpointIndex = emberAfIndexFromEndpoint(endpoint); |
| EmberAfDefinedEndpoint * definedEndpoint; |
| |
| if (endpointIndex == 0xFFFF) |
| { |
| return NULL; |
| } |
| definedEndpoint = &(emAfEndpoints[endpointIndex]); |
| |
| if (clusterIndex >= definedEndpoint->endpointType->clusterCount) |
| { |
| return NULL; |
| } |
| return &(definedEndpoint->endpointType->cluster[clusterIndex]); |
| } |
| |
| uint16_t emberAfGetDeviceIdForEndpoint(EndpointId endpoint) |
| { |
| uint16_t endpointIndex = emberAfIndexFromEndpoint(endpoint); |
| if (endpointIndex == 0xFFFF) |
| { |
| return 0xFFFF; |
| } |
| return emAfEndpoints[endpointIndex].deviceId; |
| } |
| |
| // Returns the cluster of Nth server or client cluster, |
| // depending on server toggle. |
| EmberAfCluster * emberAfGetNthCluster(EndpointId endpoint, uint8_t n, bool server) |
| { |
| uint16_t index = emberAfIndexFromEndpoint(endpoint); |
| EmberAfDefinedEndpoint * de; |
| uint8_t i, c = 0; |
| EmberAfCluster * cluster; |
| |
| if (index == 0xFFFF) |
| { |
| return NULL; |
| } |
| de = &(emAfEndpoints[index]); |
| |
| for (i = 0; i < de->endpointType->clusterCount; i++) |
| { |
| cluster = &(de->endpointType->cluster[i]); |
| |
| if ((server && emberAfClusterIsServer(cluster)) || ((!server) && emberAfClusterIsClient(cluster))) |
| { |
| if (c == n) |
| { |
| return cluster; |
| } |
| c++; |
| } |
| } |
| return NULL; |
| } |
| |
| // Returns number of clusters put into the passed cluster list |
| // for the given endpoint and client/server polarity |
| uint8_t emberAfGetClustersFromEndpoint(EndpointId endpoint, ClusterId * clusterList, uint8_t listLen, bool server) |
| { |
| uint8_t clusterCount = emberAfClusterCount(endpoint, server); |
| uint8_t i; |
| EmberAfCluster * cluster; |
| if (clusterCount > listLen) |
| { |
| clusterCount = listLen; |
| } |
| for (i = 0; i < clusterCount; i++) |
| { |
| cluster = emberAfGetNthCluster(endpoint, i, server); |
| clusterList[i] = (cluster == NULL ? 0xFFFF : cluster->clusterId); |
| } |
| return clusterCount; |
| } |
| |
| void emberAfInitializeAttributes(EndpointId endpoint) |
| { |
| emAfLoadAttributeDefaults(endpoint, false); |
| } |
| |
| void emberAfResetAttributes(EndpointId endpoint) |
| { |
| emAfLoadAttributeDefaults(endpoint, true); |
| } |
| |
| void emAfLoadAttributeDefaults(EndpointId endpoint, bool writeTokens) |
| { |
| uint16_t ep; |
| uint8_t clusterI, curNetwork = 0 /* emberGetCurrentNetwork() */; |
| uint16_t attr; |
| uint8_t * ptr; |
| uint16_t epCount = emberAfEndpointCount(); |
| |
| for (ep = 0; ep < epCount; ep++) |
| { |
| EmberAfDefinedEndpoint * de; |
| if (endpoint != EMBER_BROADCAST_ENDPOINT) |
| { |
| ep = emberAfIndexFromEndpoint(endpoint); |
| if (ep == 0xFFFF) |
| { |
| return; |
| } |
| } |
| de = &(emAfEndpoints[ep]); |
| |
| // Ensure that the endpoint is on the current network |
| if (endpoint == EMBER_BROADCAST_ENDPOINT && de->networkIndex != curNetwork) |
| { |
| continue; |
| } |
| for (clusterI = 0; clusterI < de->endpointType->clusterCount; clusterI++) |
| { |
| EmberAfCluster * cluster = &(de->endpointType->cluster[clusterI]); |
| |
| // when the attributeCount is high, the loop takes too long to run and a |
| // watchdog kicks in causing a reset. As a workaround, we'll |
| // conditionally manually reset the watchdog. 300 sounds like a good |
| // magic number for now. |
| if (cluster->attributeCount > 300) |
| { |
| // halResetWatchdog(); |
| } |
| for (attr = 0; attr < cluster->attributeCount; attr++) |
| { |
| EmberAfAttributeMetadata * am = &(cluster->attributes[attr]); |
| if (!(am->mask & ATTRIBUTE_MASK_EXTERNAL_STORAGE)) |
| { |
| EmberAfAttributeSearchRecord record; |
| record.endpoint = de->endpoint; |
| record.clusterId = cluster->clusterId; |
| record.clusterMask = (emberAfAttributeIsClient(am) ? CLUSTER_MASK_CLIENT : CLUSTER_MASK_SERVER); |
| record.attributeId = am->attributeId; |
| record.manufacturerCode = emAfGetManufacturerCodeForAttribute(cluster, am); |
| if ((am->mask & ATTRIBUTE_MASK_MIN_MAX) != 0U) |
| { |
| if (emberAfAttributeSize(am) <= 2) |
| { |
| ptr = (uint8_t *) &(am->defaultValue.ptrToMinMaxValue->defaultValue.defaultValue); |
| } |
| else |
| { |
| ptr = (uint8_t *) am->defaultValue.ptrToMinMaxValue->defaultValue.ptrToDefaultValue; |
| } |
| } |
| else |
| { |
| if (emberAfAttributeSize(am) <= 2) |
| { |
| ptr = (uint8_t *) &(am->defaultValue.defaultValue); |
| } |
| else |
| { |
| ptr = (uint8_t *) am->defaultValue.ptrToDefaultValue; |
| } |
| } |
| // At this point, ptr either points to a default value, or is NULL, in which case |
| // it should be treated as if it is pointing to an array of all zeroes. |
| |
| #if (BIGENDIAN_CPU) |
| // The default value for one- and two-byte attributes is stored in an |
| // uint16_t. On big-endian platforms, a pointer to the default value of |
| // a one-byte attribute will point to the wrong byte. So, for those |
| // cases, nudge the pointer forward so it points to the correct byte. |
| if (emberAfAttributeSize(am) == 1 && ptr != NULL) |
| { |
| *ptr++; |
| } |
| #endif // BIGENDIAN |
| emAfReadOrWriteAttribute(&record, |
| NULL, // metadata - unused |
| ptr, |
| 0, // buffer size - unused |
| true); // write? |
| if (writeTokens) |
| { |
| emAfSaveAttributeToToken(ptr, de->endpoint, record.clusterId, am); |
| } |
| } |
| } |
| } |
| if (endpoint != EMBER_BROADCAST_ENDPOINT) |
| { |
| break; |
| } |
| } |
| |
| if (!writeTokens) |
| { |
| emAfLoadAttributesFromTokens(endpoint); |
| } |
| } |
| |
| void emAfLoadAttributesFromTokens(EndpointId endpoint) |
| { |
| // On EZSP host we currently do not support this. We need to come up with some |
| // callbacks. |
| #ifndef EZSP_HOST |
| GENERATED_TOKEN_LOADER(endpoint); |
| #endif // EZSP_HOST |
| } |
| |
| // 'data' argument may be null, since we changed the ptrToDefaultValue |
| // to be null instead of pointing to all zeroes. |
| // This function has to be able to deal with that. |
| void emAfSaveAttributeToToken(uint8_t * data, EndpointId endpoint, ClusterId clusterId, EmberAfAttributeMetadata * metadata) |
| { |
| // Get out of here if this attribute doesn't have a token. |
| if (!emberAfAttributeIsTokenized(metadata)) |
| { |
| return; |
| } |
| |
| // On EZSP host we currently do not support this. We need to come up with some |
| // callbacks. |
| #ifndef EZSP_HOST |
| GENERATED_TOKEN_SAVER; |
| #endif // EZSP_HOST |
| } |
| |
| // This function returns the actual function point from the array, |
| // iterating over the function bits. |
| EmberAfGenericClusterFunction emberAfFindClusterFunction(EmberAfCluster * cluster, EmberAfClusterMask functionMask) |
| { |
| EmberAfClusterMask mask = 0x01; |
| uint8_t functionIndex = 0; |
| |
| if ((cluster->mask & functionMask) == 0) |
| { |
| return NULL; |
| } |
| |
| while (mask < functionMask) |
| { |
| if ((cluster->mask & mask) != 0) |
| { |
| functionIndex++; |
| } |
| mask = static_cast<EmberAfClusterMask>(mask << 1); |
| } |
| return cluster->functions[functionIndex]; |
| } |
| |
| #ifdef EMBER_AF_SUPPORT_COMMAND_DISCOVERY |
| |
| uint16_t emAfGetManufacturerCodeForCommand(EmberAfCommandMetadata * command) |
| { |
| return getManufacturerCode((EmberAfManufacturerCodeEntry *) commandManufacturerCodes, commandManufacturerCodeCount, |
| static_cast<uint16_t>(command - generatedCommands)); |
| } |
| |
| /** |
| * This function populates command IDs into a given buffer. |
| * |
| * It returns true if commands are complete, meaning there are NO MORE |
| * commands that would be returned after the last command. |
| * It returns false, if there were more commands, but were not populated |
| * because of maxIdCount limitation. |
| */ |
| bool emberAfExtractCommandIds(bool outgoing, EmberAfClusterCommand * cmd, ClusterId clusterId, uint8_t * buffer, |
| uint16_t bufferLength, uint16_t * bufferIndex, uint8_t startId, uint8_t maxIdCount) |
| { |
| uint16_t i, count = 0; |
| bool returnValue = true; |
| uint8_t cmdDirMask = 0; |
| |
| // determine the appropriate mask to match the request |
| // discover commands generated, client is asking server what commands do you generate? |
| if (outgoing && (cmd->direction == ZCL_DIRECTION_CLIENT_TO_SERVER)) |
| { |
| cmdDirMask = COMMAND_MASK_OUTGOING_SERVER; |
| // discover commands generated server is asking client what commands do you generate? |
| } |
| else if (outgoing && (cmd->direction == ZCL_DIRECTION_SERVER_TO_CLIENT)) |
| { |
| cmdDirMask = COMMAND_MASK_OUTGOING_CLIENT; |
| // discover commands received client is asking server what commands do you receive? |
| } |
| else if (!outgoing && (cmd->direction == ZCL_DIRECTION_CLIENT_TO_SERVER)) |
| { |
| cmdDirMask = COMMAND_MASK_INCOMING_SERVER; |
| // discover commands received server is asking client what commands do you receive? |
| } |
| else |
| { |
| cmdDirMask = COMMAND_MASK_INCOMING_CLIENT; |
| } |
| |
| for (i = 0; i < EMBER_AF_GENERATED_COMMAND_COUNT; i++) |
| { |
| if (generatedCommands[i].clusterId != clusterId) |
| { |
| continue; |
| } |
| |
| if ((generatedCommands[i].mask & cmdDirMask) == 0) |
| { |
| continue; |
| } |
| |
| // Only start from the passed command id |
| if (generatedCommands[i].commandId < startId) |
| { |
| continue; |
| } |
| |
| // According to spec: if cmd->mfgSpecific is set, then we ONLY return the |
| // mfg specific commands. If it's not, then we ONLY return non-mfg specific. |
| if (generatedCommands[i].mask & COMMAND_MASK_MANUFACTURER_SPECIFIC) |
| { |
| // Command is Mfg specific |
| if (!cmd->mfgSpecific) |
| { |
| continue; // ignore if asking for not mfg specific |
| } |
| if (cmd->mfgCode != emAfGetManufacturerCodeForCommand((EmberAfCommandMetadata *) &(generatedCommands[i]))) |
| { |
| continue; // Ignore if mfg code doesn't match the commands |
| } |
| } |
| else |
| { |
| // Command is not mfg specific. |
| if (cmd->mfgSpecific) |
| { |
| continue; // Ignore if asking for mfg specific |
| } |
| } |
| |
| // The one we are about to put in, is beyond the maxIdCount, |
| // so instead of populating it in, we set the return flag to |
| // false and get out of here. |
| if (maxIdCount == count || count >= bufferLength) |
| { |
| returnValue = false; |
| break; |
| } |
| buffer[count] = generatedCommands[i].commandId; |
| (*bufferIndex)++; |
| count++; |
| } |
| return returnValue; |
| } |
| #else |
| // We just need an empty stub if we don't support it |
| bool emberAfExtractCommandIds(bool outgoing, EmberAfClusterCommand * cmd, ClusterId clusterId, uint8_t * buffer, |
| uint16_t bufferLength, uint16_t * bufferIndex, uint8_t startId, uint8_t maxIdCount) |
| { |
| return true; |
| } |
| #endif |
| |
| bool registerAttributeAccessOverride(app::AttributeAccessInterface * attrOverride) |
| { |
| for (auto * cur = gAttributeAccessOverrides; cur; cur = cur->GetNext()) |
| { |
| if (cur->Matches(*attrOverride)) |
| { |
| ChipLogError(Zcl, "Duplicate attribute override registration failed"); |
| return false; |
| } |
| } |
| attrOverride->SetNext(gAttributeAccessOverrides); |
| gAttributeAccessOverrides = attrOverride; |
| return true; |
| } |
| |
| app::AttributeAccessInterface * findAttributeAccessOverride(EndpointId endpointId, ClusterId clusterId) |
| { |
| for (app::AttributeAccessInterface * cur = gAttributeAccessOverrides; cur; cur = cur->GetNext()) |
| { |
| if (cur->Matches(endpointId, clusterId)) |
| { |
| return cur; |
| } |
| } |
| |
| return nullptr; |
| } |