| /** |
| * |
| * 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 This file contains the code to manipulate |
| *the Smart Energy attribute table. This handles |
| *external calls to read/write the table, as well as |
| *internal ones. |
| ******************************************************************************* |
| ******************************************************************************/ |
| |
| // this file contains all the common includes for clusters in the zcl-util |
| |
| #include <app/util/attribute-storage.h> |
| |
| // for pulling in defines dealing with EITHER server or client |
| #include "app/util/common.h" |
| #include <app-common/zap-generated/callback.h> |
| #include <app/util/af-main.h> |
| |
| #include <app/reporting/reporting.h> |
| |
| using namespace chip; |
| |
| //------------------------------------------------------------------------------ |
| |
| //------------------------------------------------------------------------------ |
| // External Declarations |
| |
| //------------------------------------------------------------------------------ |
| // Forward Declarations |
| |
| //------------------------------------------------------------------------------ |
| // Globals |
| |
| EmberAfStatus emberAfWriteAttributeExternal(EndpointId endpoint, ClusterId cluster, AttributeId attributeID, uint8_t mask, |
| uint16_t manufacturerCode, uint8_t * dataPtr, EmberAfAttributeType dataType) |
| { |
| EmberAfAttributeWritePermission extWritePermission = |
| emberAfAllowNetworkWriteAttributeCallback(endpoint, cluster, attributeID, mask, manufacturerCode, dataPtr, dataType); |
| switch (extWritePermission) |
| { |
| case EMBER_ZCL_ATTRIBUTE_WRITE_PERMISSION_DENY_WRITE: |
| return EMBER_ZCL_STATUS_FAILURE; |
| case EMBER_ZCL_ATTRIBUTE_WRITE_PERMISSION_ALLOW_WRITE_NORMAL: |
| case EMBER_ZCL_ATTRIBUTE_WRITE_PERMISSION_ALLOW_WRITE_OF_READ_ONLY: |
| return emAfWriteAttribute(endpoint, cluster, attributeID, mask, manufacturerCode, dataPtr, dataType, |
| (extWritePermission == EMBER_ZCL_ATTRIBUTE_WRITE_PERMISSION_ALLOW_WRITE_OF_READ_ONLY), false); |
| default: |
| return (EmberAfStatus) extWritePermission; |
| } |
| } |
| |
| //@deprecated use emberAfWriteServerAttribute or emberAfWriteClientAttribute |
| EmberAfStatus emberAfWriteAttribute(EndpointId endpoint, ClusterId cluster, AttributeId attributeID, uint8_t mask, |
| uint8_t * dataPtr, EmberAfAttributeType dataType) |
| { |
| return emAfWriteAttribute(endpoint, cluster, attributeID, mask, EMBER_AF_NULL_MANUFACTURER_CODE, dataPtr, dataType, |
| true, // override read-only? |
| false); // just test? |
| } |
| |
| EmberAfStatus emberAfWriteClientAttribute(EndpointId endpoint, ClusterId cluster, AttributeId attributeID, uint8_t * dataPtr, |
| EmberAfAttributeType dataType) |
| { |
| return emAfWriteAttribute(endpoint, cluster, attributeID, CLUSTER_MASK_CLIENT, EMBER_AF_NULL_MANUFACTURER_CODE, dataPtr, |
| dataType, |
| true, // override read-only? |
| false); // just test? |
| } |
| |
| EmberAfStatus emberAfWriteServerAttribute(EndpointId endpoint, ClusterId cluster, AttributeId attributeID, uint8_t * dataPtr, |
| EmberAfAttributeType dataType) |
| { |
| return emAfWriteAttribute(endpoint, cluster, attributeID, CLUSTER_MASK_SERVER, EMBER_AF_NULL_MANUFACTURER_CODE, dataPtr, |
| dataType, |
| true, // override read-only? |
| false); // just test? |
| } |
| |
| EmberAfStatus emberAfWriteManufacturerSpecificClientAttribute(EndpointId endpoint, ClusterId cluster, AttributeId attributeID, |
| uint16_t manufacturerCode, uint8_t * dataPtr, |
| EmberAfAttributeType dataType) |
| { |
| return emAfWriteAttribute(endpoint, cluster, attributeID, CLUSTER_MASK_CLIENT, manufacturerCode, dataPtr, dataType, |
| true, // override read-only? |
| false); // just test? |
| } |
| |
| EmberAfStatus emberAfWriteManufacturerSpecificServerAttribute(EndpointId endpoint, ClusterId cluster, AttributeId attributeID, |
| uint16_t manufacturerCode, uint8_t * dataPtr, |
| EmberAfAttributeType dataType) |
| { |
| return emAfWriteAttribute(endpoint, cluster, attributeID, CLUSTER_MASK_SERVER, manufacturerCode, dataPtr, dataType, |
| true, // override read-only? |
| false); // just test? |
| } |
| |
| EmberAfStatus emberAfVerifyAttributeWrite(EndpointId endpoint, ClusterId cluster, AttributeId attributeID, uint8_t mask, |
| uint16_t manufacturerCode, uint8_t * dataPtr, EmberAfAttributeType dataType) |
| { |
| return emAfWriteAttribute(endpoint, cluster, attributeID, mask, manufacturerCode, dataPtr, dataType, |
| false, // override read-only? |
| true); // just test? |
| } |
| |
| EmberAfStatus emberAfReadAttribute(EndpointId endpoint, ClusterId cluster, AttributeId attributeID, uint8_t mask, uint8_t * dataPtr, |
| uint16_t readLength, EmberAfAttributeType * dataType) |
| { |
| return emAfReadAttribute(endpoint, cluster, attributeID, mask, EMBER_AF_NULL_MANUFACTURER_CODE, dataPtr, readLength, dataType); |
| } |
| |
| EmberAfStatus emberAfReadServerAttribute(EndpointId endpoint, ClusterId cluster, AttributeId attributeID, uint8_t * dataPtr, |
| uint16_t readLength) |
| { |
| return emAfReadAttribute(endpoint, cluster, attributeID, CLUSTER_MASK_SERVER, EMBER_AF_NULL_MANUFACTURER_CODE, dataPtr, |
| readLength, NULL); |
| } |
| |
| EmberAfStatus emberAfReadClientAttribute(EndpointId endpoint, ClusterId cluster, AttributeId attributeID, uint8_t * dataPtr, |
| uint16_t readLength) |
| { |
| return emAfReadAttribute(endpoint, cluster, attributeID, CLUSTER_MASK_CLIENT, EMBER_AF_NULL_MANUFACTURER_CODE, dataPtr, |
| readLength, NULL); |
| } |
| |
| EmberAfStatus emberAfReadManufacturerSpecificServerAttribute(EndpointId endpoint, ClusterId cluster, AttributeId attributeID, |
| uint16_t manufacturerCode, uint8_t * dataPtr, uint16_t readLength) |
| { |
| return emAfReadAttribute(endpoint, cluster, attributeID, CLUSTER_MASK_SERVER, manufacturerCode, dataPtr, readLength, NULL); |
| } |
| |
| EmberAfStatus emberAfReadManufacturerSpecificClientAttribute(EndpointId endpoint, ClusterId cluster, AttributeId attributeID, |
| uint16_t manufacturerCode, uint8_t * dataPtr, uint16_t readLength) |
| { |
| return emAfReadAttribute(endpoint, cluster, attributeID, CLUSTER_MASK_CLIENT, manufacturerCode, dataPtr, readLength, NULL); |
| } |
| |
| bool emberAfReadSequentialAttributesAddToResponse(EndpointId endpoint, ClusterId clusterId, AttributeId startAttributeId, |
| uint8_t mask, uint16_t manufacturerCode, uint8_t maxAttributeIds, |
| bool includeAccessControl) |
| { |
| uint16_t i; |
| uint16_t discovered = 0; |
| uint16_t skipped = 0; |
| uint16_t total = 0; |
| |
| EmberAfCluster * cluster = emberAfFindClusterWithMfgCode(endpoint, clusterId, mask, manufacturerCode); |
| |
| EmberAfAttributeSearchRecord record; |
| record.endpoint = endpoint; |
| record.clusterId = clusterId; |
| record.clusterMask = mask; |
| record.attributeId = startAttributeId; |
| record.manufacturerCode = manufacturerCode; |
| |
| // If we don't have the cluster or it doesn't match the search, we're done. |
| if (cluster == NULL || !emAfMatchCluster(cluster, &record)) |
| { |
| return true; |
| } |
| |
| for (i = 0; i < cluster->attributeCount; i++) |
| { |
| EmberAfAttributeMetadata * metadata = &cluster->attributes[i]; |
| |
| // If the cluster is not manufacturer-specific, an attribute is considered |
| // only if its manufacturer code matches that of the command (which may be |
| // unset). |
| if (!emberAfClusterIsManufacturerSpecific(cluster)) |
| { |
| record.attributeId = metadata->attributeId; |
| if (!emAfMatchAttribute(cluster, metadata, &record)) |
| { |
| continue; |
| } |
| } |
| |
| if (metadata->attributeId < startAttributeId) |
| { |
| skipped++; |
| } |
| else if (discovered < maxAttributeIds) |
| { |
| emberAfPutInt32uInResp(metadata->attributeId); |
| emberAfPutInt8uInResp(metadata->attributeType); |
| if (includeAccessControl) |
| { |
| // bit 0 : Readable <-- All our attributes are readable |
| // bit 1 : Writable <-- The only thing we track in the attribute metadata mask |
| // bit 2 : Reportable <-- All our attributes are reportable |
| emberAfPutInt8uInResp((metadata->mask & ATTRIBUTE_MASK_WRITABLE) ? 0x07 : 0x05); |
| } |
| discovered++; |
| } |
| else |
| { |
| // MISRA requires ..else if.. to have terminating else. |
| } |
| total++; |
| } |
| |
| // We are finished if there are no more attributes to find, which means the |
| // number of attributes discovered plus the number skipped equals the total |
| // attributes in the cluster. For manufacturer-specific clusters, the total |
| // includes all attributes in the cluster. For standard ZCL clusters, if the |
| // the manufacturer code is set, the total is the number of attributes that |
| // match the manufacturer code. Otherwise, the total is the number of |
| // standard ZCL attributes in the cluster. |
| return (discovered + skipped == total); |
| } |
| |
| static void emberAfAttributeDecodeAndPrintCluster(ClusterId cluster, uint16_t mfgCode) |
| { |
| #if defined(EMBER_AF_PRINT_ENABLE) && defined(EMBER_AF_PRINT_ATTRIBUTES) |
| uint16_t index = emberAfFindClusterNameIndexWithMfgCode(cluster, mfgCode); |
| if (index != 0xFFFF) |
| { |
| emberAfAttributesPrintln("(%p)", zclClusterNames[index].name); |
| } |
| emberAfAttributesFlush(); |
| #endif // defined(EMBER_AF_PRINT_ENABLE) && defined(EMBER_AF_PRINT_ATTRIBUTES) |
| } |
| |
| void emberAfPrintAttributeTable(void) |
| { |
| uint8_t data[ATTRIBUTE_LARGEST]; |
| decltype(emberAfEndpointCount()) endpointIndex; |
| decltype(EmberAfEndpointType::clusterCount) clusterIndex; |
| uint16_t attributeIndex; |
| EmberAfStatus status; |
| uint16_t mfgCode; |
| for (endpointIndex = 0; endpointIndex < emberAfEndpointCount(); endpointIndex++) |
| { |
| EmberAfDefinedEndpoint * ep = &(emAfEndpoints[endpointIndex]); |
| emberAfAttributesPrintln("ENDPOINT %x", ep->endpoint); |
| emberAfAttributesPrintln("clus / side / attr / mfg /type(len)/ rw / storage / data (raw)"); |
| emberAfAttributesFlush(); |
| for (clusterIndex = 0; clusterIndex < ep->endpointType->clusterCount; clusterIndex++) |
| { |
| EmberAfCluster * cluster = &(ep->endpointType->cluster[clusterIndex]); |
| |
| for (attributeIndex = 0; attributeIndex < cluster->attributeCount; attributeIndex++) |
| { |
| EmberAfAttributeMetadata * metaData = &(cluster->attributes[attributeIndex]); |
| |
| // Depending on user config, this loop can take a very long time to |
| // run and watchdog reset will kick in. As a workaround, we'll |
| // manually reset the watchdog. |
| // halResetWatchdog(); |
| |
| emberAfAttributesPrint("%2x / %p / %2x / ", cluster->clusterId, |
| (emberAfAttributeIsClient(metaData) ? "clnt" : "srvr"), metaData->attributeId); |
| mfgCode = emAfGetManufacturerCodeForAttribute(cluster, metaData); |
| if (mfgCode == EMBER_AF_NULL_MANUFACTURER_CODE) |
| { |
| emberAfAttributesPrint("----"); |
| } |
| else |
| { |
| emberAfAttributesPrint("%2x", mfgCode); |
| } |
| emberAfAttributesPrint(" / %x (%x) / %p / %p / ", metaData->attributeType, emberAfAttributeSize(metaData), |
| (emberAfAttributeIsReadOnly(metaData) ? "RO" : "RW"), |
| (emberAfAttributeIsTokenized(metaData) |
| ? " token " |
| : (emberAfAttributeIsExternal(metaData) ? "extern " : " RAM "))); |
| emberAfAttributesFlush(); |
| status = emAfReadAttribute(ep->endpoint, cluster->clusterId, metaData->attributeId, |
| (emberAfAttributeIsClient(metaData) ? CLUSTER_MASK_CLIENT : CLUSTER_MASK_SERVER), |
| mfgCode, data, ATTRIBUTE_LARGEST, NULL); |
| if (status == EMBER_ZCL_STATUS_UNSUPPORTED_ATTRIBUTE) |
| { |
| emberAfAttributesPrintln("Unsupported"); |
| } |
| else |
| { |
| uint16_t length; |
| if (emberAfIsStringAttributeType(metaData->attributeType)) |
| { |
| length = static_cast<uint16_t>(emberAfStringLength(data) + 1); |
| } |
| else if (emberAfIsLongStringAttributeType(metaData->attributeType)) |
| { |
| length = static_cast<uint16_t>(emberAfLongStringLength(data) + 2); |
| } |
| else |
| { |
| length = emberAfAttributeSize(metaData); |
| } |
| UNUSED_VAR(length); |
| emberAfAttributesPrintBuffer(data, length, true); |
| emberAfAttributesFlush(); |
| emberAfAttributeDecodeAndPrintCluster(cluster->clusterId, mfgCode); |
| } |
| } |
| } |
| emberAfAttributesFlush(); |
| } |
| } |
| |
| // given a clusterId and an attribute to read, this crafts the response |
| // and places it in the response buffer. Response is one of two items: |
| // 1) unsupported: [attrId:2] [status:1] |
| // 2) supported: [attrId:2] [status:1] [type:1] [data:n] |
| // |
| void emberAfRetrieveAttributeAndCraftResponse(EndpointId endpoint, ClusterId clusterId, AttributeId attrId, uint8_t mask, |
| uint16_t manufacturerCode, uint16_t readLength) |
| { |
| EmberAfStatus status; |
| uint8_t data[ATTRIBUTE_LARGEST]; |
| uint8_t dataType; |
| uint16_t dataLen; |
| |
| // account for at least one byte of data |
| if (readLength < 5) |
| { |
| return; |
| } |
| |
| emberAfAttributesPrintln("OTA READ: ep:%x cid:%2x attid:%2x msk:%x mfcode:%2x", endpoint, clusterId, attrId, mask, |
| manufacturerCode); |
| |
| // lookup the attribute in our table |
| status = emAfReadAttribute(endpoint, clusterId, attrId, mask, manufacturerCode, data, ATTRIBUTE_LARGEST, &dataType); |
| if (status == EMBER_ZCL_STATUS_SUCCESS) |
| { |
| dataLen = emberAfAttributeValueSize(clusterId, attrId, dataType, data); |
| if ((readLength - 4) < dataLen) |
| { // Not enough space for attribute. |
| return; |
| } |
| } |
| else |
| { |
| emberAfPutInt32uInResp(attrId); |
| emberAfPutStatusInResp(status); |
| emberAfAttributesPrintln("READ: clus %2x, attr %2x failed %x", clusterId, attrId, status); |
| emberAfAttributesFlush(); |
| return; |
| } |
| |
| // put attribute in least sig byte first |
| emberAfPutInt32uInResp(attrId); |
| |
| // attribute is found, so copy in the status and the data type |
| emberAfPutInt8uInResp(EMBER_ZCL_STATUS_SUCCESS); |
| emberAfPutInt8uInResp(dataType); |
| |
| if (dataLen < (EMBER_AF_RESPONSE_BUFFER_LEN - appResponseLength)) |
| { |
| #if (BIGENDIAN_CPU) |
| // strings go over the air as length byte and then in human |
| // readable format. These should not be flipped. Other attributes |
| // need to be flipped so they go little endian OTA |
| if (isThisDataTypeSentLittleEndianOTA(dataType)) |
| { |
| uint8_t i; |
| for (i = 0; i < dataLen; i++) |
| { |
| appResponseData[appResponseLength + i] = data[dataLen - i - 1]; |
| } |
| } |
| else |
| { |
| memmove(&(appResponseData[appResponseLength]), data, dataLen); |
| } |
| #else //(BIGENDIAN_CPU) |
| memmove(&(appResponseData[appResponseLength]), data, dataLen); |
| #endif //(BIGENDIAN_CPU) |
| // TODO: How do we know this does not overflow? |
| appResponseLength = static_cast<uint16_t>(appResponseLength + dataLen); |
| } |
| |
| emberAfAttributesPrintln("READ: clus %2x, attr %2x, dataLen: %x, OK", clusterId, attrId, dataLen); |
| emberAfAttributesFlush(); |
| } |
| |
| // This function appends the attribute report fields for the given endpoint, |
| // cluster, and attribute to the buffer starting at the index. If there is |
| // insufficient space in the buffer or an error occurs, buffer and bufIndex will |
| // remain unchanged. Otherwise, bufIndex will be incremented appropriately and |
| // the fields will be written to the buffer. |
| EmberAfStatus emberAfAppendAttributeReportFields(EndpointId endpoint, ClusterId clusterId, AttributeId attributeId, uint8_t mask, |
| uint8_t * buffer, uint8_t bufLen, uint8_t * bufIndex) |
| { |
| EmberAfStatus status; |
| EmberAfAttributeType type; |
| uint16_t size; |
| uint16_t bufLen16 = (uint16_t) bufLen; |
| uint8_t data[ATTRIBUTE_LARGEST]; |
| |
| status = emberAfReadAttribute(endpoint, clusterId, attributeId, mask, data, sizeof(data), &type); |
| if (status != EMBER_ZCL_STATUS_SUCCESS) |
| { |
| goto kickout; |
| } |
| |
| size = emberAfAttributeValueSize(clusterId, attributeId, type, data); |
| if (bufLen16 - *bufIndex < 3 || size > bufLen16 - (*bufIndex + 3)) |
| { |
| status = EMBER_ZCL_STATUS_INSUFFICIENT_SPACE; |
| goto kickout; |
| } |
| |
| buffer[(*bufIndex)++] = EMBER_LOW_BYTE(attributeId); |
| buffer[(*bufIndex)++] = EMBER_HIGH_BYTE(attributeId); |
| buffer[(*bufIndex)++] = type; |
| #if (BIGENDIAN_CPU) |
| if (isThisDataTypeSentLittleEndianOTA(type)) |
| { |
| emberReverseMemCopy(buffer + *bufIndex, data, size); |
| } |
| else |
| { |
| memmove(buffer + *bufIndex, data, size); |
| } |
| #else |
| memmove(buffer + *bufIndex, data, size); |
| #endif |
| *bufIndex = static_cast<uint8_t>(*bufIndex + size); |
| |
| kickout: |
| emberAfAttributesPrintln("REPORT: clus 0x%2x, attr 0x%2x: 0x%x", clusterId, attributeId, status); |
| emberAfAttributesFlush(); |
| |
| return status; |
| } |
| |
| //------------------------------------------------------------------------------ |
| // Internal Functions |
| |
| // writes an attribute (identified by clusterID and attrID to the given value. |
| // this returns: |
| // - EMBER_ZCL_STATUS_UNSUPPORTED_ATTRIBUTE: if attribute isnt supported by the device (the |
| // device is not found in the attribute table) |
| // - EMBER_ZCL_STATUS_INVALID_DATA_TYPE: if the data type passed in doesnt match the type |
| // stored in the attribute table |
| // - EMBER_ZCL_STATUS_READ_ONLY: if the attribute isnt writable |
| // - EMBER_ZCL_STATUS_INVALID_VALUE: if the value is set out of the allowable range for |
| // the attribute |
| // - EMBER_ZCL_STATUS_SUCCESS: if the attribute was found and successfully written |
| // |
| // if true is passed in for overrideReadOnlyAndDataType then the data type is |
| // not checked and the read-only flag is ignored. This mode is meant for |
| // testing or setting the initial value of the attribute on the device. |
| // |
| // if true is passed for justTest, then the type is not written but all |
| // checks are done to see if the type could be written |
| // reads the attribute specified, returns false if the attribute is not in |
| // the table or the data is too large, returns true and writes to dataPtr |
| // if the attribute is supported and the readLength specified is less than |
| // the length of the data. |
| EmberAfStatus emAfWriteAttribute(EndpointId endpoint, ClusterId cluster, AttributeId attributeID, uint8_t mask, |
| uint16_t manufacturerCode, uint8_t * data, EmberAfAttributeType dataType, |
| bool overrideReadOnlyAndDataType, bool justTest) |
| { |
| EmberAfAttributeMetadata * metadata = NULL; |
| EmberAfAttributeSearchRecord record; |
| record.endpoint = endpoint; |
| record.clusterId = cluster; |
| record.clusterMask = mask; |
| record.attributeId = attributeID; |
| record.manufacturerCode = manufacturerCode; |
| emAfReadOrWriteAttribute(&record, &metadata, |
| NULL, // buffer |
| 0, // buffer size |
| false); // write? |
| |
| // if we dont support that attribute |
| if (metadata == NULL) |
| { |
| emberAfAttributesPrintln("%pep %x clus %2x attr %2x not supported", "WRITE ERR: ", endpoint, cluster, attributeID); |
| emberAfAttributesFlush(); |
| return EMBER_ZCL_STATUS_UNSUPPORTED_ATTRIBUTE; |
| } |
| |
| // if the data type specified by the caller is incorrect |
| if (!(overrideReadOnlyAndDataType)) |
| { |
| if (dataType != metadata->attributeType) |
| { |
| emberAfAttributesPrintln("%pinvalid data type", "WRITE ERR: "); |
| emberAfAttributesFlush(); |
| return EMBER_ZCL_STATUS_INVALID_DATA_TYPE; |
| } |
| |
| if (emberAfAttributeIsReadOnly(metadata)) |
| { |
| emberAfAttributesPrintln("%pattr not writable", "WRITE ERR: "); |
| emberAfAttributesFlush(); |
| return EMBER_ZCL_STATUS_READ_ONLY; |
| } |
| } |
| |
| // if the value the attribute is being set to is out of range |
| // return EMBER_ZCL_STATUS_INVALID_VALUE |
| if ((metadata->mask & ATTRIBUTE_MASK_MIN_MAX) != 0U) |
| { |
| EmberAfDefaultAttributeValue minv = metadata->defaultValue.ptrToMinMaxValue->minValue; |
| EmberAfDefaultAttributeValue maxv = metadata->defaultValue.ptrToMinMaxValue->maxValue; |
| bool isAttributeSigned = emberAfIsTypeSigned(metadata->attributeType); |
| uint16_t dataLen = emberAfAttributeSize(metadata); |
| if (dataLen <= 2) |
| { |
| int8_t minR, maxR; |
| uint8_t * minI = (uint8_t *) &(minv.defaultValue); |
| uint8_t * maxI = (uint8_t *) &(maxv.defaultValue); |
| // On big endian cpu with length 1 only the second byte counts |
| #if (BIGENDIAN_CPU) |
| if (dataLen == 1) |
| { |
| minI++; |
| maxI++; |
| } |
| #endif // BIGENDIAN_CPU |
| minR = emberAfCompareValues(minI, data, dataLen, isAttributeSigned); |
| maxR = emberAfCompareValues(maxI, data, dataLen, isAttributeSigned); |
| if ((minR == 1) || (maxR == -1)) |
| { |
| return EMBER_ZCL_STATUS_INVALID_VALUE; |
| } |
| } |
| else |
| { |
| if ((emberAfCompareValues(minv.ptrToDefaultValue, data, dataLen, isAttributeSigned) == 1) || |
| (emberAfCompareValues(maxv.ptrToDefaultValue, data, dataLen, isAttributeSigned) == -1)) |
| { |
| return EMBER_ZCL_STATUS_INVALID_VALUE; |
| } |
| } |
| } |
| |
| // write the data unless this is only a test |
| if (!justTest) |
| { |
| // Pre write attribute callback for all attribute changes, |
| // regardless of cluster. |
| EmberAfStatus status = emberAfPreAttributeChangeCallback(endpoint, cluster, attributeID, mask, manufacturerCode, dataType, |
| emberAfAttributeSize(metadata), data); |
| if (status != EMBER_ZCL_STATUS_SUCCESS) |
| { |
| return status; |
| } |
| |
| // Pre-write attribute callback specific |
| // to the cluster that the attribute lives in. |
| status = emAfClusterPreAttributeChangedCallback(endpoint, cluster, attributeID, mask, manufacturerCode, dataType, |
| emberAfAttributeSize(metadata), data); |
| if (status != EMBER_ZCL_STATUS_SUCCESS) |
| { |
| return status; |
| } |
| |
| // write the attribute |
| status = emAfReadOrWriteAttribute(&record, |
| NULL, // metadata |
| data, |
| 0, // buffer size - unused |
| true); // write? |
| |
| if (status != EMBER_ZCL_STATUS_SUCCESS) |
| { |
| return status; |
| } |
| |
| // Save the attribute to token if needed |
| // Function itself will weed out tokens that are not tokenized. |
| emAfSaveAttributeToToken(data, endpoint, cluster, metadata); |
| |
| InteractionModelReportingAttributeChangeCallback(endpoint, cluster, attributeID, mask, manufacturerCode, dataType, data); |
| |
| // Post write attribute callback for all attributes changes, regardless |
| // of cluster. |
| emberAfPostAttributeChangeCallback(endpoint, cluster, attributeID, mask, manufacturerCode, dataType, |
| emberAfAttributeSize(metadata), data); |
| |
| // Post-write attribute callback specific |
| // to the cluster that the attribute lives in. |
| emAfClusterAttributeChangedCallback(endpoint, cluster, attributeID, mask, manufacturerCode); |
| } |
| else |
| { |
| // bug: 11618, we are not handling properly external attributes |
| // in this case... We need to do something. We don't really |
| // know if it will succeed. |
| emberAfAttributesPrintln("WRITE: no write, just a test"); |
| emberAfAttributesFlush(); |
| } |
| |
| return EMBER_ZCL_STATUS_SUCCESS; |
| } |
| |
| // If dataPtr is NULL, no data is copied to the caller. |
| // readLength should be 0 in that case. |
| |
| EmberAfStatus emAfReadAttribute(EndpointId endpoint, ClusterId cluster, AttributeId attributeID, uint8_t mask, |
| uint16_t manufacturerCode, uint8_t * dataPtr, uint16_t readLength, EmberAfAttributeType * dataType) |
| { |
| EmberAfAttributeMetadata * metadata = NULL; |
| EmberAfAttributeSearchRecord record; |
| EmberAfStatus status; |
| record.endpoint = endpoint; |
| record.clusterId = cluster; |
| record.clusterMask = mask; |
| record.attributeId = attributeID; |
| record.manufacturerCode = manufacturerCode; |
| status = emAfReadOrWriteAttribute(&record, &metadata, dataPtr, readLength, |
| false); // write? |
| |
| if (status == EMBER_ZCL_STATUS_SUCCESS) |
| { |
| // It worked! If the user asked for the type, set it before returning. |
| if (dataType != NULL) |
| { |
| (*dataType) = metadata->attributeType; |
| } |
| } |
| else |
| { // failed, print debug info |
| if (status == EMBER_ZCL_STATUS_INSUFFICIENT_SPACE) |
| { |
| emberAfAttributesPrintln("READ: attribute size too large for caller"); |
| emberAfAttributesFlush(); |
| } |
| } |
| |
| return status; |
| } |