| /** |
| * |
| * 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 function that processes |
| *global ZCL message. |
| ******************************************************************************* |
| ******************************************************************************/ |
| |
| #include "af.h" |
| #include "common.h" |
| |
| #include <app/clusters/ias-zone-client/ias-zone-client.h> |
| |
| #ifdef EMBER_AF_PLUGIN_REPORTING |
| #include <app/reporting/reporting.h> |
| #endif // EMBER_AF_PLUGIN_REPORTING |
| |
| #include "gen/attribute-id.h" |
| #include "gen/attribute-type.h" |
| #include "gen/callback.h" |
| #include "gen/cluster-id.h" |
| #include "gen/command-id.h" |
| |
| #ifdef EMBER_AF_PLUGIN_COMMS_HUB_FUNCTION_SUB_GHZ |
| #include "app/framework/plugin/comms-hub-function-sub-ghz/comms-hub-function-sub-ghz.h" |
| #endif |
| |
| #include <support/CodeUtils.h> |
| |
| using namespace chip; |
| |
| // flag to keep track of the fact that we just sent a read attr for time and |
| // we should set our time to the result of the read attr response. |
| bool emAfSyncingTime = false; |
| |
| #ifdef EMBER_AF_GBCS_COMPATIBLE |
| // Some GBCS use cases (e.g. GCS15e, GCS21f) require that ReadAttributesResponse |
| // should be send back with Disable Default Response flag set. The only pattern |
| // is that the decision is based on the cluster and attribute IDs requested. |
| // To reduce the possibility of false positives, we disable default response |
| // only for responses containing at least the specified minimum of attributes. |
| #define MIN_MATCHING_ATTR_IDS_TO_DISABLE_DEFAULT_RESPONSE 3 |
| #endif |
| |
| #define DISC_ATTR_RSP_MAX_ATTRIBUTES \ |
| (((EMBER_AF_MAXIMUM_APS_PAYLOAD_LENGTH - EMBER_AF_ZCL_MANUFACTURER_SPECIFIC_OVERHEAD /* max ZCL header size */ \ |
| - 1) /* discovery is complete boolean */ \ |
| / 3) /* size of one discover attributes response entry */ \ |
| % UINT8_MAX) /* make count fit in an 8 bit integer */ |
| #define DISC_ATTR_EXT_RSP_MAX_ATTRIBUTES \ |
| (((EMBER_AF_MAXIMUM_APS_PAYLOAD_LENGTH - EMBER_AF_ZCL_MANUFACTURER_SPECIFIC_OVERHEAD /* max ZCL header size */ \ |
| - 1) /* discovery is complete boolean */ \ |
| / 4) /* size of one discover attributes extended response entry */ \ |
| % UINT8_MAX) /* make count fit in an 8 bit integer */ |
| |
| #if defined(EMBER_AF_SUPPORT_COMMAND_DISCOVERY) |
| static void printDiscoverCommandsResponse(bool generated, ClusterId clusterId, bool discoveryComplete, uint8_t * buffer, |
| uint16_t length) |
| { |
| uint16_t i; |
| emberAfServiceDiscoveryPrint("Discover Commands response (complete: %c), %p IDs: ", (discoveryComplete ? 'y' : 'n'), |
| (generated ? "Generated" : "Received")); |
| for (i = 0; i < length; i++) |
| { |
| emberAfServiceDiscoveryPrint("0x%X ", buffer[i]); |
| } |
| emberAfServiceDiscoveryPrintln(""); |
| } |
| #endif |
| |
| bool emAfProcessGlobalCommand(EmberAfClusterCommand * cmd) |
| { |
| AttributeId attrId; |
| uint8_t frameControl; |
| // This is a little clumsy but easier to read and port |
| // from earlier implementation. |
| ClusterId clusterId = cmd->apsFrame->clusterId; |
| CommandId zclCmd = cmd->commandId; |
| uint8_t * message = cmd->buffer; |
| uint16_t msgLen = cmd->bufLen; |
| uint16_t msgIndex = cmd->payloadStartIndex; |
| uint8_t clientServerMask = (cmd->direction == ZCL_DIRECTION_CLIENT_TO_SERVER ? CLUSTER_MASK_SERVER : CLUSTER_MASK_CLIENT); |
| |
| // If we are disabled then we can only respond to read or write commands |
| // or identify cluster (see device enabled attr of basic cluster) |
| if (!emberAfIsDeviceEnabled(cmd->apsFrame->destinationEndpoint) && zclCmd != ZCL_READ_ATTRIBUTES_COMMAND_ID && |
| zclCmd != ZCL_WRITE_ATTRIBUTES_COMMAND_ID && zclCmd != ZCL_WRITE_ATTRIBUTES_UNDIVIDED_COMMAND_ID && |
| zclCmd != ZCL_WRITE_ATTRIBUTES_NO_RESPONSE_COMMAND_ID && clusterId != ZCL_IDENTIFY_CLUSTER_ID) |
| { |
| emberAfCorePrintln("disabled"); |
| emberAfDebugPrintln("%pd, dropping global cmd:%x", "disable", zclCmd); |
| emberAfSendDefaultResponse(cmd, EMBER_ZCL_STATUS_FAILURE); |
| return true; |
| } |
| |
| // If a manufacturer-specific command arrives using our special internal "not |
| // manufacturer specific" code, we need to reject it outright without letting |
| // it pass through to the rest of the code. The internal read and write APIs |
| // would interpret it as a standard attribute or cluster and return incorrect |
| // results. |
| if (cmd->mfgSpecific && cmd->mfgCode == EMBER_AF_NULL_MANUFACTURER_CODE) |
| { |
| goto kickout; |
| } |
| |
| // Clear out the response buffer by setting its length to zero |
| appResponseLength = 0; |
| |
| // Make the ZCL header for the response |
| // note: cmd byte is set below |
| frameControl = static_cast<uint8_t>(ZCL_GLOBAL_COMMAND | |
| (cmd->direction == ZCL_DIRECTION_CLIENT_TO_SERVER |
| ? ZCL_FRAME_CONTROL_SERVER_TO_CLIENT | EMBER_AF_DEFAULT_RESPONSE_POLICY_RESPONSES |
| : ZCL_FRAME_CONTROL_CLIENT_TO_SERVER | EMBER_AF_DEFAULT_RESPONSE_POLICY_RESPONSES)); |
| if (cmd->mfgSpecific) |
| { |
| frameControl |= ZCL_MANUFACTURER_SPECIFIC_MASK; |
| } |
| emberAfPutInt8uInResp(frameControl); |
| if (cmd->mfgSpecific) |
| { |
| emberAfPutInt16uInResp(cmd->mfgCode); |
| } |
| emberAfPutInt8uInResp(cmd->seqNum); |
| |
| switch (zclCmd) |
| { |
| // The format of the read attributes cmd is: |
| // ([attr ID:2]) * N |
| // The format of the read attributes response is: |
| // ([attr ID:2] [status:1] [data type:0/1] [data:0/N]) * N |
| case ZCL_READ_ATTRIBUTES_COMMAND_ID: { |
| emberAfAttributesPrintln("%p: clus %2x", "READ_ATTR", clusterId); |
| // Set the cmd byte - this is byte 3 index 2, but since we have |
| // already incremented past the 3 byte ZCL header (our index is at 3), |
| // this gets written to "-1" since 3 - 1 = 2. |
| emberAfPutInt8uInResp(ZCL_READ_ATTRIBUTES_RESPONSE_COMMAND_ID); |
| |
| // This message contains N 2-byte attr IDs after the 3 byte ZCL header, |
| // for each one we need to look it up and make a response |
| while (msgIndex + 2 <= msgLen) |
| { |
| // Get the attribute ID and store it in the response buffer |
| // least significant byte is first OTA |
| attrId = emberAfGetInt16u(message, msgIndex, msgLen); |
| |
| #ifdef EMBER_AF_GBCS_COMPATIBLE |
| // GBCS explicitly lists some commands that need to be sent with "disable |
| // default response" flag set, including some ReadAttributes responses. |
| // We make it conditional on GBCS so it does not affect standard SE apps. |
| { |
| static const struct |
| { |
| ClusterId clusterId; |
| AttributeId attrId; |
| } noDefaultResponseSet[] = { |
| { ZCL_PRICE_CLUSTER_ID, ZCL_THRESHOLD_MULTIPLIER_ATTRIBUTE_ID }, |
| { ZCL_PRICE_CLUSTER_ID, ZCL_THRESHOLD_DIVISOR_ATTRIBUTE_ID }, |
| { ZCL_PRICE_CLUSTER_ID, ZCL_STANDING_CHARGE_ATTRIBUTE_ID }, |
| { ZCL_PRICE_CLUSTER_ID, ZCL_TARIFF_UNIT_OF_MEASURE_ATTRIBUTE_ID }, |
| { ZCL_SIMPLE_METERING_CLUSTER_ID, ZCL_UNIT_OF_MEASURE_ATTRIBUTE_ID }, |
| { ZCL_SIMPLE_METERING_CLUSTER_ID, ZCL_MULTIPLIER_ATTRIBUTE_ID }, |
| { ZCL_SIMPLE_METERING_CLUSTER_ID, ZCL_DIVISOR_ATTRIBUTE_ID }, |
| }; |
| uint8_t i; |
| uint8_t foundMatchingAttrIdsCount = 0; |
| |
| for (i = 0; i < sizeof noDefaultResponseSet / sizeof noDefaultResponseSet[0]; ++i) |
| { |
| if (noDefaultResponseSet[i].clusterId == clusterId && noDefaultResponseSet[i].attrId == attrId) |
| { |
| if (++foundMatchingAttrIdsCount >= MIN_MATCHING_ATTR_IDS_TO_DISABLE_DEFAULT_RESPONSE) |
| { |
| emberAfSetDisableDefaultResponse(EMBER_AF_DISABLE_DEFAULT_RESPONSE_ONE_SHOT); |
| break; |
| } |
| } |
| } |
| } |
| |
| #ifdef EMBER_AF_PLUGIN_COMMS_HUB_FUNCTION_SUB_GHZ |
| // This plugin sets channel change notification flags and needs to know |
| // when those flags have been read. |
| if (clientServerMask == CLUSTER_MASK_SERVER) |
| { |
| emAfCommsHubFunctionSubGhzReadAttributeNotification(cmd->source, clusterId, attrId); |
| } |
| #endif |
| #endif |
| |
| // This function reads the attribute and creates the correct response |
| // in the response buffer |
| emberAfRetrieveAttributeAndCraftResponse(cmd->apsFrame->destinationEndpoint, clusterId, attrId, clientServerMask, |
| cmd->mfgCode, |
| static_cast<uint16_t>(EMBER_AF_RESPONSE_BUFFER_LEN - appResponseLength)); |
| // Go to next attrID |
| msgIndex = static_cast<uint16_t>(msgIndex + 2); |
| } |
| } |
| |
| emberAfSendResponse(); |
| return true; |
| |
| // Write undivided means all attributes must be written in order to write |
| // any of them. So first do a check. If the check fails, send back a fail |
| // response. If it works, fall through to the normal write attr code. |
| // write attr responses are the same for undivided and normal writes. |
| case ZCL_WRITE_ATTRIBUTES_UNDIVIDED_COMMAND_ID: { |
| uint8_t numFailures = 0; |
| uint8_t dataType; |
| uint16_t dataSize; |
| EmberAfStatus status; |
| |
| emberAfPutInt8uInResp(ZCL_WRITE_ATTRIBUTES_RESPONSE_COMMAND_ID); |
| |
| // Go through the message until there are no more attrID/type/data |
| while (msgIndex < msgLen - 3) |
| { |
| attrId = emberAfGetInt16u(message, msgIndex, msgLen); |
| dataType = emberAfGetInt8u(message, msgIndex + 2, msgLen); |
| |
| dataSize = emberAfAttributeValueSize(dataType, message + msgIndex + 3); |
| |
| // Check to see if there are dataSize bytes left in the message if it is a string |
| if (emberAfIsThisDataTypeAStringType(dataType) && (dataSize < msgLen - (msgIndex + 3))) |
| { |
| // This command is malformed |
| status = EMBER_ZCL_STATUS_MALFORMED_COMMAND; |
| } |
| else |
| { |
| status = emberAfVerifyAttributeWrite(cmd->apsFrame->destinationEndpoint, clusterId, attrId, clientServerMask, |
| cmd->mfgCode, &(message[msgIndex + 3]), dataType); |
| } |
| |
| if (status != EMBER_ZCL_STATUS_SUCCESS) |
| { |
| numFailures++; |
| // Write to the response buffer - status and then attrID |
| emberAfPutInt8uInResp(status); |
| emberAfPutInt16uInResp(attrId); |
| |
| emberAfAttributesPrintln("WRITE: clus %2x attr %2x ", clusterId, attrId); |
| emberAfAttributesPrintln("FAIL %x", status); |
| emberAfCoreFlush(); |
| if (status == EMBER_ZCL_STATUS_MALFORMED_COMMAND) |
| { |
| // this attribute is malformed, terminate attribute processing. |
| break; |
| } |
| } |
| |
| // Increment past the attribute id (two bytes), the type (one byte), and |
| // the data (N bytes, including the length byte for strings). |
| msgIndex = static_cast<uint16_t>(msgIndex + 3 + dataSize); |
| } |
| // If there are any failures, send the response and exit |
| if (numFailures > 0) |
| { |
| emberAfSendResponse(); |
| return true; |
| } |
| } |
| // Reset message back to start |
| msgIndex = cmd->payloadStartIndex; |
| appResponseLength = (cmd->mfgSpecific ? 4 : 2); |
| FALLTHROUGH; |
| /* fall through */ |
| // DO NOT BREAK from this case |
| |
| // the format of the write attributes cmd is: |
| // ([attr ID:2] [data type:1] [data:N]) * N |
| // the format of the write attributes response is: |
| // ([status 1] [attr ID 2]) * n |
| // ONLY errors are reported unless all are successful then a single success |
| // is sent. write attr no response is handled by just executing the same |
| // code but not setting the flag that sends the response at the end. |
| case ZCL_WRITE_ATTRIBUTES_NO_RESPONSE_COMMAND_ID: |
| case ZCL_WRITE_ATTRIBUTES_COMMAND_ID: { |
| uint8_t numFailures = 0; |
| uint8_t numSuccess = 0; |
| uint8_t dataType; |
| uint16_t dataSize; |
| #if (BIGENDIAN_CPU) |
| uint8_t writeData[ATTRIBUTE_LARGEST]; |
| #endif //(BIGENDIAN_CPU) |
| EmberAfStatus status; |
| |
| // set the cmd byte - this is byte 3 index 2, but since we have |
| // already incremented past the 3 byte ZCL header (our index is at 3), |
| // this gets written to "-1" since 3 - 1 = 2. |
| emberAfPutInt8uInResp(ZCL_WRITE_ATTRIBUTES_RESPONSE_COMMAND_ID); |
| |
| // go through the message until there are no more attrID/type/data |
| while (msgLen > msgIndex + 3) |
| { |
| attrId = emberAfGetInt16u(message, msgIndex, msgLen); |
| dataType = emberAfGetInt8u(message, msgIndex + 2, msgLen); |
| |
| dataSize = emberAfAttributeValueSize(dataType, message + msgIndex + 3); |
| |
| // the data is sent little endian over-the-air, it needs to be |
| // inserted into the table big endian for the EM250 and little |
| // endian for the EZSP hosts. This means for the EM250 the data |
| // needs to be reversed before sending to writeAttributes |
| #if (BIGENDIAN_CPU) |
| if (dataSize <= msgLen - (msgIndex + 3) && dataSize <= ATTRIBUTE_LARGEST) |
| { |
| // strings go over the air as length byte and then in human |
| // readable format. These should not be flipped. |
| if (emberAfIsThisDataTypeAStringType(dataType)) |
| { |
| memmove(writeData, message + msgIndex + 3, dataSize); |
| } |
| else |
| { |
| // the data is sent little endian over-the-air, it needs to be |
| // inserted into the table big endian |
| uint16_t i; |
| for (i = 0; i < dataSize; i++) |
| { |
| writeData[i] = message[msgIndex + 3 + dataSize - i - 1]; |
| } |
| } |
| #else //(BIGENDIAN_CPU) |
| if (dataSize <= msgLen - (msgIndex + 3)) |
| { |
| #endif //(BIGENDIAN_CPU) |
| |
| status = emberAfWriteAttributeExternal(cmd->apsFrame->destinationEndpoint, clusterId, attrId, clientServerMask, |
| cmd->mfgCode, |
| #if (BIGENDIAN_CPU) |
| writeData, |
| #else //(BIGENDIAN_CPU) |
| &(message[msgIndex + 3]), |
| #endif //(BIGENDIAN_CPU) |
| dataType); |
| emberAfAttributesPrint("WRITE: clus %2x attr %2x ", clusterId, attrId); |
| if (status == EMBER_ZCL_STATUS_SUCCESS) |
| { |
| numSuccess++; |
| emberAfAttributesPrintln("OK"); |
| } |
| else |
| { |
| numFailures++; |
| // write to the response buffer - status and then attrID |
| emberAfPutInt8uInResp(status); |
| emberAfPutInt16uInResp(attrId); |
| emberAfAttributesPrintln("FAIL %x", status); |
| } |
| emberAfCoreFlush(); |
| |
| // Increment past the attribute id (two bytes), the type (one byte), and |
| // the data (N bytes, including the length byte for strings). |
| msgIndex = static_cast<uint16_t>(msgIndex + 3 + dataSize); |
| } |
| else |
| { |
| numFailures++; |
| status = EMBER_ZCL_STATUS_INVALID_VALUE; |
| // write to the response buffer - status and then attrID |
| emberAfPutInt8uInResp(status); |
| emberAfPutInt16uInResp(attrId); |
| emberAfAttributesPrintln("FAIL %x", status); |
| // size exceeds buffer, terminate loop |
| break; |
| } |
| } |
| |
| // always send a response unless the cmd requested no response |
| if (zclCmd == ZCL_WRITE_ATTRIBUTES_NO_RESPONSE_COMMAND_ID) |
| { |
| return true; |
| } |
| |
| if (numFailures == 0) |
| { |
| // if no failures and no success this means the packet was too short |
| // print an error message but still return true as we consumed the |
| // message |
| if (numSuccess == 0) |
| { |
| emberAfAttributesPrintln("WRITE: too short"); |
| emberAfSendDefaultResponse(cmd, EMBER_ZCL_STATUS_MALFORMED_COMMAND); |
| return true; |
| } |
| // if no failures and at least one success, write a success status |
| // that means everything worked |
| else |
| { |
| emberAfPutInt8uInResp(EMBER_ZCL_STATUS_SUCCESS); |
| } |
| } |
| emberAfSendResponse(); |
| return true; |
| } |
| |
| // the format of discover is: [start attr ID:2] [max attr IDs:1] |
| // the format of the response is: [done:1] ([attrID:2] [type:1]) * N |
| case ZCL_DISCOVER_ATTRIBUTES_COMMAND_ID: |
| case ZCL_DISCOVER_ATTRIBUTES_EXTENDED_COMMAND_ID: { |
| AttributeId startingAttributeId; |
| uint8_t numberAttributes; |
| uint8_t * complete; |
| |
| emberAfAttributesPrintln("%p%p: clus %2x", "DISC_ATTR", |
| (zclCmd == ZCL_DISCOVER_ATTRIBUTES_EXTENDED_COMMAND_ID ? "_EXT" : ""), clusterId); |
| |
| // set the cmd byte - this is byte 3 index 2, but since we have |
| // already incremented past the 3 byte ZCL header (our index is at 3), |
| // this gets written to "-1" since 3 - 1 = 2. |
| emberAfPutInt8uInResp((zclCmd == ZCL_DISCOVER_ATTRIBUTES_COMMAND_ID |
| ? ZCL_DISCOVER_ATTRIBUTES_RESPONSE_COMMAND_ID |
| : ZCL_DISCOVER_ATTRIBUTES_EXTENDED_RESPONSE_COMMAND_ID)); |
| |
| // get the attrId to start on and the max count |
| startingAttributeId = emberAfGetInt16u(message, msgIndex, msgLen); |
| numberAttributes = emberAfGetInt8u(message, msgIndex + 2, msgLen); |
| |
| // BUGZID: EMAPPFWKV2-828, EMAPPFWKV2-1401 |
| if (zclCmd == ZCL_DISCOVER_ATTRIBUTES_COMMAND_ID && numberAttributes > DISC_ATTR_RSP_MAX_ATTRIBUTES) |
| { |
| numberAttributes = DISC_ATTR_RSP_MAX_ATTRIBUTES; |
| } |
| else if (zclCmd == ZCL_DISCOVER_ATTRIBUTES_EXTENDED_COMMAND_ID && numberAttributes > DISC_ATTR_EXT_RSP_MAX_ATTRIBUTES) |
| { |
| numberAttributes = DISC_ATTR_EXT_RSP_MAX_ATTRIBUTES; |
| } |
| else |
| { |
| // MISRA requires ..else if.. to have terminating else. |
| } |
| |
| // The response has a one-byte field indicating whether discovery is |
| // complete. We can't populate that field until we've finished going |
| // through all the attributes, so save a placeholder, write a temporary |
| // value for now (so that the offset moves forward), and write the real |
| // value when we're done. |
| complete = &(appResponseData[appResponseLength]); |
| emberAfPutInt8uInResp(false); |
| *complete = emberAfReadSequentialAttributesAddToResponse(cmd->apsFrame->destinationEndpoint, clusterId, startingAttributeId, |
| clientServerMask, cmd->mfgCode, numberAttributes, |
| (zclCmd == ZCL_DISCOVER_ATTRIBUTES_EXTENDED_COMMAND_ID)); |
| emberAfSendResponse(); |
| return true; |
| } |
| |
| #ifdef EMBER_AF_PLUGIN_REPORTING |
| case ZCL_CONFIGURE_REPORTING_COMMAND_ID: |
| if (emberAfConfigureReportingCommandCallback(cmd)) |
| { |
| return true; |
| } |
| break; |
| |
| case ZCL_READ_REPORTING_CONFIGURATION_COMMAND_ID: |
| if (emberAfReadReportingConfigurationCommandCallback(cmd)) |
| { |
| return true; |
| } |
| break; |
| #endif // EMBER_AF_PLUGIN_REPORTING |
| |
| // ([attribute id:2] [status:1] [type:0/1] [value:0/V])+ |
| case ZCL_READ_ATTRIBUTES_RESPONSE_COMMAND_ID: |
| // The "timesync" command in the CLI sends a Read Attributes command for the |
| // Time attribute on another device and then sets a flag. If that flag is |
| // set and a Read Attributes Response command for the time comes in, we set |
| // the time to the value in the message. |
| if (clusterId == ZCL_TIME_CLUSTER_ID) |
| { |
| if (emAfSyncingTime && !cmd->mfgSpecific && msgLen - msgIndex == 8 // attr:2 status:1 type:1 data:4 |
| && (emberAfGetInt16u(message, msgIndex, msgLen) == ZCL_TIME_ATTRIBUTE_ID) && |
| (emberAfGetInt8u(message, msgIndex + 2, msgLen) == EMBER_ZCL_STATUS_SUCCESS) && |
| (emberAfGetInt8u(message, msgIndex + 3, msgLen) == ZCL_UTC_TIME_ATTRIBUTE_TYPE)) |
| { |
| // emberAfSetTime(emberAfGetInt32u(message, msgIndex + 4, msgLen)); |
| // emberAfDebugPrintln("time sync ok, time: %4x", emberAfGetCurrentTime()); |
| emAfSyncingTime = false; |
| } |
| #ifdef EMBER_AF_PLUGIN_SMART_ENERGY_REGISTRATION_TIME_SOURCE_REQUIRED |
| emAfPluginSmartEnergyRegistrationReadAttributesResponseCallback(message + msgIndex, msgLen - msgIndex); |
| #endif // EMBER_AF_PLUGIN_SMART_ENERGY_REGISTRATION_TIME_SOURCE_REQUIRED |
| #ifdef EMBER_AF_PLUGIN_WWAH_SERVER_SILABS |
| emAfPluginSlWwahReadAttributesResponseCallback(clusterId, message, msgLen); |
| #endif |
| } |
| |
| #ifdef EMBER_AF_PLUGIN_TRUST_CENTER_KEEPALIVE |
| if (clusterId == ZCL_KEEPALIVE_CLUSTER_ID && !cmd->mfgSpecific) |
| { |
| emAfPluginTrustCenterKeepaliveReadAttributesResponseCallback(message + msgIndex, msgLen - msgIndex); |
| } |
| #endif // EMBER_AF_PLUGIN_TRUST_CENTER_KEEPALIVE |
| |
| #if defined(EMBER_AF_PLUGIN_KEY_ESTABLISHMENT) |
| if (clusterId == ZCL_KEY_ESTABLISHMENT_CLUSTER_ID && !cmd->mfgSpecific && |
| msgLen - msgIndex == 6 // attr:2 status:1 type:1 data:2 |
| && (emberAfGetInt16u(message, msgIndex, msgLen) == ZCL_KEY_ESTABLISHMENT_SUITE_CLIENT_ATTRIBUTE_ID) && |
| (emberAfGetInt8u(message, msgIndex + 2, msgLen) == EMBER_ZCL_STATUS_SUCCESS) && |
| ((emberAfGetInt8u(message, msgIndex + 3, msgLen) == ZCL_ENUM16_ATTRIBUTE_TYPE) || |
| (emberAfGetInt8u(message, msgIndex + 3, msgLen) == ZCL_BITMAP16_ATTRIBUTE_TYPE))) |
| { |
| uint16_t suite = emberAfGetInt16u(message, msgIndex + 4, msgLen); |
| emberAfPluginKeyEstablishmentReadAttributesCallback(suite); |
| } |
| #endif |
| |
| #if defined(EMBER_AF_PLUGIN_TEST_HARNESS) |
| emberAfPluginTestHarnessReadAttributesResponseCallback(clusterId, message + msgIndex, msgLen - msgIndex); |
| #endif |
| |
| #if defined(EMBER_AF_PLUGIN_IAS_ZONE_CLIENT) |
| emberAfPluginIasZoneClientReadAttributesResponseCallback(clusterId, message + msgIndex, |
| static_cast<uint16_t>(msgLen - msgIndex)); |
| #endif |
| |
| #if defined(EMBER_AF_PLUGIN_SIMPLE_METERING_SERVER) |
| emberAfPluginSimpleMeteringClusterReadAttributesResponseCallback(clusterId, message + msgIndex, |
| static_cast<uint16_t>(msgLen - msgIndex)); |
| #endif |
| |
| if (!emberAfReadAttributesResponseCallback(clusterId, message + msgIndex, static_cast<uint16_t>(msgLen - msgIndex))) |
| { |
| emberAfSendDefaultResponse(cmd, EMBER_ZCL_STATUS_SUCCESS); |
| } |
| return true; |
| |
| // ([status:1] [attribute id:2])+ |
| case ZCL_WRITE_ATTRIBUTES_RESPONSE_COMMAND_ID: |
| |
| #if defined(EMBER_AF_PLUGIN_TEST_HARNESS) |
| emberAfPluginTestHarnessWriteAttributesResponseCallback(clusterId, message + msgIndex, |
| static_cast<uint16_t>(msgLen - msgIndex)); |
| #endif |
| |
| #if defined(EMBER_AF_PLUGIN_IAS_ZONE_CLIENT) |
| emberAfPluginIasZoneClientWriteAttributesResponseCallback(clusterId, message + msgIndex, |
| static_cast<uint16_t>(msgLen - msgIndex)); |
| #endif |
| |
| if (!emberAfWriteAttributesResponseCallback(clusterId, message + msgIndex, static_cast<uint16_t>(msgLen - msgIndex))) |
| { |
| emberAfSendDefaultResponse(cmd, EMBER_ZCL_STATUS_SUCCESS); |
| } |
| return true; |
| |
| #ifdef EMBER_AF_PLUGIN_REPORTING |
| // ([status:1] [direction:1] [attribute id:2])+ |
| case ZCL_CONFIGURE_REPORTING_RESPONSE_COMMAND_ID: |
| if (!emberAfConfigureReportingResponseCallback(clusterId, message + msgIndex, static_cast<uint16_t>(msgLen - msgIndex))) |
| { |
| emberAfSendDefaultResponse(cmd, EMBER_ZCL_STATUS_SUCCESS); |
| } |
| return true; |
| |
| // ([status:1] [direction:1] [attribute id:2] [type:0/1] ... |
| // ... [min interval:0/2] [max interval:0/2] [reportable change:0/V] ... |
| // ... [timeout:0/2])+ |
| case ZCL_READ_REPORTING_CONFIGURATION_RESPONSE_COMMAND_ID: |
| if (!emberAfReadReportingConfigurationResponseCallback(clusterId, message + msgIndex, |
| static_cast<uint16_t>(msgLen - msgIndex))) |
| { |
| emberAfSendDefaultResponse(cmd, EMBER_ZCL_STATUS_SUCCESS); |
| } |
| return true; |
| #endif // EMBER_AF_PLUGIN_REPORTING |
| |
| // ([attribute id:2] [type:1] [data:V])+ |
| case ZCL_REPORT_ATTRIBUTES_COMMAND_ID: |
| if (!emberAfReportAttributesCallback(clusterId, message + msgIndex, static_cast<uint16_t>(msgLen - msgIndex))) |
| { |
| emberAfSendDefaultResponse(cmd, EMBER_ZCL_STATUS_SUCCESS); |
| } |
| return true; |
| |
| // [command id:1] [status:1] |
| case ZCL_DEFAULT_RESPONSE_COMMAND_ID: { |
| EmberAfStatus status; |
| CommandId commandId; |
| commandId = emberAfGetInt8u(message, msgIndex, msgLen); |
| msgIndex++; |
| status = (EmberAfStatus) emberAfGetInt8u(message, msgIndex, msgLen); |
| |
| emberAfClusterDefaultResponseWithMfgCodeCallback(cmd->apsFrame->destinationEndpoint, clusterId, commandId, status, |
| clientServerMask, cmd->mfgCode); |
| emberAfDefaultResponseCallback(clusterId, commandId, status); |
| return true; |
| } |
| |
| // [discovery complete:1] ([attribute id:2] [type:1])* |
| case ZCL_DISCOVER_ATTRIBUTES_RESPONSE_COMMAND_ID: |
| case ZCL_DISCOVER_ATTRIBUTES_EXTENDED_RESPONSE_COMMAND_ID: { |
| bool discoveryComplete = emberAfGetInt8u(message, msgIndex, msgLen); |
| msgIndex++; |
| if (!emberAfDiscoverAttributesResponseCallback(clusterId, discoveryComplete, message + msgIndex, |
| static_cast<uint16_t>(msgLen - msgIndex), |
| (zclCmd == ZCL_DISCOVER_ATTRIBUTES_EXTENDED_RESPONSE_COMMAND_ID))) |
| { |
| emberAfSendDefaultResponse(cmd, EMBER_ZCL_STATUS_SUCCESS); |
| } |
| return true; |
| } |
| |
| #ifdef EMBER_AF_SUPPORT_COMMAND_DISCOVERY |
| // Command discovery takes a bit of flash because we need to add structs |
| // for commands into the generated hader. Hence it's all configurable. |
| case ZCL_DISCOVER_COMMANDS_RECEIVED_COMMAND_ID: |
| case ZCL_DISCOVER_COMMANDS_GENERATED_COMMAND_ID: { |
| uint8_t startCommandIdentifier = emberAfGetInt8u(message, msgIndex, msgLen); |
| uint8_t maximumCommandIdentifiers = emberAfGetInt8u(message, msgIndex + 1, msgLen); |
| uint16_t savedIndex; |
| bool flag; |
| |
| // Ok. This is the command that matters. |
| if (zclCmd == ZCL_DISCOVER_COMMANDS_RECEIVED_COMMAND_ID) |
| { |
| emberAfPutInt8uInResp(ZCL_DISCOVER_COMMANDS_RECEIVED_RESPONSE_COMMAND_ID); |
| flag = false; |
| } |
| else |
| { |
| emberAfPutInt8uInResp(ZCL_DISCOVER_COMMANDS_GENERATED_RESPONSE_COMMAND_ID); |
| flag = true; |
| } |
| savedIndex = appResponseLength; |
| flag = emberAfExtractCommandIds(flag, cmd, clusterId, appResponseData + appResponseLength + 1, |
| static_cast<uint16_t>(EMBER_AF_RESPONSE_BUFFER_LEN - appResponseLength - 1), |
| &appResponseLength, startCommandIdentifier, maximumCommandIdentifiers); |
| appResponseData[savedIndex] = (flag ? 1 : 0); |
| appResponseLength++; |
| emberAfSendResponse(); |
| return true; |
| } |
| case ZCL_DISCOVER_COMMANDS_RECEIVED_RESPONSE_COMMAND_ID: { |
| bool discoveryComplete = emberAfGetInt8u(message, msgIndex, msgLen); |
| msgIndex++; |
| if (msgIndex <= msgLen) |
| { |
| printDiscoverCommandsResponse(false, // is ZCL command generated? |
| clusterId, discoveryComplete, message + msgIndex, |
| static_cast<uint16_t>(msgLen - msgIndex)); |
| if (!emberAfDiscoverCommandsReceivedResponseCallback(clusterId, cmd->mfgCode, discoveryComplete, message + msgIndex, |
| static_cast<uint16_t>(msgLen - msgIndex))) |
| { |
| emberAfSendDefaultResponse(cmd, EMBER_ZCL_STATUS_SUCCESS); |
| } |
| return true; |
| } |
| else |
| { |
| return false; |
| } |
| } |
| case ZCL_DISCOVER_COMMANDS_GENERATED_RESPONSE_COMMAND_ID: { |
| bool discoveryComplete = emberAfGetInt8u(message, msgIndex, msgLen); |
| msgIndex++; |
| if (msgIndex <= msgLen) |
| { |
| printDiscoverCommandsResponse(true, // is ZCL command generated? |
| clusterId, discoveryComplete, message + msgIndex, |
| static_cast<uint16_t>(msgLen - msgIndex)); |
| if (!emberAfDiscoverCommandsGeneratedResponseCallback(clusterId, cmd->mfgCode, discoveryComplete, message + msgIndex, |
| static_cast<uint16_t>(msgLen - msgIndex))) |
| { |
| emberAfSendDefaultResponse(cmd, EMBER_ZCL_STATUS_SUCCESS); |
| } |
| return true; |
| } |
| else |
| { |
| return false; |
| } |
| } |
| |
| #endif |
| default: |
| // MISRA requires default case. |
| break; |
| } |
| |
| kickout: |
| emberAfSendDefaultResponse( |
| cmd, (cmd->mfgSpecific ? EMBER_ZCL_STATUS_UNSUP_MANUF_GENERAL_COMMAND : EMBER_ZCL_STATUS_UNSUP_GENERAL_COMMAND)); |
| return true; |
| } |