3 * Copyright (c) 2020 Project CHIP Authors
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
20 * Copyright (c) 2020 Silicon Labs
22 * Licensed under the Apache License, Version 2.0 (the "License");
23 * you may not use this file except in compliance with the License.
24 * You may obtain a copy of the License at
26 * http://www.apache.org/licenses/LICENSE-2.0
28 * Unless required by applicable law or agreed to in writing, software
29 * distributed under the License is distributed on an "AS IS" BASIS,
30 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
31 * See the License for the specific language governing permissions and
32 * limitations under the License.
34 /***************************************************************************/
37 * @brief Contains the per-endpoint configuration of
39 *******************************************************************************
40 ******************************************************************************/
42 #include "attribute-storage.h"
44 #include "app/util/common.h"
46 #include "gen/attribute-type.h"
47 #include "gen/callback.h"
51 //------------------------------------------------------------------------------
53 // This is not declared CONST in order to handle dynamic endpoint information
54 // retrieved from tokens.
55 EmberAfDefinedEndpoint emAfEndpoints[MAX_ENDPOINT_COUNT];
57 #if (ATTRIBUTE_MAX_SIZE == 0)
58 #define ACTUAL_ATTRIBUTE_SIZE 1
60 #define ACTUAL_ATTRIBUTE_SIZE ATTRIBUTE_MAX_SIZE
63 uint8_t attributeData[ACTUAL_ATTRIBUTE_SIZE];
65 #if (!defined(ATTRIBUTE_SINGLETONS_SIZE)) || (ATTRIBUTE_SINGLETONS_SIZE == 0)
66 #define ACTUAL_SINGLETONS_SIZE 1
68 #define ACTUAL_SINGLETONS_SIZE ATTRIBUTE_SINGLETONS_SIZE
70 uint8_t singletonAttributeData[ACTUAL_SINGLETONS_SIZE];
72 uint8_t emberEndpointCount = 0;
74 // If we have attributes that are more than 2 bytes, then
75 // we need this data block for the defaults
76 #if (defined(GENERATED_DEFAULTS) && GENERATED_DEFAULTS_COUNT)
77 const uint8_t generatedDefaults[] = GENERATED_DEFAULTS;
78 #endif // GENERATED_DEFAULTS
80 #if (defined(GENERATED_MIN_MAX_DEFAULTS) && GENERATED_MIN_MAX_DEFAULT_COUNT)
81 const EmberAfAttributeMinMaxValue minMaxDefaults[] = GENERATED_MIN_MAX_DEFAULTS;
82 #endif // GENERATED_MIN_MAX_DEFAULTS
84 #ifdef GENERATED_FUNCTION_ARRAYS
85 GENERATED_FUNCTION_ARRAYS
88 #ifdef EMBER_AF_SUPPORT_COMMAND_DISCOVERY
89 const EmberAfCommandMetadata generatedCommands[] = GENERATED_COMMANDS;
90 const EmberAfManufacturerCodeEntry commandManufacturerCodes[] = GENERATED_COMMAND_MANUFACTURER_CODES;
91 const uint16_t commandManufacturerCodeCount = GENERATED_COMMAND_MANUFACTURER_CODE_COUNT;
94 const EmberAfAttributeMetadata generatedAttributes[] = GENERATED_ATTRIBUTES;
95 const EmberAfCluster generatedClusters[] = GENERATED_CLUSTERS;
96 const EmberAfEndpointType generatedEmberAfEndpointTypes[] = GENERATED_ENDPOINT_TYPES;
98 const EmberAfManufacturerCodeEntry clusterManufacturerCodes[] = GENERATED_CLUSTER_MANUFACTURER_CODES;
99 const uint16_t clusterManufacturerCodeCount = GENERATED_CLUSTER_MANUFACTURER_CODE_COUNT;
100 const EmberAfManufacturerCodeEntry attributeManufacturerCodes[] = GENERATED_ATTRIBUTE_MANUFACTURER_CODES;
101 const uint16_t attributeManufacturerCodeCount = GENERATED_ATTRIBUTE_MANUFACTURER_CODE_COUNT;
103 #if !defined(EMBER_SCRIPTED_TEST)
104 #define endpointNumber(x) fixedEndpoints[x]
105 #define endpointDeviceId(x) fixedDeviceIds[x]
106 #define endpointDeviceVersion(x) fixedDeviceVersions[x]
107 // Added 'Macro' to silence MISRA warning about conflict with synonymous vars.
108 #define endpointTypeMacro(x) (EmberAfEndpointType *) &(generatedEmberAfEndpointTypes[fixedEmberAfEndpointTypes[x]])
109 #define endpointNetworkIndex(x) fixedNetworks[x]
112 //------------------------------------------------------------------------------
113 // Forward declarations
115 // Returns endpoint index within a given cluster
116 static uint8_t findClusterEndpointIndex(EndpointId endpoint, ClusterId clusterId, uint8_t mask, uint16_t manufacturerCode);
118 //------------------------------------------------------------------------------
120 // Initial configuration
121 void emberAfEndpointConfigure(void)
125 #if !defined(EMBER_SCRIPTED_TEST)
126 uint8_t fixedEndpoints[] = FIXED_ENDPOINT_ARRAY;
127 uint16_t fixedDeviceIds[] = FIXED_DEVICE_IDS;
128 uint8_t fixedDeviceVersions[] = FIXED_DEVICE_VERSIONS;
129 uint8_t fixedEmberAfEndpointTypes[] = FIXED_ENDPOINT_TYPES;
130 uint8_t fixedNetworks[] = FIXED_NETWORKS;
133 emberEndpointCount = FIXED_ENDPOINT_COUNT;
134 for (ep = 0; ep < FIXED_ENDPOINT_COUNT; ep++)
136 emAfEndpoints[ep].endpoint = endpointNumber(ep);
137 emAfEndpoints[ep].deviceId = endpointDeviceId(ep);
138 emAfEndpoints[ep].deviceVersion = endpointDeviceVersion(ep);
139 emAfEndpoints[ep].endpointType = endpointTypeMacro(ep);
140 emAfEndpoints[ep].networkIndex = endpointNetworkIndex(ep);
141 emAfEndpoints[ep].bitmask = EMBER_AF_ENDPOINT_ENABLED;
145 void emberAfSetEndpointCount(uint8_t dynamicEndpointCount)
147 emberEndpointCount = static_cast<uint8_t>(FIXED_ENDPOINT_COUNT + dynamicEndpointCount);
150 uint8_t emberAfFixedEndpointCount(void)
152 return FIXED_ENDPOINT_COUNT;
155 uint8_t emberAfEndpointCount(void)
157 return emberEndpointCount;
160 bool emberAfEndpointIndexIsEnabled(uint8_t index)
162 return (emAfEndpoints[index].bitmask & EMBER_AF_ENDPOINT_ENABLED);
165 // some data types (like strings) are sent OTA in human readable order
166 // (how they are read) instead of little endian as the data types are.
167 bool emberAfIsThisDataTypeAStringType(EmberAfAttributeType dataType)
169 return (dataType == ZCL_OCTET_STRING_ATTRIBUTE_TYPE || dataType == ZCL_CHAR_STRING_ATTRIBUTE_TYPE ||
170 dataType == ZCL_LONG_OCTET_STRING_ATTRIBUTE_TYPE || dataType == ZCL_LONG_CHAR_STRING_ATTRIBUTE_TYPE);
173 bool emberAfIsStringAttributeType(EmberAfAttributeType attributeType)
175 return (attributeType == ZCL_OCTET_STRING_ATTRIBUTE_TYPE || attributeType == ZCL_CHAR_STRING_ATTRIBUTE_TYPE);
178 bool emberAfIsLongStringAttributeType(EmberAfAttributeType attributeType)
180 return (attributeType == ZCL_LONG_OCTET_STRING_ATTRIBUTE_TYPE || attributeType == ZCL_LONG_CHAR_STRING_ATTRIBUTE_TYPE);
183 // This function is used to call the per-cluster default response callback
184 void emberAfClusterDefaultResponseWithMfgCodeCallback(EndpointId endpoint, ClusterId clusterId, uint8_t commandId,
185 EmberAfStatus status, uint8_t clientServerMask, uint16_t manufacturerCode)
187 EmberAfCluster * cluster = emberAfFindClusterWithMfgCode(endpoint, clusterId, clientServerMask, manufacturerCode);
190 EmberAfGenericClusterFunction f = emberAfFindClusterFunction(cluster, CLUSTER_MASK_DEFAULT_RESPONSE_FUNCTION);
193 // emberAfPushEndpointNetworkIndex(endpoint);
194 ((EmberAfDefaultResponseFunction) f)(endpoint, commandId, status);
195 // emberAfPopNetworkIndex();
200 // This function is used to call the per-cluster default response callback, and
201 // wraps the emberAfClusterDefaultResponseWithMfgCodeCallback with a
202 // EMBER_AF_NULL_MANUFACTURER_CODE.
203 void emberAfClusterDefaultResponseCallback(EndpointId endpoint, ClusterId clusterId, CommandId commandId, EmberAfStatus status,
204 uint8_t clientServerMask)
206 emberAfClusterDefaultResponseWithMfgCodeCallback(endpoint, clusterId, commandId, status, clientServerMask,
207 EMBER_AF_NULL_MANUFACTURER_CODE);
210 // This function is used to call the per-cluster message sent callback
211 void emberAfClusterMessageSentWithMfgCodeCallback(EmberOutgoingMessageType type, uint64_t indexOrDestination,
212 EmberApsFrame * apsFrame, uint16_t msgLen, uint8_t * message, EmberStatus status,
215 if (apsFrame != NULL && message != NULL && msgLen != 0)
217 EmberAfCluster * cluster = emberAfFindClusterWithMfgCode(
218 apsFrame->sourceEndpoint, apsFrame->clusterId,
219 (((message[0] & ZCL_FRAME_CONTROL_DIRECTION_MASK) == ZCL_FRAME_CONTROL_SERVER_TO_CLIENT) ? CLUSTER_MASK_SERVER
220 : CLUSTER_MASK_CLIENT),
224 EmberAfGenericClusterFunction f = emberAfFindClusterFunction(cluster, CLUSTER_MASK_MESSAGE_SENT_FUNCTION);
227 // emberAfPushEndpointNetworkIndex(apsFrame->sourceEndpoint);
228 ((EmberAfMessageSentFunction) f)(type, indexOrDestination, apsFrame, msgLen, message, status);
229 // emberAfPopNetworkIndex();
235 // This function is used to call the per-cluster message sent callback, and
236 // wraps the emberAfClusterMessageSentWithMfgCodeCallback with a
237 // EMBER_AF_NULL_MANUFACTURER_CODE.
238 void emberAfClusterMessageSentCallback(EmberOutgoingMessageType type, uint16_t indexOrDestination, EmberApsFrame * apsFrame,
239 uint16_t msgLen, uint8_t * message, EmberStatus status)
241 emberAfClusterMessageSentWithMfgCodeCallback(type, indexOrDestination, apsFrame, msgLen, message, status,
242 EMBER_AF_NULL_MANUFACTURER_CODE);
245 // This function is used to call the per-cluster attribute changed callback
246 void emAfClusterAttributeChangedCallback(EndpointId endpoint, ClusterId clusterId, AttributeId attributeId,
247 uint8_t clientServerMask, uint16_t manufacturerCode)
249 EmberAfCluster * cluster = emberAfFindClusterWithMfgCode(endpoint, clusterId, clientServerMask, manufacturerCode);
252 if (manufacturerCode == EMBER_AF_NULL_MANUFACTURER_CODE)
254 EmberAfGenericClusterFunction f = emberAfFindClusterFunction(cluster, CLUSTER_MASK_ATTRIBUTE_CHANGED_FUNCTION);
257 // emberAfPushEndpointNetworkIndex(endpoint);
258 ((EmberAfClusterAttributeChangedCallback) f)(endpoint, attributeId);
259 // emberAfPopNetworkIndex();
264 EmberAfGenericClusterFunction f =
265 emberAfFindClusterFunction(cluster, CLUSTER_MASK_MANUFACTURER_SPECIFIC_ATTRIBUTE_CHANGED_FUNCTION);
268 // emberAfPushEndpointNetworkIndex(endpoint);
269 ((EmberAfManufacturerSpecificClusterAttributeChangedCallback) f)(endpoint, attributeId, manufacturerCode);
270 // emberAfPopNetworkIndex();
276 // This function is used to call the per-cluster pre-attribute changed callback
277 EmberAfStatus emAfClusterPreAttributeChangedCallback(EndpointId endpoint, ClusterId clusterId, AttributeId attributeId,
278 uint8_t clientServerMask, uint16_t manufacturerCode,
279 EmberAfAttributeType attributeType, uint8_t size, uint8_t * value)
281 EmberAfCluster * cluster = emberAfFindClusterWithMfgCode(endpoint, clusterId, clientServerMask, manufacturerCode);
284 return EMBER_ZCL_STATUS_UNSUPPORTED_ATTRIBUTE;
288 EmberAfStatus status = EMBER_ZCL_STATUS_SUCCESS;
289 if (manufacturerCode == EMBER_AF_NULL_MANUFACTURER_CODE)
291 EmberAfGenericClusterFunction f = emberAfFindClusterFunction(cluster, CLUSTER_MASK_PRE_ATTRIBUTE_CHANGED_FUNCTION);
294 // emberAfPushEndpointNetworkIndex(endpoint);
295 status = ((EmberAfClusterPreAttributeChangedCallback) f)(endpoint, attributeId, attributeType, size, value);
296 // emberAfPopNetworkIndex();
303 static void initializeEndpoint(EmberAfDefinedEndpoint * definedEndpoint)
305 uint8_t clusterIndex;
306 EmberAfEndpointType * epType = definedEndpoint->endpointType;
307 // emberAfPushEndpointNetworkIndex(definedEndpoint->endpoint);
308 for (clusterIndex = 0; clusterIndex < epType->clusterCount; clusterIndex++)
310 EmberAfCluster * cluster = &(epType->cluster[clusterIndex]);
311 EmberAfGenericClusterFunction f;
312 emberAfClusterInitCallback(definedEndpoint->endpoint, cluster->clusterId);
313 f = emberAfFindClusterFunction(cluster, CLUSTER_MASK_INIT_FUNCTION);
316 ((EmberAfInitFunction) f)(definedEndpoint->endpoint);
319 // emberAfPopNetworkIndex();
322 // Calls the init functions.
323 void emAfCallInits(void)
326 for (index = 0; index < emberAfEndpointCount(); index++)
328 if (emberAfEndpointIndexIsEnabled(index))
330 initializeEndpoint(&(emAfEndpoints[index]));
335 // Returns the pointer to metadata, or null if it is not found
336 EmberAfAttributeMetadata * emberAfLocateAttributeMetadata(EndpointId endpoint, ClusterId clusterId, AttributeId attributeId,
337 uint8_t mask, uint16_t manufacturerCode)
339 EmberAfAttributeMetadata * metadata = NULL;
340 EmberAfAttributeSearchRecord record;
341 record.endpoint = endpoint;
342 record.clusterId = clusterId;
343 record.clusterMask = mask;
344 record.attributeId = attributeId;
345 record.manufacturerCode = manufacturerCode;
346 emAfReadOrWriteAttribute(&record, &metadata,
353 static uint8_t * singletonAttributeLocation(EmberAfAttributeMetadata * am)
355 EmberAfAttributeMetadata * m = (EmberAfAttributeMetadata *) &(generatedAttributes[0]);
359 if ((m->mask & ATTRIBUTE_MASK_SINGLETON) != 0U)
361 index = static_cast<uint16_t>(index + m->size);
365 return (uint8_t *) (singletonAttributeData + index);
368 // This function does mem copy, but smartly, which means that if the type is a
369 // string, it will copy as much as it can.
370 // If src == NULL, then this method will set memory to zeroes
371 // See documentation for emAfReadOrWriteAttribute for the semantics of
372 // readLength when reading and writing.
373 static EmberAfStatus typeSensitiveMemCopy(uint8_t * dest, uint8_t * src, EmberAfAttributeMetadata * am, bool write,
376 EmberAfAttributeType attributeType = am->attributeType;
377 // readLength == 0 for a read indicates that we should just trust that the
378 // caller has enough space for an attribute...
379 bool ignoreReadLength = write || (readLength == 0);
380 uint16_t bufferSize = ignoreReadLength ? am->size : readLength;
382 if (emberAfIsStringAttributeType(attributeType))
386 return EMBER_ZCL_STATUS_INSUFFICIENT_SPACE;
388 emberAfCopyString(dest, src, static_cast<uint8_t>(bufferSize - 1));
390 else if (emberAfIsLongStringAttributeType(attributeType))
394 return EMBER_ZCL_STATUS_INSUFFICIENT_SPACE;
396 emberAfCopyLongString(dest, src, static_cast<uint16_t>(bufferSize - 2));
400 if (!ignoreReadLength && readLength < am->size)
402 return EMBER_ZCL_STATUS_INSUFFICIENT_SPACE;
406 memset(dest, 0, am->size);
410 memmove(dest, src, am->size);
413 return EMBER_ZCL_STATUS_SUCCESS;
416 // Returns the manufacturer code or ::EMBER_AF_NULL_MANUFACTURER_CODE if none
418 static uint16_t getManufacturerCode(EmberAfManufacturerCodeEntry * codes, uint16_t codeTableSize, uint16_t tableIndex)
421 for (i = 0; i < codeTableSize; i++)
423 if (codes->index == tableIndex)
425 return codes->manufacturerCode;
429 return EMBER_AF_NULL_MANUFACTURER_CODE;
432 // This function basically wraps getManufacturerCode with the parameters
433 // associating an attributes metadata with its code.
434 uint16_t emberAfGetMfgCode(EmberAfAttributeMetadata * metadata)
436 return getManufacturerCode((EmberAfManufacturerCodeEntry *) attributeManufacturerCodes, attributeManufacturerCodeCount,
437 static_cast<uint16_t>((metadata - generatedAttributes)));
440 uint16_t emAfGetManufacturerCodeForAttribute(EmberAfCluster * cluster, EmberAfAttributeMetadata * attMetaData)
442 return (emberAfClusterIsManufacturerSpecific(cluster) ? emAfGetManufacturerCodeForCluster(cluster)
443 : emberAfGetMfgCode(attMetaData));
446 uint16_t emAfGetManufacturerCodeForCluster(EmberAfCluster * cluster)
448 return getManufacturerCode((EmberAfManufacturerCodeEntry *) clusterManufacturerCodes, clusterManufacturerCodeCount,
449 static_cast<uint16_t>(cluster - generatedClusters));
453 * @brief Matches a cluster based on cluster id, direction and manufacturer code.
454 * This function assumes that the passed cluster's endpoint already
455 * matches the endpoint of the EmberAfAttributeSearchRecord.
457 * Cluster's match if:
458 * 1. Cluster ids match AND
459 * 2. Cluster directions match as defined by cluster->mask
460 * and attRecord->clusterMask AND
461 * 3. If the clusters are mf specific, their mfg codes match.
463 bool emAfMatchCluster(EmberAfCluster * cluster, EmberAfAttributeSearchRecord * attRecord)
465 return (cluster->clusterId == attRecord->clusterId && cluster->mask & attRecord->clusterMask &&
466 (!emberAfClusterIsManufacturerSpecific(cluster) ||
467 (emAfGetManufacturerCodeForCluster(cluster) == attRecord->manufacturerCode)));
471 * @brief Matches an attribute based on attribute id and manufacturer code.
472 * This function assumes that the passed cluster already matches the
473 * clusterId, direction and mf specificity of the passed
474 * EmberAfAttributeSearchRecord.
476 * Note: If both the attribute and cluster are manufacturer specific,
477 * the cluster's mf code gets precedence.
479 * Attributes match if:
480 * 1. Att ids match AND
481 * a. cluster IS mf specific OR
482 * b. both stored and saught attributes are NOT mf specific OR
483 * c. stored att IS mf specific AND mfg codes match.
485 bool emAfMatchAttribute(EmberAfCluster * cluster, EmberAfAttributeMetadata * am, EmberAfAttributeSearchRecord * attRecord)
487 return (am->attributeId == attRecord->attributeId &&
488 (emberAfClusterIsManufacturerSpecific(cluster) ||
489 (emAfGetManufacturerCodeForAttribute(cluster, am) == attRecord->manufacturerCode)));
492 // When reading non-string attributes, this function returns an error when destination
493 // buffer isn't large enough to accommodate the attribute type. For strings, the
494 // function will copy at most readLength bytes. This means the resulting string
495 // may be truncated. The length byte(s) in the resulting string will reflect
496 // any truncation. If readLength is zero, we are working with backwards-
497 // compatibility wrapper functions and we just cross our fingers and hope for
500 // When writing attributes, readLength is ignored. For non-string attributes,
501 // this function assumes the source buffer is the same size as the attribute
502 // type. For strings, the function will copy as many bytes as will fit in the
503 // attribute. This means the resulting string may be truncated. The length
504 // byte(s) in the resulting string will reflect any truncated.
505 EmberAfStatus emAfReadOrWriteAttribute(EmberAfAttributeSearchRecord * attRecord, EmberAfAttributeMetadata ** metadata,
506 uint8_t * buffer, uint16_t readLength, bool write)
509 uint16_t attributeOffsetIndex = 0;
511 for (i = 0; i < emberAfEndpointCount(); i++)
513 if (emAfEndpoints[i].endpoint == attRecord->endpoint)
515 EmberAfEndpointType * endpointType = emAfEndpoints[i].endpointType;
516 uint8_t clusterIndex;
517 if (!emberAfEndpointIndexIsEnabled(i))
521 for (clusterIndex = 0; clusterIndex < endpointType->clusterCount; clusterIndex++)
523 EmberAfCluster * cluster = &(endpointType->cluster[clusterIndex]);
524 if (emAfMatchCluster(cluster, attRecord))
527 for (attrIndex = 0; attrIndex < cluster->attributeCount; attrIndex++)
529 EmberAfAttributeMetadata * am = &(cluster->attributes[attrIndex]);
530 if (emAfMatchAttribute(cluster, am, attRecord))
531 { // Got the attribute
532 // If passed metadata location is not null, populate
533 if (metadata != NULL)
539 uint8_t * attributeLocation =
540 (am->mask & ATTRIBUTE_MASK_SINGLETON ? singletonAttributeLocation(am)
541 : attributeData + attributeOffsetIndex);
546 dst = attributeLocation;
547 if (!emberAfAttributeWriteAccessCallback(attRecord->endpoint, attRecord->clusterId,
548 emAfGetManufacturerCodeForAttribute(cluster, am),
551 return EMBER_ZCL_STATUS_NOT_AUTHORIZED;
558 return EMBER_ZCL_STATUS_SUCCESS;
561 src = attributeLocation;
563 if (!emberAfAttributeReadAccessCallback(attRecord->endpoint, attRecord->clusterId,
564 emAfGetManufacturerCodeForAttribute(cluster, am),
567 return EMBER_ZCL_STATUS_NOT_AUTHORIZED;
571 return (am->mask & ATTRIBUTE_MASK_EXTERNAL_STORAGE
572 ? (write) ? emberAfExternalAttributeWriteCallback(
573 attRecord->endpoint, attRecord->clusterId, am,
574 emAfGetManufacturerCodeForAttribute(cluster, am), buffer)
575 : emberAfExternalAttributeReadCallback(
576 attRecord->endpoint, attRecord->clusterId, am,
577 emAfGetManufacturerCodeForAttribute(cluster, am), buffer,
578 emberAfAttributeSize(am))
579 : typeSensitiveMemCopy(dst, src, am, write, readLength));
583 { // Not the attribute we are looking for
584 // Increase the index if attribute is not externally stored
585 if (!(am->mask & ATTRIBUTE_MASK_EXTERNAL_STORAGE) && !(am->mask & ATTRIBUTE_MASK_SINGLETON))
587 attributeOffsetIndex = static_cast<uint16_t>(attributeOffsetIndex + emberAfAttributeSize(am));
593 { // Not the cluster we are looking for
594 attributeOffsetIndex = static_cast<uint16_t>(attributeOffsetIndex + cluster->clusterSize);
599 { // Not the endpoint we are looking for
600 attributeOffsetIndex = static_cast<uint16_t>(attributeOffsetIndex + emAfEndpoints[i].endpointType->endpointSize);
603 return EMBER_ZCL_STATUS_UNSUPPORTED_ATTRIBUTE; // Sorry, attribute was not found.
606 // Check if a cluster is implemented or not. If yes, the cluster is returned.
607 // If the cluster is not manufacturerSpecific [ClusterId < FC00] then
608 // manufacturerCode argument is ignored otherwise checked.
610 // mask = 0 -> find either client or server
611 // mask = CLUSTER_MASK_CLIENT -> find client
612 // mask = CLUSTER_MASK_SERVER -> find server
613 EmberAfCluster * emberAfFindClusterInTypeWithMfgCode(EmberAfEndpointType * endpointType, ClusterId clusterId,
614 EmberAfClusterMask mask, uint16_t manufacturerCode)
617 for (i = 0; i < endpointType->clusterCount; i++)
619 EmberAfCluster * cluster = &(endpointType->cluster[i]);
620 if (cluster->clusterId == clusterId &&
621 (mask == 0 || (mask == CLUSTER_MASK_CLIENT && emberAfClusterIsClient(cluster)) ||
622 (mask == CLUSTER_MASK_SERVER && emberAfClusterIsServer(cluster))) &&
623 (!emberAfClusterIsManufacturerSpecific(cluster) ||
624 (emAfGetManufacturerCodeForCluster(cluster) == manufacturerCode)
625 // For compatibility with older stack api, we ignore manufacturer code here
626 // if the manufacturerCode == EMBER_AF_NULL_MANUFACTURER_CODE
627 || manufacturerCode == EMBER_AF_NULL_MANUFACTURER_CODE))
635 // This functions wraps emberAfFindClusterInTypeWithMfgCode with
636 // a manufacturerCode of EMBER_AF_NULL_MANUFACTURER_CODE.
637 EmberAfCluster * emberAfFindClusterInType(EmberAfEndpointType * endpointType, ClusterId clusterId, EmberAfClusterMask mask)
639 return emberAfFindClusterInTypeWithMfgCode(endpointType, clusterId, mask, EMBER_AF_NULL_MANUFACTURER_CODE);
642 // This code is used during unit tests for clusters that do not involve manufacturer code.
643 // Should this code be used in other locations, manufacturerCode should be added.
644 uint8_t emberAfClusterIndex(EndpointId endpoint, ClusterId clusterId, EmberAfClusterMask mask)
647 uint8_t index = 0xFF;
648 for (ep = 0; ep < emberAfEndpointCount(); ep++)
650 EmberAfEndpointType * endpointType = emAfEndpoints[ep].endpointType;
651 if (emberAfFindClusterInTypeWithMfgCode(endpointType, clusterId, mask, EMBER_AF_NULL_MANUFACTURER_CODE) != NULL)
654 if (emAfEndpoints[ep].endpoint == endpoint)
663 // Returns true uf endpoint contains passed cluster
664 bool emberAfContainsClusterWithMfgCode(EndpointId endpoint, ClusterId clusterId, uint16_t manufacturerCode)
666 return (emberAfFindClusterWithMfgCode(endpoint, clusterId, 0, manufacturerCode) != NULL);
669 // Returns true if endpoint contains passed cluster as a server
670 bool emberAfContainsServerWithMfgCode(EndpointId endpoint, ClusterId clusterId, uint16_t manufacturerCode)
672 return (emberAfFindClusterWithMfgCode(endpoint, clusterId, CLUSTER_MASK_SERVER, manufacturerCode) != NULL);
675 // Returns true if endpoint contains passed cluster as a client
676 bool emberAfContainsClientWithMfgCode(EndpointId endpoint, ClusterId clusterId, uint16_t manufacturerCode)
678 return (emberAfFindClusterWithMfgCode(endpoint, clusterId, CLUSTER_MASK_CLIENT, manufacturerCode) != NULL);
681 // Wraps emberAfContainsClusterWithMfgCode with EMBER_AF_NULL_MANUFACTURER_CODE
682 // This will find the first cluster that has the clusterId given, regardless of mfgCode.
683 bool emberAfContainsCluster(EndpointId endpoint, ClusterId clusterId)
685 return (emberAfFindClusterWithMfgCode(endpoint, clusterId, 0, EMBER_AF_NULL_MANUFACTURER_CODE) != NULL);
688 // Wraps emberAfContainsServerWithMfgCode with EMBER_AF_NULL_MANUFACTURER_CODE
689 // This will find the first server that has the clusterId given, regardless of mfgCode.
690 bool emberAfContainsServer(EndpointId endpoint, ClusterId clusterId)
692 return (emberAfFindClusterWithMfgCode(endpoint, clusterId, CLUSTER_MASK_SERVER, EMBER_AF_NULL_MANUFACTURER_CODE) != NULL);
695 // Wraps emberAfContainsClientWithMfgCode with EMBER_AF_NULL_MANUFACTURER_CODE
696 // This will find the first client that has the clusterId given, regardless of mfgCode.
697 bool emberAfContainsClient(EndpointId endpoint, ClusterId clusterId)
699 return (emberAfFindClusterWithMfgCode(endpoint, clusterId, CLUSTER_MASK_CLIENT, EMBER_AF_NULL_MANUFACTURER_CODE) != NULL);
702 // Finds the cluster that matches endpoint, clusterId, direction, and manufacturerCode.
703 EmberAfCluster * emberAfFindClusterWithMfgCode(EndpointId endpoint, ClusterId clusterId, EmberAfClusterMask mask,
704 uint16_t manufacturerCode)
706 uint8_t ep = emberAfIndexFromEndpoint(endpoint);
713 return emberAfFindClusterInTypeWithMfgCode(emAfEndpoints[ep].endpointType, clusterId, mask, manufacturerCode);
717 // This function wraps emberAfFindClusterWithMfgCode with EMBER_AF_NULL_MANUFACTURER_CODE
718 // and will ignore the manufacturerCode when trying to find clusters.
719 // This will return the first cluster in the cluster table that matches the parameters given.
720 EmberAfCluster * emberAfFindCluster(EndpointId endpoint, ClusterId clusterId, EmberAfClusterMask mask)
722 return emberAfFindClusterWithMfgCode(endpoint, clusterId, mask, EMBER_AF_NULL_MANUFACTURER_CODE);
725 // Returns cluster within the endpoint; Does not ignore disabled endpoints
726 EmberAfCluster * emberAfFindClusterIncludingDisabledEndpointsWithMfgCode(EndpointId endpoint, ClusterId clusterId,
727 EmberAfClusterMask mask, uint16_t manufacturerCode)
729 uint8_t ep = emberAfIndexFromEndpointIncludingDisabledEndpoints(endpoint);
730 if (ep < MAX_ENDPOINT_COUNT)
732 return emberAfFindClusterInTypeWithMfgCode(emAfEndpoints[ep].endpointType, clusterId, mask, manufacturerCode);
737 // Returns cluster within the endpoint; Does not ignore disabled endpoints
738 // This will ignore manufacturerCode.
739 EmberAfCluster * emberAfFindClusterIncludingDisabledEndpoints(EndpointId endpoint, ClusterId clusterId, EmberAfClusterMask mask)
741 return emberAfFindClusterIncludingDisabledEndpointsWithMfgCode(endpoint, clusterId, mask, EMBER_AF_NULL_MANUFACTURER_CODE);
744 // Server wrapper for findClusterEndpointIndex.
745 static uint8_t emberAfFindClusterServerEndpointIndexWithMfgCode(EndpointId endpoint, ClusterId clusterId, uint16_t manufacturerCode)
747 return findClusterEndpointIndex(endpoint, clusterId, CLUSTER_MASK_SERVER, manufacturerCode);
750 // Client wrapper for findClusterEndpointIndex.
751 uint8_t emberAfFindClusterClientEndpointIndexWithMfgCode(EndpointId endpoint, ClusterId clusterId, uint16_t manufacturerCode)
753 return findClusterEndpointIndex(endpoint, clusterId, CLUSTER_MASK_CLIENT, manufacturerCode);
756 // Server wrapper for findClusterEndpointIndex
757 // This will ignore manufacturerCode, and return the index for the first server that matches on clusterId
758 uint8_t emberAfFindClusterServerEndpointIndex(EndpointId endpoint, ClusterId clusterId)
760 return emberAfFindClusterServerEndpointIndexWithMfgCode(endpoint, clusterId, EMBER_AF_NULL_MANUFACTURER_CODE);
763 // Client wrapper for findClusterEndpointIndex
764 // This will ignore manufacturerCode, and return the index for the first client that matches on clusterId
765 uint8_t emberAfFindClusterClientEndpointIndex(EndpointId endpoint, ClusterId clusterId)
767 return emberAfFindClusterClientEndpointIndexWithMfgCode(endpoint, clusterId, EMBER_AF_NULL_MANUFACTURER_CODE);
770 // Returns the endpoint index within a given cluster
771 static uint8_t findClusterEndpointIndex(EndpointId endpoint, ClusterId clusterId, uint8_t mask, uint16_t manufacturerCode)
775 if (emberAfFindClusterWithMfgCode(endpoint, clusterId, mask, manufacturerCode) == NULL)
780 for (i = 0; i < emberAfEndpointCount(); i++)
782 if (emAfEndpoints[i].endpoint == endpoint)
786 epi = static_cast<uint8_t>(epi +
787 ((emberAfFindClusterIncludingDisabledEndpointsWithMfgCode(emAfEndpoints[i].endpoint, clusterId,
788 mask, manufacturerCode) != NULL)
796 static uint8_t findIndexFromEndpoint(EndpointId endpoint, bool ignoreDisabledEndpoints)
799 for (epi = 0; epi < emberAfEndpointCount(); epi++)
801 if (emAfEndpoints[epi].endpoint == endpoint &&
802 (!ignoreDisabledEndpoints || emAfEndpoints[epi].bitmask & EMBER_AF_ENDPOINT_ENABLED))
810 bool emberAfEndpointIsEnabled(EndpointId endpoint)
812 uint8_t index = findIndexFromEndpoint(endpoint,
813 false); // ignore disabled endpoints?
815 EMBER_TEST_ASSERT(0xFF != index);
822 return emberAfEndpointIndexIsEnabled(index);
825 // bool emberAfEndpointEnableDisable(EndpointId endpoint, bool enable)
827 // uint8_t index = findIndexFromEndpoint(endpoint,
828 // false); // ignore disabled endpoints?
829 // bool currentlyEnabled;
831 // if (0xFF == index)
836 // currentlyEnabled = emAfEndpoints[index].bitmask & EMBER_AF_ENDPOINT_ENABLED;
840 // emAfEndpoints[index].bitmask |= EMBER_AF_ENDPOINT_ENABLED;
844 // emAfEndpoints[index].bitmask &= EMBER_AF_ENDPOINT_DISABLED;
847 // #if defined(EZSP_HOST)
848 // ezspSetEndpointFlags(endpoint, (enable ? EZSP_ENDPOINT_ENABLED : EZSP_ENDPOINT_DISABLED));
851 // if (currentlyEnabled != enable)
855 // initializeEndpoint(&(emAfEndpoints[index]));
860 // for (i = 0; i < emAfEndpoints[index].endpointType->clusterCount; i++)
862 // EmberAfCluster * cluster = &((emAfEndpoints[index].endpointType->cluster)[i]);
863 // // emberAfCorePrintln("Disabling cluster tick for ep:%d, cluster:0x%2X, %p",
865 // // cluster->clusterId,
866 // // ((cluster->mask & CLUSTER_MASK_CLIENT)
869 // // emberAfCoreFlush();
870 // emberAfDeactivateClusterTick(
871 // endpoint, cluster->clusterId,
872 // (cluster->mask & CLUSTER_MASK_CLIENT ? EMBER_AF_CLIENT_CLUSTER_TICK : EMBER_AF_SERVER_CLUSTER_TICK));
880 // Returns the index of a given endpoint. Does not consider disabled endpoints.
881 uint8_t emberAfIndexFromEndpoint(EndpointId endpoint)
883 return findIndexFromEndpoint(endpoint,
884 true); // ignore disabled endpoints?
887 // Returns the index of a given endpoint. Considers disabled endpoints.
888 uint8_t emberAfIndexFromEndpointIncludingDisabledEndpoints(EndpointId endpoint)
890 return findIndexFromEndpoint(endpoint,
891 false); // ignore disabled endpoints?
894 EndpointId emberAfEndpointFromIndex(uint8_t index)
896 return emAfEndpoints[index].endpoint;
899 // If server == true, returns the number of server clusters,
900 // otherwise number of client clusters on this endpoint
901 uint8_t emberAfClusterCount(EndpointId endpoint, bool server)
903 uint8_t index = emberAfIndexFromEndpoint(endpoint);
905 EmberAfDefinedEndpoint * de;
906 EmberAfCluster * cluster;
912 de = &(emAfEndpoints[index]);
913 if (de->endpointType == NULL)
917 for (i = 0; i < de->endpointType->clusterCount; i++)
919 cluster = &(de->endpointType->cluster[i]);
920 if (server && emberAfClusterIsServer(cluster))
924 if ((!server) && emberAfClusterIsClient(cluster))
932 uint8_t emberAfGetClusterCountForEndpoint(EndpointId endpoint)
934 uint8_t index = emberAfIndexFromEndpoint(endpoint);
939 return emAfEndpoints[index].endpointType->clusterCount;
942 // Note the difference in implementation from emberAfGetNthCluster().
943 // emberAfGetClusterByIndex() retrieves the cluster by index regardless of server/client
944 // and those indexes may be DIFFERENT than the indexes returned from
945 // emberAfGetNthCluster(). In other words:
947 // - Use emberAfGetClustersFromEndpoint() with emberAfGetNthCluster()
948 // - Use emberAfGetClusterCountForEndpoint() with emberAfGetClusterByIndex()
951 EmberAfCluster * emberAfGetClusterByIndex(EndpointId endpoint, uint8_t clusterIndex)
953 uint8_t endpointIndex = emberAfIndexFromEndpoint(endpoint);
954 EmberAfDefinedEndpoint * definedEndpoint;
956 if (endpointIndex == 0xFF)
960 definedEndpoint = &(emAfEndpoints[endpointIndex]);
962 if (clusterIndex >= definedEndpoint->endpointType->clusterCount)
966 return &(definedEndpoint->endpointType->cluster[clusterIndex]);
969 uint16_t emberAfGetDeviceIdForEndpoint(EndpointId endpoint)
971 uint8_t endpointIndex = emberAfIndexFromEndpoint(endpoint);
972 if (endpointIndex == 0xFF)
976 return emAfEndpoints[endpointIndex].deviceId;
979 // Returns the cluster of Nth server or client cluster,
980 // depending on server toggle.
981 EmberAfCluster * emberAfGetNthCluster(EndpointId endpoint, uint8_t n, bool server)
983 uint8_t index = emberAfIndexFromEndpoint(endpoint);
984 EmberAfDefinedEndpoint * de;
986 EmberAfCluster * cluster;
992 de = &(emAfEndpoints[index]);
994 for (i = 0; i < de->endpointType->clusterCount; i++)
996 cluster = &(de->endpointType->cluster[i]);
998 if ((server && emberAfClusterIsServer(cluster)) || ((!server) && emberAfClusterIsClient(cluster)))
1010 // Returns number of clusters put into the passed cluster list
1011 // for the given endpoint and client/server polarity
1012 uint8_t emberAfGetClustersFromEndpoint(EndpointId endpoint, ClusterId * clusterList, uint8_t listLen, bool server)
1014 uint8_t clusterCount = emberAfClusterCount(endpoint, server);
1016 EmberAfCluster * cluster;
1017 if (clusterCount > listLen)
1019 clusterCount = listLen;
1021 for (i = 0; i < clusterCount; i++)
1023 cluster = emberAfGetNthCluster(endpoint, i, server);
1024 clusterList[i] = (cluster == NULL ? 0xFFFF : cluster->clusterId);
1026 return clusterCount;
1029 void emberAfInitializeAttributes(EndpointId endpoint)
1031 emAfLoadAttributeDefaults(endpoint, false);
1034 void emberAfResetAttributes(EndpointId endpoint)
1036 emAfLoadAttributeDefaults(endpoint, true);
1039 void emAfLoadAttributeDefaults(EndpointId endpoint, bool writeTokens)
1041 uint8_t ep, clusterI, curNetwork = 0 /* emberGetCurrentNetwork() */;
1044 uint8_t epCount = emberAfEndpointCount();
1046 for (ep = 0; ep < epCount; ep++)
1048 EmberAfDefinedEndpoint * de;
1049 if (endpoint != EMBER_BROADCAST_ENDPOINT)
1051 ep = emberAfIndexFromEndpoint(endpoint);
1057 de = &(emAfEndpoints[ep]);
1059 // Ensure that the endpoint is on the current network
1060 if (endpoint == EMBER_BROADCAST_ENDPOINT && de->networkIndex != curNetwork)
1064 for (clusterI = 0; clusterI < de->endpointType->clusterCount; clusterI++)
1066 EmberAfCluster * cluster = &(de->endpointType->cluster[clusterI]);
1068 // when the attributeCount is high, the loop takes too long to run and a
1069 // watchdog kicks in causing a reset. As a workaround, we'll
1070 // conditionally manually reset the watchdog. 300 sounds like a good
1071 // magic number for now.
1072 if (cluster->attributeCount > 300)
1074 // halResetWatchdog();
1076 for (attr = 0; attr < cluster->attributeCount; attr++)
1078 EmberAfAttributeMetadata * am = &(cluster->attributes[attr]);
1079 if (!(am->mask & ATTRIBUTE_MASK_EXTERNAL_STORAGE))
1081 EmberAfAttributeSearchRecord record;
1082 record.endpoint = de->endpoint;
1083 record.clusterId = cluster->clusterId;
1084 record.clusterMask = (emberAfAttributeIsClient(am) ? CLUSTER_MASK_CLIENT : CLUSTER_MASK_SERVER);
1085 record.attributeId = am->attributeId;
1086 record.manufacturerCode = emAfGetManufacturerCodeForAttribute(cluster, am);
1087 if ((am->mask & ATTRIBUTE_MASK_MIN_MAX) != 0U)
1089 if (emberAfAttributeSize(am) <= 2)
1091 ptr = (uint8_t *) &(am->defaultValue.ptrToMinMaxValue->defaultValue.defaultValue);
1095 ptr = (uint8_t *) am->defaultValue.ptrToMinMaxValue->defaultValue.ptrToDefaultValue;
1100 if (emberAfAttributeSize(am) <= 2)
1102 ptr = (uint8_t *) &(am->defaultValue.defaultValue);
1106 ptr = (uint8_t *) am->defaultValue.ptrToDefaultValue;
1109 // At this point, ptr either points to a default value, or is NULL, in which case
1110 // it should be treated as if it is pointing to an array of all zeroes.
1113 // The default value for one- and two-byte attributes is stored in an
1114 // uint16_t. On big-endian platforms, a pointer to the default value of
1115 // a one-byte attribute will point to the wrong byte. So, for those
1116 // cases, nudge the pointer forward so it points to the correct byte.
1117 if (emberAfAttributeSize(am) == 1 && ptr != NULL)
1122 emAfReadOrWriteAttribute(&record,
1123 NULL, // metadata - unused
1125 0, // buffer size - unused
1129 emAfSaveAttributeToToken(ptr, de->endpoint, record.clusterId, am);
1134 if (endpoint != EMBER_BROADCAST_ENDPOINT)
1142 emAfLoadAttributesFromTokens(endpoint);
1146 void emAfLoadAttributesFromTokens(EndpointId endpoint)
1148 // On EZSP host we currently do not support this. We need to come up with some
1151 GENERATED_TOKEN_LOADER(endpoint);
1155 // 'data' argument may be null, since we changed the ptrToDefaultValue
1156 // to be null instead of pointing to all zeroes.
1157 // This function has to be able to deal with that.
1158 void emAfSaveAttributeToToken(uint8_t * data, EndpointId endpoint, ClusterId clusterId, EmberAfAttributeMetadata * metadata)
1160 // Get out of here if this attribute doesn't have a token.
1161 if (!emberAfAttributeIsTokenized(metadata))
1166 // On EZSP host we currently do not support this. We need to come up with some
1169 GENERATED_TOKEN_SAVER;
1173 // This function returns the actual function point from the array,
1174 // iterating over the function bits.
1175 EmberAfGenericClusterFunction emberAfFindClusterFunction(EmberAfCluster * cluster, EmberAfClusterMask functionMask)
1177 EmberAfClusterMask mask = 0x01;
1178 uint8_t functionIndex = 0;
1180 if ((cluster->mask & functionMask) == 0)
1185 while (mask < functionMask)
1187 if ((cluster->mask & mask) != 0)
1191 mask = static_cast<EmberAfClusterMask>(mask << 1);
1193 return cluster->functions[functionIndex];
1196 #ifdef EMBER_AF_SUPPORT_COMMAND_DISCOVERY
1198 uint16_t emAfGetManufacturerCodeForCommand(EmberAfCommandMetadata * command)
1200 return getManufacturerCode((EmberAfManufacturerCodeEntry *) commandManufacturerCodes, commandManufacturerCodeCount,
1201 static_cast<uint16_t>(command - generatedCommands));
1205 * This function populates command IDs into a given buffer.
1207 * It returns true if commands are complete, meaning there are NO MORE
1208 * commands that would be returned after the last command.
1209 * It returns false, if there were more commands, but were not populated
1210 * because of maxIdCount limitation.
1212 bool emberAfExtractCommandIds(bool outgoing, EmberAfClusterCommand * cmd, ClusterId clusterId, uint8_t * buffer,
1213 uint16_t bufferLength, uint16_t * bufferIndex, uint8_t startId, uint8_t maxIdCount)
1215 uint16_t i, count = 0;
1216 bool returnValue = true;
1217 uint8_t cmdDirMask = 0;
1219 // determine the appropriate mask to match the request
1220 // discover commands generated, client is asking server what commands do you generate?
1221 if (outgoing && (cmd->direction == ZCL_DIRECTION_CLIENT_TO_SERVER))
1223 cmdDirMask = COMMAND_MASK_OUTGOING_SERVER;
1224 // discover commands generated server is asking client what commands do you generate?
1226 else if (outgoing && (cmd->direction == ZCL_DIRECTION_SERVER_TO_CLIENT))
1228 cmdDirMask = COMMAND_MASK_OUTGOING_CLIENT;
1229 // discover commands received client is asking server what commands do you receive?
1231 else if (!outgoing && (cmd->direction == ZCL_DIRECTION_CLIENT_TO_SERVER))
1233 cmdDirMask = COMMAND_MASK_INCOMING_SERVER;
1234 // discover commands received server is asking client what commands do you receive?
1238 cmdDirMask = COMMAND_MASK_INCOMING_CLIENT;
1241 for (i = 0; i < EMBER_AF_GENERATED_COMMAND_COUNT; i++)
1243 if (generatedCommands[i].clusterId != clusterId)
1248 if ((generatedCommands[i].mask & cmdDirMask) == 0)
1253 // Only start from the passed command id
1254 if (generatedCommands[i].commandId < startId)
1259 // According to spec: if cmd->mfgSpecific is set, then we ONLY return the
1260 // mfg specific commands. If it's not, then we ONLY return non-mfg specific.
1261 if (generatedCommands[i].mask & COMMAND_MASK_MANUFACTURER_SPECIFIC)
1263 // Command is Mfg specific
1264 if (!cmd->mfgSpecific)
1266 continue; // ignore if asking for not mfg specific
1268 if (cmd->mfgCode != emAfGetManufacturerCodeForCommand((EmberAfCommandMetadata *) &(generatedCommands[i])))
1270 continue; // Ignore if mfg code doesn't match the commands
1275 // Command is not mfg specific.
1276 if (cmd->mfgSpecific)
1278 continue; // Ignore if asking for mfg specific
1282 // The one we are about to put in, is beyond the maxIdCount,
1283 // so instead of populating it in, we set the return flag to
1284 // false and get out of here.
1285 if (maxIdCount == count || count >= bufferLength)
1287 returnValue = false;
1290 buffer[count] = generatedCommands[i].commandId;
1297 // We just need an empty stub if we don't support it
1298 bool emberAfExtractCommandIds(bool outgoing, EmberAfClusterCommand * cmd, ClusterId clusterId, uint8_t * buffer,
1299 uint16_t bufferLength, uint16_t * bufferIndex, uint8_t startId, uint8_t maxIdCount)