blob: d0b3783754024092280eae325f72650faf031fb2 [file] [log] [blame]
/**
*
* Copyright (c) 2020-2021 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 all of the common ZCL
*command and attribute handling code for Ember's ZCL
*implementation
*******************************************************************************
******************************************************************************/
#include "af-event.h"
#include "af-main.h"
#include "af.h"
#include "common.h"
#include "gen/attribute-id.h"
#include "gen/attribute-type.h"
#include "gen/callback.h"
#include "gen/cluster-id.h"
#include "gen/command-id.h"
#include "gen/print-cluster.h"
#ifdef EMBER_AF_PLUGIN_GROUPS_SERVER
#include <app/clusters/groups-server/groups-server.h>
#endif // EMBER_AF_PLUGIN_GROUPS_SERVER
#ifdef EMBER_AF_PLUGIN_REPORTING
#include <app/reporting/reporting.h>
#endif // EMBER_AF_PLUGIN_REPORTING
using namespace chip;
//------------------------------------------------------------------------------
// Forward Declarations
//------------------------------------------------------------------------------
// Globals
// Storage and functions for turning on and off devices
bool afDeviceEnabled[MAX_ENDPOINT_COUNT];
#ifdef EMBER_AF_ENABLE_STATISTICS
// a variable containing the number of messages send from the utilities
// since emberAfInit was called.
uint32_t afNumPktsSent;
#endif
const EmberAfClusterName zclClusterNames[] = {
CLUSTER_IDS_TO_NAMES // defined in print-cluster.h
{ ZCL_NULL_CLUSTER_ID, EMBER_AF_NULL_MANUFACTURER_CODE, NULL }, // terminator
};
static const EmberAfClusterCommand staticCmd = {};
EmberAfClusterCommand curCmd;
// A pointer to the current command being processed
// This struct is allocated on the stack inside
// emberAfProcessMessage. The pointer below is set
// to NULL when the function exits.
EmberAfClusterCommand * emAfCurrentCommand;
// DEPRECATED.
uint8_t emberAfIncomingZclSequenceNumber = 0xFF;
// Sequence used for outgoing messages if they are
// not responses.
uint8_t emberAfSequenceNumber = 0xFF;
// A bool value so we know when the device is performing
// key establishment.
bool emAfDeviceIsPerformingKeyEstablishment = false;
static uint8_t /*enum EmberAfRetryOverride*/ emberAfApsRetryOverride = EMBER_AF_RETRY_OVERRIDE_NONE;
static uint8_t /*enum EmberAfDisableDefaultResponse*/ emAfDisableDefaultResponse = EMBER_AF_DISABLE_DEFAULT_RESPONSE_NONE;
static uint8_t /*enum EmberAfDisableDefaultResponse*/ emAfSavedDisableDefaultResponseVale = EMBER_AF_DISABLE_DEFAULT_RESPONSE_NONE;
// Holds the response type
uint8_t emberAfResponseType = ZCL_UTIL_RESP_NORMAL;
static EmberAfInterpanHeader interpanResponseHeader;
static const uint8_t emberAfAnalogDiscreteThresholds[] = { 0x07, EMBER_AF_DATA_TYPE_NONE, 0x1F, EMBER_AF_DATA_TYPE_DISCRETE,
0x2F, EMBER_AF_DATA_TYPE_ANALOG, 0x37, EMBER_AF_DATA_TYPE_DISCRETE,
0x3F, EMBER_AF_DATA_TYPE_ANALOG, 0x57, EMBER_AF_DATA_TYPE_DISCRETE,
0xDF, EMBER_AF_DATA_TYPE_NONE, 0xE7, EMBER_AF_DATA_TYPE_ANALOG,
0xFF, EMBER_AF_DATA_TYPE_NONE };
uint8_t emAfExtendedPanId[EXTENDED_PAN_ID_SIZE] = {
0, 0, 0, 0, 0, 0, 0, 0,
};
#ifdef EMBER_AF_PLUGIN_TEMPERATURE_MEASUREMENT_SERVER
void emberAfPluginTemperatureMeasurementServerInitCallback(void);
#endif
#ifdef EMBER_AF_PLUGIN_BARRIER_CONTROL_SERVER
void emberAfPluginBarrierControlServerInitCallback(void);
#endif
#ifdef EMBER_AF_PLUGIN_DOOR_LOCK_SERVER
void emberAfPluginDoorLockServerInitCallback(void);
#endif
#ifdef EMBER_AF_GENERATED_PLUGIN_TICK_FUNCTION_DECLARATIONS
EMBER_AF_GENERATED_PLUGIN_TICK_FUNCTION_DECLARATIONS
#endif
//------------------------------------------------------------------------------
// Device enabled/disabled functions
bool emberAfIsDeviceEnabled(EndpointId endpoint)
{
uint8_t index;
#ifdef ZCL_USING_BASIC_CLUSTER_DEVICE_ENABLED_ATTRIBUTE
bool deviceEnabled;
if (emberAfReadServerAttribute(endpoint, ZCL_BASIC_CLUSTER_ID, ZCL_DEVICE_ENABLED_ATTRIBUTE_ID, (uint8_t *) &deviceEnabled,
sizeof(deviceEnabled)) == EMBER_ZCL_STATUS_SUCCESS)
{
return deviceEnabled;
}
#endif
index = emberAfIndexFromEndpoint(endpoint);
if (index != 0xFF && index < sizeof(afDeviceEnabled))
{
return afDeviceEnabled[index];
}
return false;
}
void emberAfSetDeviceEnabled(EndpointId endpoint, bool enabled)
{
uint8_t index = emberAfIndexFromEndpoint(endpoint);
if (index != 0xFF && index < sizeof(afDeviceEnabled))
{
afDeviceEnabled[index] = enabled;
}
#ifdef ZCL_USING_BASIC_CLUSTER_DEVICE_ENABLED_ATTRIBUTE
emberAfWriteServerAttribute(endpoint, ZCL_BASIC_CLUSTER_ID, ZCL_DEVICE_ENABLED_ATTRIBUTE_ID, (uint8_t *) &enabled,
ZCL_BOOLEAN_ATTRIBUTE_TYPE);
#endif
}
// Is the device identifying?
bool emberAfIsDeviceIdentifying(EndpointId endpoint)
{
#ifdef ZCL_USING_IDENTIFY_CLUSTER_SERVER
uint16_t identifyTime;
EmberAfStatus status = emberAfReadServerAttribute(endpoint, ZCL_IDENTIFY_CLUSTER_ID, ZCL_IDENTIFY_TIME_ATTRIBUTE_ID,
(uint8_t *) &identifyTime, sizeof(identifyTime));
return (status == EMBER_ZCL_STATUS_SUCCESS && 0 < identifyTime);
#else
return false;
#endif
}
// Calculates difference. See EmberAfDifferenceType for the maximum data size
// that this function will support.
EmberAfDifferenceType emberAfGetDifference(uint8_t * pData, EmberAfDifferenceType value, uint8_t dataSize)
{
EmberAfDifferenceType value2 = 0, diff;
uint8_t i;
// only support data types up to 8 bytes
if (dataSize > sizeof(EmberAfDifferenceType))
{
return 0;
}
// get the value
for (i = 0; i < dataSize; i++)
{
value2 = value2 << 8;
#if (BIGENDIAN_CPU)
value2 += pData[i];
#else // BIGENDIAN
value2 += pData[dataSize - i - 1];
#endif // BIGENDIAN
}
if (value > value2)
{
diff = value - value2;
}
else
{
diff = value2 - value;
}
return diff;
}
// --------------------------------------------------
static void prepareForResponse(const EmberAfClusterCommand * cmd)
{
emberAfResponseApsFrame.clusterId = cmd->apsFrame->clusterId;
emberAfResponseApsFrame.sourceEndpoint = cmd->apsFrame->destinationEndpoint;
emberAfResponseApsFrame.destinationEndpoint = cmd->apsFrame->sourceEndpoint;
// Use the default APS options for the response, but also use encryption and
// retries if the incoming message used them. The rationale is that the
// sender of the request cares about some aspects of the delivery, so we as
// the receiver should make equal effort for the response.
emberAfResponseApsFrame.options = EMBER_AF_DEFAULT_APS_OPTIONS;
if ((cmd->apsFrame->options & EMBER_APS_OPTION_RETRY) != 0U)
{
emberAfResponseApsFrame.options |= EMBER_APS_OPTION_RETRY;
}
if (cmd->interPanHeader == NULL)
{
emberAfResponseDestination = cmd->source;
emberAfResponseType = static_cast<uint8_t>(emberAfResponseType & ~ZCL_UTIL_RESP_INTERPAN);
}
else
{
emberAfResponseType |= ZCL_UTIL_RESP_INTERPAN;
memmove(&interpanResponseHeader, cmd->interPanHeader, sizeof(EmberAfInterpanHeader));
// Always send responses as unicast
interpanResponseHeader.messageType = EMBER_AF_INTER_PAN_UNICAST;
}
}
// ****************************************
// Initialize Clusters
// ****************************************
void emberAfInit(void)
{
uint8_t i;
#ifdef EMBER_AF_ENABLE_STATISTICS
afNumPktsSent = 0;
#endif
for (i = 0; i < EMBER_SUPPORTED_NETWORKS; i++)
{
// FIXME: Do we need to support more than one network?
// emberAfPushNetworkIndex(i);
emberAfInitializeAttributes(EMBER_BROADCAST_ENDPOINT);
// emberAfPopNetworkIndex();
}
memset(afDeviceEnabled, true, emberAfEndpointCount());
// Set up client API buffer.
emberAfSetExternalBuffer(appResponseData, EMBER_AF_RESPONSE_BUFFER_LEN, &appResponseLength, &emberAfResponseApsFrame);
// initialize event management system
emAfInitEvents();
#ifdef EMBER_AF_PLUGIN_REPORTING
emberAfPluginReportingInitCallback();
#endif
#ifdef EMBER_AF_PLUGIN_TEMPERATURE_MEASUREMENT_SERVER
emberAfPluginTemperatureMeasurementServerInitCallback();
#endif
#ifdef EMBER_AF_PLUGIN_BARRIER_CONTROL_SERVER
emberAfPluginBarrierControlServerInitCallback();
#endif
#ifdef EMBER_AF_PLUGIN_DOOR_LOCK_SERVER
emberAfPluginDoorLockServerInitCallback();
#endif
emAfCallInits();
}
void emberAfTick(void)
{
// Call the AFV2-specific per-endpoint callbacks
// Anything that defines callbacks as void *TickCallback(void) is called in
// emAfInit(), which is a generated file
#ifdef EMBER_AF_GENERATED_PLUGIN_TICK_FUNCTION_CALLS
EMBER_AF_GENERATED_PLUGIN_TICK_FUNCTION_CALLS
#endif
}
// ****************************************
// This function is called by the application when the stack goes down,
// such as after a leave network. This allows zcl utils to clear state
// that should not be kept when changing networks
// ****************************************
void emberAfStackDown(void)
{
// (Case 14696) Clearing the report table is only necessary if the stack is
// going down for good; if we're rejoining, leave the table intact since we'll
// be right back, hopefully.
// (Issue 77101) Also don't clear the table if the stack has gone down as a
// a result of losing its parent or some other transient state where a future
// rejoin is expected to get us back online.
if (false
// emberStackIsPerformingRejoin() == false
// && emberNetworkState() == EMBER_NO_NETWORK
)
{
#ifdef EMBER_AF_PLUGIN_REPORTING
// the report table should be cleared when the stack comes down.
// going to a new network means new report devices should be discovered.
// if the table isnt cleared the device keeps trying to send messages.
emberAfClearReportTableCallback();
#endif // EMBER_AF_PLUGIN_REPORTING
}
emberAfRegistrationAbortCallback();
}
// ****************************************
// Print out information about each cluster
// ****************************************
uint16_t emberAfFindClusterNameIndexWithMfgCode(ClusterId cluster, uint16_t mfgCode)
{
static_assert(sizeof(ClusterId) == 2, "May need to adjust our index type or somehow define it in terms of cluster id type");
uint16_t index = 0;
while (zclClusterNames[index].id != ZCL_NULL_CLUSTER_ID)
{
if (zclClusterNames[index].id == cluster
// This check sees if its a standard cluster, in which mfgCode is ignored
// due to the name being well defined.
// If it is manufacturer specific, then we try to check to see if we
// know the name of the cluster within the list.
// If the mfgCode we are given is null, then we just ignore it for backward
// compatibility reasons
&& (cluster < 0xFC00 || zclClusterNames[index].mfgCode == mfgCode || mfgCode == EMBER_AF_NULL_MANUFACTURER_CODE))
{
return index;
}
index++;
}
return 0xFFFF;
}
uint16_t emberAfFindClusterNameIndex(ClusterId cluster)
{
return emberAfFindClusterNameIndexWithMfgCode(cluster, EMBER_AF_NULL_MANUFACTURER_CODE);
}
// This function parses into the cluster name table, and tries to find
// the index in the table that has the two keys: cluster + mfgcode.
void emberAfDecodeAndPrintClusterWithMfgCode(ClusterId cluster, uint16_t mfgCode)
{
uint16_t index = emberAfFindClusterNameIndexWithMfgCode(cluster, mfgCode);
if (index == 0xFFFF)
{
static_assert(sizeof(ClusterId) == 2, "Adjust the print formatting");
emberAfPrint(emberAfPrintActiveArea, "(Unknown clus. [0x%2x])", cluster);
}
else
{
emberAfPrint(emberAfPrintActiveArea, "(%p)", zclClusterNames[index].name);
}
}
void emberAfDecodeAndPrintCluster(ClusterId cluster)
{
emberAfDecodeAndPrintClusterWithMfgCode(cluster, EMBER_AF_NULL_MANUFACTURER_CODE);
}
// This function makes the assumption that
// emberAfCurrentCommand will either be NULL
// when invalid, or will have a valid mfgCode
// when called.
// If it is invalid, we just return the
// EMBER_AF_NULL_MANUFACTURER_CODE, which we tend to use
// for references to the standard library.
uint16_t emberAfGetMfgCodeFromCurrentCommand(void)
{
if (emberAfCurrentCommand() != NULL)
{
return emberAfCurrentCommand()->mfgCode;
}
else
{
return EMBER_AF_NULL_MANUFACTURER_CODE;
}
}
static void printIncomingZclMessage(const EmberAfClusterCommand * cmd)
{
#if defined(EMBER_AF_PRINT_ENABLE) && defined(EMBER_AF_PRINT_APP)
if (emberAfPrintReceivedMessages)
{
// emberAfAppPrint("\r\nT%4x:", emberAfGetCurrentTime());
emberAfAppPrint("RX len %d, ep %x, clus 0x%2x ", cmd->bufLen, cmd->apsFrame->destinationEndpoint, cmd->apsFrame->clusterId);
emberAfAppDebugExec(emberAfDecodeAndPrintClusterWithMfgCode(cmd->apsFrame->clusterId, cmd->mfgCode));
if (cmd->mfgSpecific)
{
emberAfAppPrint(" mfgId %2x", cmd->mfgCode);
}
emberAfAppPrint(" FC %x seq %x cmd %x payload[",
cmd->buffer[0], // frame control
cmd->seqNum, cmd->commandId);
emberAfAppFlush();
emberAfAppPrintBuffer(cmd->buffer + cmd->payloadStartIndex, // message
static_cast<uint16_t>(cmd->bufLen - cmd->payloadStartIndex), // length
true); // spaces?
emberAfAppFlush();
emberAfAppPrintln("]");
}
#endif
}
static bool dispatchZclMessage(EmberAfClusterCommand * cmd)
{
uint8_t index = emberAfIndexFromEndpoint(cmd->apsFrame->destinationEndpoint);
if (index == 0xFF)
{
emberAfDebugPrint("Drop cluster 0x%2x command 0x%x", cmd->apsFrame->clusterId, cmd->commandId);
emberAfDebugPrint(" due to invalid endpoint: ");
emberAfDebugPrintln("0x%x", cmd->apsFrame->destinationEndpoint);
return false;
}
else if (emberAfNetworkIndexFromEndpointIndex(index) != cmd->networkIndex)
{
emberAfDebugPrint("Drop cluster 0x%2x command 0x%x", cmd->apsFrame->clusterId, cmd->commandId);
emberAfDebugPrint(" for endpoint 0x%x due to wrong %s: ", cmd->apsFrame->destinationEndpoint, "network");
emberAfDebugPrintln("%d", cmd->networkIndex);
return false;
}
#ifdef EMBER_AF_PLUGIN_GROUPS_SERVER
else if ((cmd->type == EMBER_INCOMING_MULTICAST || cmd->type == EMBER_INCOMING_MULTICAST_LOOPBACK) &&
!emberAfGroupsClusterEndpointInGroupCallback(cmd->apsFrame->destinationEndpoint, cmd->apsFrame->groupId))
{
emberAfDebugPrint("Drop cluster 0x%2x command 0x%x", cmd->apsFrame->clusterId, cmd->commandId);
emberAfDebugPrint(" for endpoint 0x%x due to wrong %s: ", cmd->apsFrame->destinationEndpoint, "group");
emberAfDebugPrintln("0x%02x", cmd->apsFrame->groupId);
return false;
}
#endif // EMBER_AF_PLUGIN_GROUPS_SERVER
else
{
return (cmd->clusterSpecific ? emAfProcessClusterSpecificCommand(cmd) : emAfProcessGlobalCommand(cmd));
}
}
bool emberAfProcessMessageIntoZclCmd(EmberApsFrame * apsFrame, EmberIncomingMessageType type, uint8_t * message,
uint16_t messageLength, NodeId source, InterPanHeader * interPanHeader,
EmberAfClusterCommand * returnCmd)
{
uint8_t minLength =
(message[0] & ZCL_MANUFACTURER_SPECIFIC_MASK ? EMBER_AF_ZCL_MANUFACTURER_SPECIFIC_OVERHEAD : EMBER_AF_ZCL_OVERHEAD);
if (messageLength < minLength)
{
emberAfAppPrintln("%pRX pkt too short: %d < %d", "ERROR: ", messageLength, minLength);
return false;
}
// Populate the cluster command struct for processing.
returnCmd->apsFrame = apsFrame;
returnCmd->type = type;
returnCmd->source = source;
returnCmd->buffer = message;
returnCmd->bufLen = messageLength;
returnCmd->clusterSpecific = (message[0] & ZCL_CLUSTER_SPECIFIC_COMMAND);
returnCmd->mfgSpecific = (message[0] & ZCL_MANUFACTURER_SPECIFIC_MASK);
returnCmd->direction =
((message[0] & ZCL_FRAME_CONTROL_DIRECTION_MASK) ? ZCL_DIRECTION_SERVER_TO_CLIENT : ZCL_DIRECTION_CLIENT_TO_SERVER);
returnCmd->payloadStartIndex = 1;
if (returnCmd->mfgSpecific)
{
returnCmd->mfgCode = emberAfGetInt16u(message, returnCmd->payloadStartIndex, messageLength);
returnCmd->payloadStartIndex = static_cast<uint8_t>(returnCmd->payloadStartIndex + 2);
}
else
{
returnCmd->mfgCode = EMBER_AF_NULL_MANUFACTURER_CODE;
}
returnCmd->seqNum = message[returnCmd->payloadStartIndex++];
returnCmd->commandId = message[returnCmd->payloadStartIndex++];
if (returnCmd->payloadStartIndex > returnCmd->bufLen)
{
emberAfAppPrintln("%pRX pkt malformed: %d < %d", "ERROR: ", returnCmd->bufLen, returnCmd->payloadStartIndex);
return false;
}
returnCmd->interPanHeader = interPanHeader;
// returnCmd->networkIndex = emberGetCurrentNetwork();
return true;
}
// a single call to process global and cluster-specific messages and callbacks.
bool emberAfProcessMessage(EmberApsFrame * apsFrame, EmberIncomingMessageType type, uint8_t * message, uint16_t msgLen,
NodeId source, InterPanHeader * interPanHeader)
{
bool msgHandled = false;
// reset/reinitialize curCmd
curCmd = staticCmd;
if (!emberAfProcessMessageIntoZclCmd(apsFrame, type, message, msgLen, source, interPanHeader, &curCmd))
{
goto kickout;
}
emAfCurrentCommand = &curCmd;
// All of these should be covered by the EmberAfClusterCommand but are
// still here until all the code is moved over to use the cmd. -WEH
emberAfIncomingZclSequenceNumber = curCmd.seqNum;
printIncomingZclMessage(&curCmd);
prepareForResponse(&curCmd);
if (emberAfPreCommandReceivedCallback(&curCmd))
{
msgHandled = true;
goto kickout;
}
if (interPanHeader != NULL && !(interPanHeader->options & EMBER_AF_INTERPAN_OPTION_MAC_HAS_LONG_ADDRESS))
{
// For safety, dump all interpan messages that don't have a long
// source in the MAC layer. In theory they should not get past
// the MAC filters but this is insures they will not get processed.
goto kickout;
}
if (curCmd.apsFrame->destinationEndpoint == EMBER_BROADCAST_ENDPOINT)
{
uint8_t i;
for (i = 0; i < emberAfEndpointCount(); i++)
{
EndpointId endpoint = emberAfEndpointFromIndex(i);
if (!emberAfEndpointIndexIsEnabled(i) ||
!emberAfContainsClusterWithMfgCode(endpoint, curCmd.apsFrame->clusterId, curCmd.mfgCode))
{
continue;
}
// Since the APS frame is cleared after each sending,
// we must reinitialize it. It is cleared to prevent
// data from leaking out and being sent inadvertently.
prepareForResponse(&curCmd);
// Change the destination endpoint of the incoming command and the source
// source endpoint of the response so they both reflect the endpoint the
// message is actually being passed to in this iteration of the loop.
curCmd.apsFrame->destinationEndpoint = endpoint;
emberAfResponseApsFrame.sourceEndpoint = endpoint;
if (dispatchZclMessage(&curCmd))
{
msgHandled = true;
}
}
}
else
{
msgHandled = dispatchZclMessage(&curCmd);
}
kickout:
emberAfClearResponseData();
memset(&interpanResponseHeader, 0, sizeof(EmberAfInterpanHeader));
emAfCurrentCommand = NULL;
return msgHandled;
}
uint8_t emberAfNextSequence(void)
{
return ((++emberAfSequenceNumber) & EMBER_AF_ZCL_SEQUENCE_MASK);
}
uint8_t emberAfGetLastSequenceNumber(void)
{
return (emberAfSequenceNumber & EMBER_AF_ZCL_SEQUENCE_MASK);
}
// the caller to the library can set a flag to say do not respond to the
// next ZCL message passed in to the library. Passing true means disable
// the reply for the next ZCL message. Setting to false re-enables the
// reply (in the case where the app disables it and then doesnt send a
// message that gets parsed).
void emberAfSetNoReplyForNextMessage(bool set)
{
if (set)
{
emberAfResponseType |= ZCL_UTIL_RESP_NONE;
}
else
{
emberAfResponseType = static_cast<uint8_t>(emberAfResponseType & ~ZCL_UTIL_RESP_NONE);
}
}
void emberAfSetRetryOverride(EmberAfRetryOverride value)
{
emberAfApsRetryOverride = value;
}
EmberAfRetryOverride emberAfGetRetryOverride(void)
{
return (EmberAfRetryOverride) emberAfApsRetryOverride;
}
void emAfApplyRetryOverride(EmberApsOption * options)
{
if (options == NULL)
{
return;
}
else if (emberAfApsRetryOverride == EMBER_AF_RETRY_OVERRIDE_SET)
{
*options |= EMBER_APS_OPTION_RETRY;
}
else if (emberAfApsRetryOverride == EMBER_AF_RETRY_OVERRIDE_UNSET)
{
*options = static_cast<EmberApsOption>(*options & ~EMBER_APS_OPTION_RETRY);
}
else
{
// MISRA requires ..else if.. to have terminating else.
}
}
void emberAfSetDisableDefaultResponse(EmberAfDisableDefaultResponse value)
{
emAfDisableDefaultResponse = value;
if (value != EMBER_AF_DISABLE_DEFAULT_RESPONSE_ONE_SHOT)
{
emAfSavedDisableDefaultResponseVale = value;
}
}
EmberAfDisableDefaultResponse emberAfGetDisableDefaultResponse(void)
{
return (EmberAfDisableDefaultResponse) emAfDisableDefaultResponse;
}
void emAfApplyDisableDefaultResponse(uint8_t * frame_control)
{
if (frame_control == NULL)
{
return;
}
else if (emAfDisableDefaultResponse == EMBER_AF_DISABLE_DEFAULT_RESPONSE_ONE_SHOT)
{
emAfDisableDefaultResponse = emAfSavedDisableDefaultResponseVale;
*frame_control |= ZCL_DISABLE_DEFAULT_RESPONSE_MASK;
}
else if (emAfDisableDefaultResponse == EMBER_AF_DISABLE_DEFAULT_RESPONSE_PERMANENT)
{
*frame_control |= ZCL_DISABLE_DEFAULT_RESPONSE_MASK;
}
else
{
// MISRA requires ..else if.. to have terminating else.
}
}
static bool isBroadcastDestination(NodeId responseDestination)
{
// FIXME: Will need to actually figure out how to test for this!
return false;
}
EmberStatus emberAfSendResponseWithCallback(EmberAfMessageSentFunction callback)
{
EmberStatus status;
uint8_t label;
// If the no-response flag is set, don't send anything.
if ((emberAfResponseType & ZCL_UTIL_RESP_NONE) != 0U)
{
emberAfDebugPrintln("ZCL Util: no response at user request");
return EMBER_SUCCESS;
}
// Make sure we are respecting the request APS options
// there are seemingly some calls to emberAfSendResponse
// that occur outside of the emberAfProcessMessage context,
// which leads to a bad memory reference - AHilton
if (emberAfCurrentCommand() != NULL)
{
if ((emberAfCurrentCommand()->apsFrame->options & EMBER_APS_OPTION_RETRY) != 0U)
{
emberAfResponseApsFrame.options |= EMBER_APS_OPTION_RETRY;
}
}
// Fill commands may increase the sequence. For responses, we want to make
// sure the sequence is reset to that of the request.
if ((appResponseData[0] & ZCL_MANUFACTURER_SPECIFIC_MASK) != 0U)
{
appResponseData[3] = emberAfIncomingZclSequenceNumber;
}
else
{
appResponseData[1] = emberAfIncomingZclSequenceNumber;
}
// The manner in which the message is sent depends on the response flags and
// the destination of the message.
if ((emberAfResponseType & ZCL_UTIL_RESP_INTERPAN) != 0U)
{
label = 'I';
status = emberAfInterpanSendMessageCallback(&interpanResponseHeader, appResponseLength, appResponseData);
emberAfResponseType = static_cast<uint8_t>(emberAfResponseType & ~ZCL_UTIL_RESP_INTERPAN);
}
else if (!isBroadcastDestination(emberAfResponseDestination))
{
label = 'U';
status = emberAfSendUnicastWithCallback(EMBER_OUTGOING_DIRECT, emberAfResponseDestination, &emberAfResponseApsFrame,
appResponseLength, appResponseData, callback);
}
else
{
label = 'B';
#if 0
status = emberAfSendBroadcastWithCallback(emberAfResponseDestination,
&emberAfResponseApsFrame,
appResponseLength,
appResponseData,
callback);
#else
status = EMBER_SUCCESS;
#endif
}
UNUSED_VAR(label);
emberAfDebugPrintln("T%4x:TX (%p) %ccast 0x%x%p", 0, "resp", label, status, "");
emberAfDebugPrint("TX buffer: [");
emberAfDebugFlush();
emberAfDebugPrintBuffer(appResponseData, appResponseLength, true);
emberAfDebugPrintln("]");
emberAfDebugFlush();
#ifdef EMBER_AF_ENABLE_STATISTICS
if (status == EMBER_SUCCESS)
{
afNumPktsSent++;
}
#endif
return status;
}
EmberStatus emberAfSendResponse(void)
{
return emberAfSendResponseWithCallback(NULL);
}
EmberStatus emberAfSendImmediateDefaultResponseWithCallback(EmberAfStatus status, EmberAfMessageSentFunction callback)
{
return emberAfSendDefaultResponseWithCallback(emberAfCurrentCommand(), status, callback);
}
EmberStatus emberAfSendImmediateDefaultResponse(EmberAfStatus status)
{
return emberAfSendImmediateDefaultResponseWithCallback(status, NULL);
}
EmberStatus emberAfSendDefaultResponseWithCallback(const EmberAfClusterCommand * cmd, EmberAfStatus status,
EmberAfMessageSentFunction callback)
{
uint8_t frameControl;
// Default Response commands are only sent in response to unicast commands.
if (cmd->type != EMBER_INCOMING_UNICAST && cmd->type != EMBER_INCOMING_UNICAST_REPLY)
{
return EMBER_SUCCESS;
}
// If the Disable Default Response sub-field is set, Default Response commands
// are only sent if there was an error.
if ((cmd->buffer[0] & ZCL_DISABLE_DEFAULT_RESPONSE_MASK) && status == EMBER_ZCL_STATUS_SUCCESS)
{
return EMBER_SUCCESS;
}
// Default Response commands are never sent in response to other Default
// Response commands.
if (!cmd->clusterSpecific && cmd->commandId == ZCL_DEFAULT_RESPONSE_COMMAND_ID)
{
return EMBER_SUCCESS;
}
appResponseLength = 0;
frameControl = static_cast<uint8_t>(ZCL_GLOBAL_COMMAND |
(cmd->direction == ZCL_DIRECTION_CLIENT_TO_SERVER ? ZCL_FRAME_CONTROL_SERVER_TO_CLIENT
: ZCL_FRAME_CONTROL_CLIENT_TO_SERVER));
if (!cmd->mfgSpecific)
{
emberAfPutInt8uInResp(frameControl & (uint8_t) ~ZCL_MANUFACTURER_SPECIFIC_MASK);
}
else
{
emberAfPutInt8uInResp(frameControl | ZCL_MANUFACTURER_SPECIFIC_MASK);
emberAfPutInt16uInResp(cmd->mfgCode);
}
emberAfPutInt8uInResp(cmd->seqNum);
emberAfPutInt8uInResp(ZCL_DEFAULT_RESPONSE_COMMAND_ID);
emberAfPutInt8uInResp(cmd->commandId);
emberAfPutInt8uInResp(status);
prepareForResponse(cmd);
return emberAfSendResponseWithCallback(callback);
}
EmberStatus emberAfSendDefaultResponse(const EmberAfClusterCommand * cmd, EmberAfStatus status)
{
return emberAfSendDefaultResponseWithCallback(cmd, status, NULL);
}
uint8_t emberAfMaximumApsPayloadLength(EmberOutgoingMessageType type, uint64_t indexOrDestination, EmberApsFrame * apsFrame)
{
NodeId destination = EMBER_UNKNOWN_NODE_ID;
uint8_t max = EMBER_AF_MAXIMUM_APS_PAYLOAD_LENGTH;
if ((apsFrame->options & EMBER_APS_OPTION_SOURCE_EUI64) != 0U)
{
max = static_cast<uint8_t>(max - EUI64_SIZE);
}
if ((apsFrame->options & EMBER_APS_OPTION_DESTINATION_EUI64) != 0U)
{
max = static_cast<uint8_t>(max - EUI64_SIZE);
}
if ((apsFrame->options & EMBER_APS_OPTION_FRAGMENT) != 0U)
{
max = static_cast<uint8_t>(max - EMBER_AF_APS_FRAGMENTATION_OVERHEAD);
}
switch (type)
{
case EMBER_OUTGOING_DIRECT:
destination = indexOrDestination;
break;
case EMBER_OUTGOING_VIA_ADDRESS_TABLE:
// destination = emberGetAddressTableRemoteNodeId(indexOrDestination);
break;
case EMBER_OUTGOING_VIA_BINDING:
// destination = emberGetBindingRemoteNodeId(indexOrDestination);
break;
case EMBER_OUTGOING_MULTICAST:
// APS multicast messages include the two-byte group id and exclude the
// one-byte destination endpoint, for a net loss of an extra byte.
max--;
break;
case EMBER_OUTGOING_BROADCAST:
break;
default:
// MISRA requires default case.
break;
}
max = static_cast<uint8_t>(max - emberAfGetSourceRouteOverheadCallback(destination));
return max;
}
void emberAfCopyInt16u(uint8_t * data, uint16_t index, uint16_t x)
{
data[index] = (uint8_t)(((x)) & 0xFF);
data[index + 1] = (uint8_t)(((x) >> 8) & 0xFF);
}
void emberAfCopyInt24u(uint8_t * data, uint16_t index, uint32_t x)
{
data[index] = (uint8_t)(((x)) & 0xFF);
data[index + 1] = (uint8_t)(((x) >> 8) & 0xFF);
data[index + 2] = (uint8_t)(((x) >> 16) & 0xFF);
}
void emberAfCopyInt32u(uint8_t * data, uint16_t index, uint32_t x)
{
data[index] = (uint8_t)(((x)) & 0xFF);
data[index + 1] = (uint8_t)(((x) >> 8) & 0xFF);
data[index + 2] = (uint8_t)(((x) >> 16) & 0xFF);
data[index + 3] = (uint8_t)(((x) >> 24) & 0xFF);
}
void emberAfCopyString(uint8_t * dest, const uint8_t * src, uint8_t size)
{
if (src == NULL)
{
dest[0] = 0; // Zero out the length of string
}
else if (src[0] == 0xFF)
{
dest[0] = src[0];
}
else
{
uint8_t length = emberAfStringLength(src);
if (size < length)
{
length = size;
}
memmove(dest + 1, src + 1, length);
dest[0] = length;
}
}
void emberAfCopyLongString(uint8_t * dest, const uint8_t * src, uint16_t size)
{
if (src == NULL)
{
dest[0] = dest[1] = 0; // Zero out the length of string
}
else if ((src[0] == 0xFF) && (src[1] == 0xFF))
{
dest[0] = 0xFF;
dest[1] = 0xFF;
}
else
{
uint16_t length = emberAfLongStringLength(src);
if (size < length)
{
length = size;
}
memmove(dest + 2, src + 2, length);
dest[0] = EMBER_LOW_BYTE(length);
dest[1] = EMBER_HIGH_BYTE(length);
}
}
#if (BIGENDIAN_CPU)
#define EM_BIG_ENDIAN true
#else
#define EM_BIG_ENDIAN false
#endif
// You can pass in val1 as NULL, which will assume that it is
// pointing to an array of all zeroes. This is used so that
// default value of NULL is treated as all zeroes.
int8_t emberAfCompareValues(uint8_t * val1, uint8_t * val2, uint8_t len, bool signedNumber)
{
uint8_t i, j, k;
if (signedNumber)
{ // signed number comparison
if (len <= 4)
{ // only number with 32-bits or less is supported
int32_t accum1 = 0x0;
int32_t accum2 = 0x0;
int32_t all1s = -1;
for (i = 0; i < len; i++)
{
j = (val1 == NULL ? 0 : (EM_BIG_ENDIAN ? val1[i] : val1[(len - 1) - i]));
accum1 |= j << (8 * (len - 1 - i));
k = (EM_BIG_ENDIAN ? val2[i] : val2[(len - 1) - i]);
accum2 |= k << (8 * (len - 1 - i));
}
// sign extending, no need for 32-bits numbers
if (len < 4)
{
if ((accum1 & (1 << (8 * len - 1))) != 0)
{ // check sign
accum1 |= all1s - ((1 << (len * 8)) - 1);
}
if ((accum2 & (1 << (8 * len - 1))) != 0)
{ // check sign
accum2 |= all1s - ((1 << (len * 8)) - 1);
}
}
if (accum1 > accum2)
{
return 1;
}
else if (accum1 < accum2)
{
return -1;
}
else
{
return 0;
}
}
else
{ // not supported
return 0;
}
}
else
{ // regular unsigned number comparison
for (i = 0; i < len; i++)
{
j = (val1 == NULL ? 0 : (EM_BIG_ENDIAN ? val1[i] : val1[(len - 1) - i]));
k = (EM_BIG_ENDIAN ? val2[i] : val2[(len - 1) - i]);
if (j > k)
{
return 1;
}
else if (k > j)
{
return -1;
}
else
{
// MISRA requires ..else if.. to have terminating else.
}
}
return 0;
}
}
#if 0
// Moving to time-util.c
int8_t emberAfCompareDates(EmberAfDate* date1, EmberAfDate* date2)
{
uint32_t val1 = emberAfEncodeDate(date1);
uint32_t val2 = emberAfEncodeDate(date2);
return (val1 == val2) ? 0 : ((val1 < val2) ? -1 : 1);
}
#endif
// returns the type that the attribute is, either EMBER_AF_DATA_TYPE_ANALOG,
// EMBER_AF_DATA_TYPE_DISCRETE, or EMBER_AF_DATA_TYPE_NONE. This is based on table
// 2.15 from the ZCL spec 075123r02
uint8_t emberAfGetAttributeAnalogOrDiscreteType(uint8_t dataType)
{
unsigned index = 0;
while (emberAfAnalogDiscreteThresholds[index] < dataType)
{
index += 2;
}
return emberAfAnalogDiscreteThresholds[index + 1];
}
// Zigbee spec says types between signed 8 bit and signed 64 bit
bool emberAfIsTypeSigned(EmberAfAttributeType dataType)
{
return (dataType >= ZCL_INT8S_ATTRIBUTE_TYPE && dataType <= ZCL_INT64S_ATTRIBUTE_TYPE);
}
EmberStatus emberAfEndpointEventControlSetInactive(EmberEventControl * controls, EndpointId endpoint)
{
uint8_t index = emberAfIndexFromEndpoint(endpoint);
if (index == 0xFF)
{
return EMBER_INVALID_ENDPOINT;
}
emberEventControlSetInactive(&controls[index]);
return EMBER_SUCCESS;
}
bool emberAfEndpointEventControlGetActive(EmberEventControl * controls, EndpointId endpoint)
{
uint8_t index = emberAfIndexFromEndpoint(endpoint);
return (index != 0xFF && emberEventControlGetActive(&controls[index]));
}
EmberStatus emberAfEndpointEventControlSetActive(EmberEventControl * controls, EndpointId endpoint)
{
uint8_t index = emberAfIndexFromEndpoint(endpoint);
if (index == 0xFF)
{
return EMBER_INVALID_ENDPOINT;
}
emberEventControlSetActive(&controls[index]);
return EMBER_SUCCESS;
}
uint8_t emberAfAppendCharacters(uint8_t * zclString, uint8_t zclStringMaxLen, const uint8_t * appendingChars,
uint8_t appendingCharsLen)
{
uint8_t freeChars;
uint8_t curLen;
uint8_t charsToWrite;
if ((zclString == NULL) || (zclStringMaxLen == 0) || (appendingChars == NULL) || (appendingCharsLen == 0))
{
return 0;
}
curLen = emberAfStringLength(zclString);
if ((zclString[0] == 0xFF) || (curLen >= zclStringMaxLen))
{
return 0;
}
freeChars = static_cast<uint8_t>(zclStringMaxLen - curLen);
charsToWrite = (freeChars > appendingCharsLen) ? appendingCharsLen : freeChars;
memcpy(&zclString[1 + curLen], // 1 is to account for zcl's length byte
appendingChars, charsToWrite);
// Cast is safe, because the sum can't be bigger than zclStringMaxLen.
zclString[0] = static_cast<uint8_t>(curLen + charsToWrite);
return charsToWrite;
}
/*
On each page, first channel maps to channel number zero and so on.
Example:
page Band Rage of 90 channels Per page channel mapping
28 863 MHz 0-26 0-26
29 863 MHz 27-34,62 0-8 (Here 7th channel maps to 34 and 8th to 62)
30 863 MHz 35 - 61 0-26
31 915 0-26 0-26
*/
EmberStatus emAfValidateChannelPages(uint8_t page, uint8_t channel)
{
switch (page)
{
case 0:
if (!((channel <= EMBER_MAX_802_15_4_CHANNEL_NUMBER) &&
((EMBER_MIN_802_15_4_CHANNEL_NUMBER == 0) || (channel >= EMBER_MIN_802_15_4_CHANNEL_NUMBER))))
{
return EMBER_PHY_INVALID_CHANNEL;
}
break;
case 28:
case 30:
case 31:
if (channel > EMBER_MAX_SUBGHZ_CHANNEL_NUMBER_ON_PAGES_28_30_31)
{
return EMBER_PHY_INVALID_CHANNEL;
}
break;
case 29:
if (channel > EMBER_MAX_SUBGHZ_CHANNEL_NUMBER_ON_PAGE_29)
{
return EMBER_PHY_INVALID_CHANNEL;
}
break;
default:
return EMBER_PHY_INVALID_CHANNEL;
break;
}
return EMBER_SUCCESS;
}
void slabAssert(const char * file, int line)
{
(void) file; // Unused parameter
(void) line; // Unused parameter
// Wait forever until the watchdog fires
while (true)
{
}
}
#define ENCODED_8BIT_CHANPG_PAGE_MASK 0xE0 // top 3 bits
#define ENCODED_8BIT_CHANPG_PAGE_MASK_PAGE_0 0x00 // 0b000xxxxx
#define ENCODED_8BIT_CHANPG_PAGE_MASK_PAGE_28 0x80 // 0b100xxxxx
#define ENCODED_8BIT_CHANPG_PAGE_MASK_PAGE_29 0xA0 // 0b101xxxxx
#define ENCODED_8BIT_CHANPG_PAGE_MASK_PAGE_30 0xC0 // 0b110xxxxx
#define ENCODED_8BIT_CHANPG_PAGE_MASK_PAGE_31 0xE0 // 0b111xxxxx
#define ENCODED_8BIT_CHANPG_CHANNEL_MASK 0x1F // bottom 5 bits
uint8_t emberAfGetPageFrom8bitEncodedChanPg(uint8_t chanPg)
{
switch (chanPg & ENCODED_8BIT_CHANPG_PAGE_MASK)
{
case ENCODED_8BIT_CHANPG_PAGE_MASK_PAGE_0:
return 0;
case ENCODED_8BIT_CHANPG_PAGE_MASK_PAGE_28:
return 28;
case ENCODED_8BIT_CHANPG_PAGE_MASK_PAGE_29:
return 29;
case ENCODED_8BIT_CHANPG_PAGE_MASK_PAGE_30:
return 30;
case ENCODED_8BIT_CHANPG_PAGE_MASK_PAGE_31:
return 31;
default:
return 0xFF;
}
}
uint8_t emberAfGetChannelFrom8bitEncodedChanPg(uint8_t chanPg)
{
return chanPg & ENCODED_8BIT_CHANPG_CHANNEL_MASK;
}
uint8_t emberAfMake8bitEncodedChanPg(uint8_t page, uint8_t channel)
{
if (emAfValidateChannelPages(page, channel) != EMBER_SUCCESS)
{
return 0xFF;
}
switch (page)
{
case 28:
return channel | ENCODED_8BIT_CHANPG_PAGE_MASK_PAGE_28;
case 29:
return channel | ENCODED_8BIT_CHANPG_PAGE_MASK_PAGE_29;
case 30:
return channel | ENCODED_8BIT_CHANPG_PAGE_MASK_PAGE_30;
case 31:
return channel | ENCODED_8BIT_CHANPG_PAGE_MASK_PAGE_31;
default:
// Strictly speaking, we only need case 0 here, but MISRA in its infinite
// wisdom requires a default case. Since we have validated the arguments
// already, and 0 is the only remaining case, we simply treat the default
// as case 0 to make MISRA happy.
return channel | ENCODED_8BIT_CHANPG_PAGE_MASK_PAGE_0;
}
}