blob: 7d4c148aad583bddaf595e3f0cf1e3400444fdf7 [file] [log] [blame]
/**
*
* 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;
}