/**************************************************************************************************
  Filename:       zcl_ss.c
  Revised:        $Date: 2006-04-03 16:28:25 -0700 (Mon, 03 Apr 2006) $
  Revision:       $Revision: 10363 $

  Description:    Zigbee Cluster Library - Security and Safety ( SS )


  Copyright 2006-2007 Texas Instruments Incorporated. All rights reserved.

  IMPORTANT: Your use of this Software is limited to those specific rights
  granted under the terms of a software license agreement between the user
  who downloaded the software, his/her employer (which must be your employer)
  and Texas Instruments Incorporated (the "License").  You may not use this
  Software unless you agree to abide by the terms of the License. The License
  limits your use, and you acknowledge, that the Software may not be modified,
  copied or distributed unless embedded on a Texas Instruments microcontroller
  or used solely and exclusively in conjunction with a Texas Instruments radio
  frequency transceiver, which is integrated into your product.  Other than for
  the foregoing purpose, you may not use, reproduce, copy, prepare derivative
  works of, modify, distribute, perform, display or sell this Software and/or
  its documentation for any purpose.

  YOU FURTHER ACKNOWLEDGE AND AGREE THAT THE SOFTWARE AND DOCUMENTATION ARE
  PROVIDED “AS IS” WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, 
  INCLUDING WITHOUT LIMITATION, ANY WARRANTY OF MERCHANTABILITY, TITLE, 
  NON-INFRINGEMENT AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL
  TEXAS INSTRUMENTS OR ITS LICENSORS BE LIABLE OR OBLIGATED UNDER CONTRACT,
  NEGLIGENCE, STRICT LIABILITY, CONTRIBUTION, BREACH OF WARRANTY, OR OTHER
  LEGAL EQUITABLE THEORY ANY DIRECT OR INDIRECT DAMAGES OR EXPENSES
  INCLUDING BUT NOT LIMITED TO ANY INCIDENTAL, SPECIAL, INDIRECT, PUNITIVE
  OR CONSEQUENTIAL DAMAGES, LOST PROFITS OR LOST DATA, COST OF PROCUREMENT
  OF SUBSTITUTE GOODS, TECHNOLOGY, SERVICES, OR ANY CLAIMS BY THIRD PARTIES
  (INCLUDING BUT NOT LIMITED TO ANY DEFENSE THEREOF), OR OTHER SIMILAR COSTS.

  Should you have any questions regarding your right to use this Software,
  contact Texas Instruments Incorporated at www.TI.com. 
**************************************************************************************************/

/*******************************************************************************
 * INCLUDES
 */
#include "ZComDef.h"
#include "OSAL.h"
#include "zcl.h"
#include "zcl_general.h"
#include "zcl_ss.h"

/*******************************************************************************
 * MACROS
 */
#define zclSS_ZoneTypeSupported( a ) ( (a) == SS_IAS_ZONE_TYPE_STANDARD_CIE              || \
                                       (a) == SS_IAS_ZONE_TYPE_MOTION_SENSOR             || \
                                       (a) == SS_IAS_ZONE_TYPE_CONTACT_SWITCH            || \
                                       (a) == SS_IAS_ZONE_TYPE_FIRE_SENSOR               || \
                                       (a) == SS_IAS_ZONE_TYPE_WATER_SENSOR              || \
                                       (a) == SS_IAS_ZONE_TYPE_GAS_SENSOR                || \
                                       (a) == SS_IAS_ZONE_TYPE_PERSONAL_EMERGENCY_DEVICE || \
                                       (a) == SS_IAS_ZONE_TYPE_VIBRATION_MOVEMENT_SENSOR || \
                                       (a) == SS_IAS_ZONE_TYPE_REMOTE_CONTROL            || \
                                       (a) == SS_IAS_ZONE_TYPE_KEY_FOB                   || \
                                       (a) == SS_IAS_ZONE_TYPE_KEYPAD                    || \
                                       (a) == SS_IAS_ZONE_TYPE_STANDARD_WARNING_DEVICE )

/*******************************************************************************
 * CONSTANTS
 */

/*******************************************************************************
 * TYPEDEFS
 */
typedef struct zclSSCBRec
{
  struct zclSSCBRec       *next;
  uint8                   endpoint; // Used to link it into the endpoint descriptor
  zclSS_AppCallbacks_t    *CBs;     // Pointer to Callback function
} zclSSCBRec_t;

typedef struct zclSS_ZoneItem
{
  struct zclSS_ZoneItem   *next;
  uint8                   endpoint; // Used to link it into the endpoint descriptor
  IAS_ACE_ZoneTable_t     zone;     // Zone info
} zclSS_ZoneItem_t;

/*******************************************************************************
 * GLOBAL VARIABLES
 */
const uint8 zclSS_UknownIeeeAddress[8] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };

/*******************************************************************************
 * GLOBAL FUNCTIONS
 */

/*******************************************************************************
 * LOCAL VARIABLES
 */
static zclSSCBRec_t *zclSSCBs = (zclSSCBRec_t *)NULL;
static uint8 zclSSPluginRegisted = FALSE;

#if defined(ZCL_ZONE) || defined(ZCL_ACE)
static zclSS_ZoneItem_t *zclSS_ZoneTable = (zclSS_ZoneItem_t *)NULL;
#endif // ZCL_ZONE || ZCL_ACE

/*******************************************************************************
 * LOCAL FUNCTIONS
 */
static ZStatus_t zclSS_HdlIncoming( zclInHdlrMsg_t *pInHdlrMsg );
static ZStatus_t zclSS_HdlInSpecificCommands( zclIncoming_t *pInMsg, uint16 logicalClusterID );

#if defined(ZCL_ZONE) || defined(ZCL_ACE) || defined(ZCL_WD)
static zclSS_AppCallbacks_t *zclSS_FindCallbacks( uint8 endpoint );
#endif // ZCL_ZONE || ZCL_ACE || ZCL_WD

#ifdef ZCL_ZONE
static ZStatus_t zclSS_ProcessInZoneStatusCmdsServer( zclIncoming_t *pInMsg );
static ZStatus_t zclSS_ProcessInZoneStatusCmdsClient( zclIncoming_t *pInMsg );

static void zclSS_ProcessInCmd_ZoneStatus_ChangeNotification( zclIncoming_t *pInMsg );
static ZStatus_t zclSS_ProcessInCmd_ZoneStatus_EnrollRequest( zclIncoming_t *pInMsg );
static void zclSS_ProcessInCmd_ZoneStatus_EnrollResponse( zclIncoming_t *pInMsg );
#endif // ZCL_ZONE

#ifdef ZCL_ACE
static ZStatus_t zclSS_ProcessInACECmdsServer( zclIncoming_t *pInMsg );
static ZStatus_t zclSS_ProcessInACECmdsClient( zclIncoming_t *pInMsg );

static ZStatus_t zclSS_ProcessInCmd_ACE_Arm( zclIncoming_t *pInMsg );
static void zclSS_ProcessInCmd_ACE_Bypass( zclIncoming_t *pInMsg );
static void zclSS_ProcessInCmd_ACE_Emergency( zclIncoming_t *pInMsg );
static void zclSS_ProcessInCmd_ACE_Fire( zclIncoming_t *pInMsg );
static void zclSS_ProcessInCmd_ACE_Panic( zclIncoming_t *pInMsg );
static ZStatus_t zclSS_ProcessInCmd_ACE_GetZoneIDMap( zclIncoming_t *pInMsg );
static ZStatus_t zclSS_ProcessInCmd_ACE_GetZoneInformation( zclIncoming_t *pInMsg );
static void zclSS_ProcessInCmd_ACE_ArmResponse( zclIncoming_t *pInMsg );
static void zclSS_ProcessInCmd_ACE_GetZoneIDMapResponse( zclIncoming_t *pInMsg );
static void zclSS_ProcessInCmd_ACE_GetZoneInformationResponse( zclIncoming_t *pInMsg );
#endif // ZCL_ACE

#ifdef ZCL_WD
static ZStatus_t zclSS_ProcessInWDCmds( zclIncoming_t *pInMsg );

static void zclSS_ProcessInCmd_WD_StartWarning( zclIncoming_t *pInMsg );
static void zclSS_ProcessInCmd_WD_Squawk( zclIncoming_t *pInMsg );
#endif // ZCL_WD

#ifdef ZCL_ZONE
static uint8 zclSS_GetNextFreeZoneID( void );
static ZStatus_t zclSS_AddZone( uint8 endpoint, IAS_ACE_ZoneTable_t *zone );
static uint8 zclSS_CountAllZones( void );
static uint8 zclSS_ZoneIDAvailable( uint8 zoneID );
#endif // ZCL_ZONE

#if defined(ZCL_ZONE) || defined(ZCL_ACE)
static IAS_ACE_ZoneTable_t *zclSS_FindZone( uint8 endpoint, uint8 zoneID );
#endif // ZCL_ZONE || ZCL_ACE

/******************************************************************************
 * @fn      zclSS_RegisterCmdCallbacks
 *
 * @brief   Register an applications command callbacks
 *
 * @param   endpoint - application's endpoint
 * @param   callbacks - pointer to the callback record.
 *
 * @return  ZMemError if not able to allocate
 */
ZStatus_t zclSS_RegisterCmdCallbacks( uint8 endpoint, zclSS_AppCallbacks_t *callbacks )
{
  zclSSCBRec_t *pNewItem;
  zclSSCBRec_t *pLoop;

  // Register as a ZCL Plugin
  if ( !zclSSPluginRegisted )
  {
    zcl_registerPlugin( ZCL_SS_LOGICAL_CLUSTER_ID_IAS_ZONE,
                        ZCL_SS_LOGICAL_CLUSTER_ID_IAS_WD,
                        zclSS_HdlIncoming );
    zclSSPluginRegisted = TRUE;
  }

  // Fill in the new profile list
  pNewItem = osal_mem_alloc( sizeof( zclSSCBRec_t ) );
  if ( pNewItem == NULL )
    return (ZMemError);

  pNewItem->next = (zclSSCBRec_t *)NULL;
  pNewItem->endpoint = endpoint;
  pNewItem->CBs = callbacks;

  // Find spot in list
  if ( zclSSCBs == NULL )
  {
    zclSSCBs = pNewItem;
  }
  else
  {
    // Look for end of list
    pLoop = zclSSCBs;
    while ( pLoop->next != NULL )
      pLoop = pLoop->next;

    // Put new item at end of list
    pLoop->next = pNewItem;
  }
  return ( ZSuccess );
}

#ifdef ZCL_ZONE
/*******************************************************************************
 * @fn      zclSS_Send_IAS_ZoneStatusChangeNotificationCmd
 *
 * @brief   Call to send out a Change Notification Command
 *
 * @param   srcEP - Sending application's endpoint
 * @param   dstAddr - where you want the message to go
 * @param   zoneStatus - current zone status - bit map
 * @param   extendedStatus - bit map, currently set to All zeros ( reserved)
 *
 * @return  ZStatus_t
 */
ZStatus_t zclSS_IAS_Send_ZoneStatusChangeNotificationCmd( uint8 srcEP, afAddrType_t *dstAddr,
                                       uint16 zoneStatus, uint8 extendedStatus, 
                                       uint8 disableDefaultRsp, uint8 seqNum )
{
  uint8 buf[3];

  buf[0] = LO_UINT16( zoneStatus );
  buf[1] = HI_UINT16( zoneStatus );
  buf[2] = extendedStatus;

  return zcl_SendCommand( srcEP, dstAddr, ZCL_SS_LOGICAL_CLUSTER_ID_IAS_ZONE, TRUE,
                          COMMAND_SS_IAS_ZONE_STATUS_CHANGE_NOTIFICATION, TRUE, 
                          ZCL_FRAME_SERVER_CLIENT_DIR, disableDefaultRsp, 0, seqNum, 3, buf );
}

/*******************************************************************************
 * @fn      zclSS_Send_IAS_ZoneStatusEnrollRequestCmd
 *
 * @brief   Call to send out a Enroll Request Command
 *
 * @param   srcEP - Sending application's endpoint
 * @param   dstAddr - where you want the message to go
 * @param   zoneType - 	  current value of Zone Type attribute
 * @param   manufacturerCode - manuf. code from node descriptor for the device
 *
 * @return  ZStatus_t
 */
ZStatus_t zclSS_IAS_Send_ZoneStatusEnrollRequestCmd( uint8 srcEP, afAddrType_t *dstAddr,
                                uint16 zoneType, uint16 manufacturerCode, 
                                uint8 disableDefaultRsp, uint8 seqNum )
{
  uint8 buf[4];

  buf[0] = LO_UINT16( zoneType );
  buf[1] = HI_UINT16( zoneType );
  buf[2] = LO_UINT16( manufacturerCode );
  buf[3] = HI_UINT16( manufacturerCode );

  return zcl_SendCommand( srcEP, dstAddr, ZCL_SS_LOGICAL_CLUSTER_ID_IAS_ZONE,
                           TRUE, COMMAND_SS_IAS_ZONE_STATUS_ENROLL_REQUEST, TRUE, 
                          ZCL_FRAME_SERVER_CLIENT_DIR, disableDefaultRsp, 0, seqNum, 4, buf );
}

/*******************************************************************************
 * @fn      zclSS_IAS_Send_ZoneStatusEnrollResponseCmd
 *
 * @brief   Call to send out a Enroll Response Command
 *
 * @param   srcEP - Sending application's endpoint
 * @param   dstAddr - where you want the message to go
 * @param   responseCode -  value of  response Code
 * @param   zoneID  - index to the zone table of the CIE
 *
 * @return  ZStatus_t
 */
ZStatus_t zclSS_IAS_Send_ZoneStatusEnrollResponseCmd( uint8 srcEP, afAddrType_t *dstAddr,
                                         uint8 responseCode, uint8 zoneID, 
                                         uint8 disableDefaultRsp, uint8 seqNum )
{
  uint8 buf[2];

  buf[0] = responseCode;
  buf[1] = zoneID;

  return zcl_SendCommand( srcEP, dstAddr, ZCL_SS_LOGICAL_CLUSTER_ID_IAS_ZONE,
                           TRUE, COMMAND_SS_IAS_ZONE_STATUS_ENROLL_RESPONSE, TRUE, 
                          ZCL_FRAME_CLIENT_SERVER_DIR, disableDefaultRsp, 0, seqNum, 2, buf );
}
#endif // ZCL_ZONE

#ifdef ZCL_ACE
/*******************************************************************************
 * @fn      zclSS_Send_IAS_ACE_ArmCmd
 *
 * @brief   Call to send out a Arm  Command  ( IAS ACE Cluster )
 *
 * @param   srcEP - Sending application's endpoint
 * @param   dstAddr - where you want the message to go
 * @param   armMode -  value of armMode
 *
 * @return  ZStatus_t
 */
ZStatus_t zclSS_Send_IAS_ACE_ArmCmd( uint8 srcEP, afAddrType_t *dstAddr,
                          uint8 armMode, uint8 disableDefaultRsp, uint8 seqNum )
{
  uint8 buf[1];

  buf[0] = armMode;

  return zcl_SendCommand( srcEP, dstAddr, ZCL_SS_LOGICAL_CLUSTER_ID_IAS_ACE,
                          TRUE, COMMAND_SS_IAS_ACE_ARM, TRUE, 
                          ZCL_FRAME_CLIENT_SERVER_DIR, disableDefaultRsp, 0, seqNum, 1, buf );
}

/*********************************************************************
 * @fn      zclSS_Send_IAS_ACE_BypassCmd
 *
 * @brief   Call to send out a Bypass Command  ( IAS ACE Cluster )
 *
 * @param   srcEP - Sending application's endpoint
 * @param   dstAddr - where you want the message to go
 * @param   numberOfZones - one byte
 * @param   bypassBuf - zone IDs array of 256 entries one byte each
 *
 * @return  ZStatus_t
 */
ZStatus_t zclSS_Send_IAS_ACE_BypassCmd( uint8 srcEP, afAddrType_t *dstAddr,
                                        uint8 numberOfZones, uint8 *bypassBuf, 
                                        uint8 disableDefaultRsp, uint8 seqNum )
{
  uint8 *buf;
  uint8 *pBuf;
  uint8 len = 1 + numberOfZones;
  ZStatus_t stat;

  buf = osal_mem_alloc( len );
  if ( buf )
  {
    pBuf = buf;
    
    *pBuf++ = numberOfZones;
    osal_memcpy( pBuf, bypassBuf, numberOfZones );

    stat = zcl_SendCommand( srcEP, dstAddr, ZCL_SS_LOGICAL_CLUSTER_ID_IAS_ACE,
                            TRUE, COMMAND_SS_IAS_ACE_BYPASS, TRUE, 
                            ZCL_FRAME_CLIENT_SERVER_DIR, disableDefaultRsp, 0, seqNum, len, buf );
    osal_mem_free( buf );
  }
  else
    stat = ZFailure;
  
  return ( stat );
}

/*********************************************************************
 * @fn      zclSS_Send_IAS_ACE_GetZoneInformationCmd
 *
 * @brief   Call to send out a Get Zone Information Command ( IAS ACE Cluster )
 *
 * @param   srcEP - Sending application's endpoint
 * @param   dstAddr - where you want the message to go
 * @param   zoneID - 8 bit value from 0 to 255
 *
 * @return  ZStatus_t
 */
ZStatus_t zclSS_Send_IAS_ACE_GetZoneInformationCmd( uint8 srcEP, afAddrType_t *dstAddr, 
                                  uint8 zoneID, uint8 disableDefaultRsp, uint8 seqNum )
{
  uint8 buf[1];

  buf[0] = zoneID;

  return zcl_SendCommand( srcEP, dstAddr, ZCL_SS_LOGICAL_CLUSTER_ID_IAS_ACE,
                          TRUE, COMMAND_SS_IAS_ACE_GET_ZONE_INFORMATION, TRUE, 
                          ZCL_FRAME_CLIENT_SERVER_DIR, disableDefaultRsp, 0, seqNum, 1, buf );
}

/*******************************************************************************
 * @fn      zclSS_Send_IAS_ACE_ArmRespponse
 *
 * @brief   Call to send out a Arm Response Command ( IAS ACE Cluster )
 *
 * @param   srcEP - Sending application's endpoint
 * @param   dstAddr - where you want the message to go
 * @param   armNotification
 *
 * @return  ZStatus_t
 */
ZStatus_t zclSS_Send_IAS_ACE_ArmResponse( uint8 srcEP, afAddrType_t *dstAddr,
                  uint8 armNotification, uint8 disableDefaultRsp, uint8 seqNum )
{
  uint8 buf[1];

  buf[0] = armNotification;

  return zcl_SendCommand( srcEP, dstAddr, ZCL_SS_LOGICAL_CLUSTER_ID_IAS_ACE,
                          TRUE, COMMAND_SS_IAS_ACE_ARM_RESPONSE, TRUE, 
                          ZCL_FRAME_SERVER_CLIENT_DIR, disableDefaultRsp, 0, seqNum, 1, buf );
}

/*********************************************************************
 * @fn      zclSS_Send_IAS_ACE_GetZoneIDMapResponseCmd
 *
 * @brief   Call to send out a Get Zone ID Map Response Cmd  ( IAS ACE Cluster )
 *
 * @param   srcEP - Sending application's endpoint
 * @param   dstAddr - where you want the message to go
 * @param   zoneIDMap - pointer to an array of 16 uint16
 *
 * @return  ZStatus_t
 */
ZStatus_t zclSS_Send_IAS_ACE_GetZoneIDMapResponseCmd( uint8 srcEP, afAddrType_t *dstAddr, 
                                uint16 *zoneIDMap, uint8 disableDefaultRsp, uint8 seqNum )
{
  uint8 *buf;
  uint8 *pIndex;
  uint8 j,len = 32;
  ZStatus_t stat;

  buf = osal_mem_alloc( len );

  if ( buf )
  {
    pIndex = buf;

    for( j = 0; j < 16; j++ )
    {
      *pIndex++  = LO_UINT16( *zoneIDMap   );
      *pIndex++  = HI_UINT16( *zoneIDMap++ );
    }

    stat = zcl_SendCommand( srcEP, dstAddr, ZCL_SS_LOGICAL_CLUSTER_ID_IAS_ACE,
                            TRUE, COMMAND_SS_IAS_ACE_GET_ZONE_ID_MAP_RESPONSE, TRUE, 
                            ZCL_FRAME_SERVER_CLIENT_DIR, disableDefaultRsp, 0, seqNum, len, buf );
    osal_mem_free( buf );
  }
  else
    stat = ZMemError;

  return ( stat );

}

/*******************************************************************************
 * @fn      zclSS_Send_IAS_ACE_GetZoneInformationResponseCmd
 *
 * @brief   Call to send out Get Zone Information Response Cmd (IAS ACE Cluster)
 *
 * @param   srcEP - Sending application's endpoint
 * @param   dstAddr - where you want the message to go
 * @param   zoneID - 8 bit value from 0 to 255
 * @param   zoneType - 16 bit
 * @param   ieeeAddress - pointer to 64 bit address ( 8bytes*8)
 *
 * @return  ZStatus_t
 */
ZStatus_t zclSS_Send_IAS_ACE_GetZoneInformationResponseCmd( uint8 srcEP, afAddrType_t *dstAddr, 
                                             uint8 zoneID, uint16 zoneType, uint8 *ieeeAddress, 
                                             uint8 disableDefaultRsp, uint8 seqNum )
{
  uint8 *buf;
  uint8 len = 11; // zoneID (1) + zoneType (2) + zoneAddress (8)
  ZStatus_t stat;

  buf = osal_mem_alloc( len );

  if ( buf )
  {
    buf[0] = zoneID;
    buf[1] = LO_UINT16( zoneType);
    buf[2] = HI_UINT16( zoneType);
    osal_cpyExtAddr( &buf[3], ieeeAddress );

    stat = zcl_SendCommand( srcEP, dstAddr, ZCL_SS_LOGICAL_CLUSTER_ID_IAS_ACE, TRUE,
                            COMMAND_SS_IAS_ACE_GET_ZONE_INFORMATION_RESPONSE, TRUE,
                            ZCL_FRAME_SERVER_CLIENT_DIR, disableDefaultRsp, 0, seqNum, len, buf );
    osal_mem_free( buf );
  }
  else
    stat = ZMemError;

  return ( stat );
}
#endif // ZCL_ACE

#ifdef ZCL_WD
/*******************************************************************************
 * @fn      zclSS_Send_IAS_WD_StartWarningCmd
 *
 * @brief   Call to send out a Start Warning  Command (IAS WD Cluster)
 *
 * @param   warning - 3 bytes  of type	zclCmdSSWDStartWarningPayload_t
 *
 * @return  ZStatus_t
 */
ZStatus_t zclSS_Send_IAS_WD_StartWarningCmd( uint8 srcEP, afAddrType_t *dstAddr,
                                       zclCmdSSWDStartWarningPayload_t *warning, 
                                       uint8 disableDefaultRsp, uint8 seqNum )
{
  uint8 buf[3];

  buf[0] = warning->warningmessage.warningbyte;
  buf[1] = LO_UINT16( warning->duration );
  buf[2] = HI_UINT16( warning->duration );

  return zcl_SendCommand( srcEP, dstAddr, ZCL_SS_LOGICAL_CLUSTER_ID_IAS_WD,
                          TRUE, COMMAND_SS_IAS_WD_START_WARNING, TRUE, 
                          ZCL_FRAME_CLIENT_SERVER_DIR, disableDefaultRsp, 0, seqNum, 3, buf );
}

/******************************************************************************
 * @fn      zclSS_Send_IAS_WD_StartWarningCmd
 *
 * @brief   Call to send out a Squawk Command  ( IAS WD Cluster )
 *
 * @param   squawk - one byte of type zclCmdSSWDSquawkPayload_t
 *
 * @return  ZStatus_t
 */
ZStatus_t zclSS_Send_IAS_WD_SquawkCmd( uint8 srcEP, afAddrType_t *dstAddr,
                                       zclCmdSSWDSquawkPayload_t *squawk, 
                                       uint8 disableDefaultRsp, uint8 seqNum )
{
  uint8 buf[1];
  buf[0] = squawk->squawkbyte;

  return zcl_SendCommand( srcEP, dstAddr, ZCL_SS_LOGICAL_CLUSTER_ID_IAS_WD,
                           TRUE, COMMAND_SS_IAS_WD_SQUAWK, TRUE, 
                          ZCL_FRAME_CLIENT_SERVER_DIR, disableDefaultRsp, 0, seqNum, 1, buf);
}
#endif // ZCL_WD

#if defined(ZCL_ZONE) || defined(ZCL_ACE) || defined(ZCL_WD)
/*********************************************************************
 * @fn      zclSS_FindCallbacks
 *
 * @brief   Find the callbacks for an endpoint
 *
 * @param   endpoint
 *
 * @return  pointer to the callbacks
 */
static zclSS_AppCallbacks_t *zclSS_FindCallbacks( uint8 endpoint )
{
  zclSSCBRec_t *pCBs;
  
  pCBs = zclSSCBs;
  while ( pCBs )
  {
    if ( pCBs->endpoint == endpoint )
      return ( pCBs->CBs );
  }
  return ( (zclSS_AppCallbacks_t *)NULL );
}
#endif // ZCL_ZONE || ZCL_ACE || ZCL_WD

/*********************************************************************
 * @fn      zclSS_HdlIncoming
 *
 * @brief   Callback from ZCL to process incoming Commands specific
 *          to this cluster library or Profile commands for attributes
 *          that aren't in the attribute list
 *
 * @param   pInMsg - pointer to the incoming message
 * @param   logicalClusterID
 *
 * @return  ZStatus_t
 */
static ZStatus_t zclSS_HdlIncoming( zclInHdlrMsg_t *pInHdlrMsg )
{
  ZStatus_t stat = ZSuccess;

  if ( zcl_ClusterCmd( pInHdlrMsg->msg->hdr.fc.type ) )
  {
    // Is this a manufacturer specific command?
    if ( pInHdlrMsg->msg->hdr.fc.manuSpecific == 0 ) 
    {
      stat = zclSS_HdlInSpecificCommands( pInHdlrMsg->msg, pInHdlrMsg->logicalClusterID );
    }
    else
    {
      // We don't support any manufacturer specific command -- ignore it.
      stat = ZFailure;
    }
  }
  else
  {
    // Handle all the normal (Read, Write...) commands
    stat = ZFailure;
  }
  return ( stat );
}

/*********************************************************************
 * @fn      zclSS_HdlInSpecificCommands
 *
 * @brief   Callback from ZCL to process incoming Commands specific
 *          to this cluster library

 * @param   pInMsg - pointer to the incoming message
 * @param   logicalClusterID
 *
 * @return  ZStatus_t
 */
static ZStatus_t zclSS_HdlInSpecificCommands( zclIncoming_t *pInMsg, uint16 logicalClusterID )
{
  ZStatus_t stat = ZSuccess;

  switch ( logicalClusterID )				
  {
#ifdef ZCL_ZONE
    case ZCL_SS_LOGICAL_CLUSTER_ID_IAS_ZONE:
      if ( zcl_ServerCmd( pInMsg->hdr.fc.direction ) )
        stat = zclSS_ProcessInZoneStatusCmdsServer( pInMsg );
      else
        stat = zclSS_ProcessInZoneStatusCmdsClient( pInMsg );
      break;
#endif // ZCL_ZONE

#ifdef ZCL_ACE
    case ZCL_SS_LOGICAL_CLUSTER_ID_IAS_ACE:
      if ( zcl_ServerCmd( pInMsg->hdr.fc.direction ) )
        stat = zclSS_ProcessInACECmdsServer( pInMsg );
      else
        stat = zclSS_ProcessInACECmdsClient( pInMsg );
      break;
#endif // ZCL_ACE

#ifdef ZCL_WD      
    case ZCL_SS_LOGICAL_CLUSTER_ID_IAS_WD:
      stat = zclSS_ProcessInWDCmds( pInMsg );
      break;
#endif // ZCL_WD
      
    default:
      stat = ZFailure;
      break;
  }

  return ( stat );
}

#ifdef ZCL_ZONE
/*********************************************************************
 * @fn      zclSS_ProcessInZoneStatusCmdsServer
 *
 * @brief   Callback from ZCL to process incoming Commands specific
 *          to this cluster library on a command ID basis

 * @param   pInMsg - pointer to the incoming message
 *
 * @return  ZStatus_t
 */
static ZStatus_t zclSS_ProcessInZoneStatusCmdsServer( zclIncoming_t *pInMsg )
{
  ZStatus_t stat = ZSuccess;
  
  if ( pInMsg->hdr.commandID == COMMAND_SS_IAS_ZONE_STATUS_ENROLL_RESPONSE )
    zclSS_ProcessInCmd_ZoneStatus_EnrollResponse( pInMsg );
  else
    stat = ZFailure;

  return ( stat );
}

/*********************************************************************
 * @fn      zclSS_ProcessInZoneStatusCmdsClient
 *
 * @brief   Callback from ZCL to process incoming Commands specific
 *          to this cluster library on a command ID basis

 * @param   pInMsg - pointer to the incoming message
 *
 * @return  ZStatus_t
 */
static ZStatus_t zclSS_ProcessInZoneStatusCmdsClient( zclIncoming_t *pInMsg )
{
  ZStatus_t stat = ZSuccess;

  switch ( pInMsg->hdr.commandID )				
  {
    case COMMAND_SS_IAS_ZONE_STATUS_CHANGE_NOTIFICATION:
      zclSS_ProcessInCmd_ZoneStatus_ChangeNotification( pInMsg );
      break;

    case COMMAND_SS_IAS_ZONE_STATUS_ENROLL_REQUEST:
      stat = zclSS_ProcessInCmd_ZoneStatus_EnrollRequest( pInMsg );
      break;

    default:
      stat = ZFailure;
      break;
  }

  return ( stat );
}
#endif // ZCL_ZONE

#ifdef ZCL_ACE
/*********************************************************************
 * @fn      zclSS_ProcessInACECmdsServer
 *
 * @brief   Callback from ZCL to process incoming Commands specific
 *          to this cluster library on a command ID basis

 * @param   pInMsg - pointer to the incoming message
 *
 * @return  ZStatus_t
 */
static ZStatus_t zclSS_ProcessInACECmdsServer( zclIncoming_t *pInMsg )
{
  ZStatus_t stat = ZSuccess;

  switch ( pInMsg->hdr.commandID )				
  {
    case COMMAND_SS_IAS_ACE_ARM:
      stat = zclSS_ProcessInCmd_ACE_Arm( pInMsg );
      break;

    case COMMAND_SS_IAS_ACE_BYPASS:
      zclSS_ProcessInCmd_ACE_Bypass( pInMsg );
      break;

    case COMMAND_SS_IAS_ACE_EMERGENCY:
      zclSS_ProcessInCmd_ACE_Emergency( pInMsg );
      break;

    case COMMAND_SS_IAS_ACE_FIRE:
      zclSS_ProcessInCmd_ACE_Fire( pInMsg );
      break;

    case COMMAND_SS_IAS_ACE_PANIC:
      zclSS_ProcessInCmd_ACE_Panic( pInMsg );
      break;

    case COMMAND_SS_IAS_ACE_GET_ZONE_ID_MAP:
      stat = zclSS_ProcessInCmd_ACE_GetZoneIDMap( pInMsg );
      break;

    case COMMAND_SS_IAS_ACE_GET_ZONE_INFORMATION:
      stat = zclSS_ProcessInCmd_ACE_GetZoneInformation( pInMsg );
      break;
      
    default:
      stat = ZFailure;
      break;
  }

  return ( stat );
}

/*********************************************************************
 * @fn      zclSS_ProcessInACECmdsClient
 *
 * @brief   Callback from ZCL to process incoming Commands specific
 *          to this cluster library on a command ID basis

 * @param   pInMsg - pointer to the incoming message
 *
 * @return  ZStatus_t
 */
static ZStatus_t zclSS_ProcessInACECmdsClient( zclIncoming_t *pInMsg )
{
  ZStatus_t stat = ZSuccess;
  
  switch ( pInMsg->hdr.commandID )				
  {
    case COMMAND_SS_IAS_ACE_ARM_RESPONSE:
      zclSS_ProcessInCmd_ACE_ArmResponse( pInMsg );
      break;

    case COMMAND_SS_IAS_ACE_GET_ZONE_ID_MAP_RESPONSE:
      zclSS_ProcessInCmd_ACE_GetZoneIDMapResponse( pInMsg );
      break;

    case COMMAND_SS_IAS_ACE_GET_ZONE_INFORMATION_RESPONSE:
      zclSS_ProcessInCmd_ACE_GetZoneInformationResponse( pInMsg );
      break;

    default:
      stat = ZFailure;
      break;
  }

  return ( stat );
}
#endif // ZCL_ACE

#ifdef ZCL_ZONE
/*********************************************************************
 * @fn      zclSS_AddZone
 *
 * @brief   Add a zone for an endpoint
 *
 * @param   endpoint -
 * @param   zone - new zone item
 *
 * @return  ZStatus_t
 */
static ZStatus_t zclSS_AddZone( uint8 endpoint, IAS_ACE_ZoneTable_t *zone )
{
  zclSS_ZoneItem_t *pNewItem;
  zclSS_ZoneItem_t *pLoop;

  // Fill in the new profile list
  pNewItem = osal_mem_alloc( sizeof( zclSS_ZoneItem_t ) );
  if ( pNewItem == NULL )
    return ( ZMemError );

  // Fill in the plugin record.
  pNewItem->next = (zclSS_ZoneItem_t *)NULL;
  pNewItem->endpoint = endpoint;
  osal_memcpy( (uint8*)&(pNewItem->zone), (uint8*)zone, sizeof ( IAS_ACE_ZoneTable_t ));

  // Find spot in list
  if (  zclSS_ZoneTable == NULL )
  {
    zclSS_ZoneTable = pNewItem;
  }
  else
  {
    // Look for end of list
    pLoop = zclSS_ZoneTable;
    while ( pLoop->next != NULL )
      pLoop = pLoop->next;

    // Put new item at end of list
    pLoop->next = pNewItem;
  }

  return ( ZSuccess );
}

/*********************************************************************
 * @fn      zclSS_CountAllZones
 *
 * @brief   Count the total number of zones
 *
 * @param   none
 *
 * @return  number of zones
 */
uint8 zclSS_CountAllZones( void )
{
  zclSS_ZoneItem_t *pLoop;
  uint8 cnt = 0;

  // Look for end of list
  pLoop = zclSS_ZoneTable;
  while ( pLoop )
  {
    cnt++;
    pLoop = pLoop->next;
  }
  return ( cnt );
}

/*********************************************************************
 * @fn      zclSS_GetNextFreeZoneID
 *
 * @brief   Get the next free zone ID
 *
 * @param   none
 *
 * @return  free zone ID (0-254)
 */
static uint8 zclSS_GetNextFreeZoneID( void )
{
  static uint8 nextAvailZoneID = 0;

  if ( zclSS_ZoneIDAvailable( nextAvailZoneID ) == FALSE )
  {
    uint8 zoneID = nextAvailZoneID;

    // Look for next available zone ID
    do
    {
      if ( ++zoneID == ZCL_SS_MAX_ZONE_ID )
        zoneID = 0; // roll over

    } while ( (zoneID != nextAvailZoneID) && (zclSS_ZoneIDAvailable( nextAvailZoneID ) == FALSE) );
    
    // Did we found a free zone ID?
    if ( zoneID != nextAvailZoneID )
      nextAvailZoneID = zoneID;
  }
  
  return ( nextAvailZoneID );
}


/*********************************************************************
 * @fn      zclSS_ZoneIDAvailable
 *
 * @brief   Check to see whether zoneID is available for use
 *
 * @param   zoneID - ID to look for zone
 *
 * @return  TRUE if zoneID is available, FALSE otherwise
 */
static uint8 zclSS_ZoneIDAvailable( uint8 zoneID )
{
  zclSS_ZoneItem_t *pLoop;

  if ( zoneID < ZCL_SS_MAX_ZONE_ID )
  {
    pLoop = zclSS_ZoneTable;
    while ( pLoop )
    {
      if ( pLoop->zone.zoneID == zoneID  )
      {
        return ( FALSE );
      }
      pLoop = pLoop->next;
    }
    
    // Zone ID not in use
    return ( TRUE );
  }

  return ( FALSE );
}
#endif // ZCL_ZONE

#if defined(ZCL_ZONE) || defined(ZCL_ACE)
/*********************************************************************
 * @fn      zclSS_FindZone
 *
 * @brief   Find a zone with endpoint and ZoneID
 *
 * @param   endpoint -
 * @param   zoneID - ID to look for zone
 *
 * @return  a pointer to the zone information, NULL if not found
 */
static IAS_ACE_ZoneTable_t *zclSS_FindZone( uint8 endpoint, uint8 zoneID )
{
  zclSS_ZoneItem_t *pLoop;

  // Look for end of list
  pLoop = zclSS_ZoneTable;
  while ( pLoop )
  {
    if ( pLoop->endpoint == endpoint && pLoop->zone.zoneID == zoneID  )
    {
      return ( &(pLoop->zone) );
    }
    pLoop = pLoop->next;
  }

  return ( (IAS_ACE_ZoneTable_t *)NULL );
}

/*********************************************************************
 * @fn      zclSS_RemoveZone
 *
 * @brief   Remove a zone with endpoint and zoneID
 *
 * @param   endpoint -
 * @param   zoneID - ID to look for zone
 *
 * @return  TRUE if removed, FALSE if not found
 */
uint8 zclSS_RemoveZone( uint8 endpoint, uint8 zoneID )
{
  zclSS_ZoneItem_t *pLoop;
  zclSS_ZoneItem_t *pPrev;

  // Look for end of list
  pLoop = zclSS_ZoneTable;
  pPrev = NULL;
  while ( pLoop )
  {
    if ( pLoop->endpoint == endpoint && pLoop->zone.zoneID == zoneID )
    {
      if ( pPrev == NULL )
        zclSS_ZoneTable = pLoop->next;
      else
        pPrev->next = pLoop->next;

      // Free the memory
      osal_mem_free( pLoop );

      return ( TRUE );
    }
    pPrev = pLoop;
    pLoop = pLoop->next;
  }

  return ( FALSE );
}

/*********************************************************************
 * @fn      zclSS_UpdateZoneAddress
 *
 * @brief   Update Zone Address for zoneID
 *
 * @param   endpoint -
 * @param   zoneID - ID to look for zone
 * @param   ieeeAddr - Device IEEE Address
 *
 * @return  none
 */
void zclSS_UpdateZoneAddress( uint8 endpoint, uint8 zoneID, uint8 *ieeeAddr )
{
  IAS_ACE_ZoneTable_t *zone;
  
  zone = zclSS_FindZone( endpoint, zoneID );
  if ( zone != NULL )
  {
    // Update the zone address
    osal_cpyExtAddr( zone->zoneAddress, ieeeAddr );
  }
}
#endif // ZCL_ZONE || ZCL_ACE

#ifdef ZCL_ZONE
/*******************************************************************************
 * @fn      zclSS_ProcessInCmd_ZoneStatus_ChangeNotification
 *
 * @brief   Process in the received StatusChangeNotification Command.
 *
 * @param   pInMsg - pointer to the incoming message
 *
 * @return  ZStatus_t
 */
static void zclSS_ProcessInCmd_ZoneStatus_ChangeNotification( zclIncoming_t *pInMsg )
{
  zclSS_AppCallbacks_t *pCBs;

  if ( pInMsg->hdr.commandID != COMMAND_SS_IAS_ZONE_STATUS_CHANGE_NOTIFICATION )
    return;   // Error ignore the command

  pCBs = zclSS_FindCallbacks( pInMsg->msg->endPoint );
  if ( pCBs && pCBs->pfnChangeNotification )
  {
    zclZoneChangeNotif_t cmd;
    
    cmd.zoneStatus = BUILD_UINT16( pInMsg->pData[0], pInMsg->pData[1] );
    cmd.extendedStatus = pInMsg->pData[2];
    
    pCBs->pfnChangeNotification( &cmd );
  }
}

/*******************************************************************************
 * @fn      zclSS_ProcessInCmd_ZoneStatus_EnrollRequest
 *
 * @brief   Process in the received StatusEnrollRequest Command.
 *
 * @param   pInMsg - pointer to the incoming message
 *
 * @return  ZStatus_t
 */
static ZStatus_t zclSS_ProcessInCmd_ZoneStatus_EnrollRequest( zclIncoming_t *pInMsg )
{
  zclSS_AppCallbacks_t *pCBs;
  IAS_ACE_ZoneTable_t zone;
  uint16 zoneType;
  uint16 manuCode;
  uint8 zoneID;
  uint8 status;

  if ( pInMsg->hdr.commandID != COMMAND_SS_IAS_ZONE_STATUS_ENROLL_REQUEST )
    return ( ZFailure );   // Error ignore the command

  zoneType = BUILD_UINT16( pInMsg->pData[0], pInMsg->pData[1] );
  manuCode = BUILD_UINT16( pInMsg->pData[2], pInMsg->pData[3] );
  
  if ( zclSS_ZoneTypeSupported( zoneType ) )
  {
    // What if the entry already exists?????
    if ( zclSS_CountAllZones() < ZCL_SS_MAX_ZONES-1 )
    {
      // Add zone to the table
      zoneID = zclSS_GetNextFreeZoneID();
      
      zone.zoneID = zoneID;
      zone.zoneType = zoneType;

      // The application will fill in the right IEEE Address later
      osal_cpyExtAddr( zone.zoneAddress, (void *)zclSS_UknownIeeeAddress );
      
      if ( zclSS_AddZone( pInMsg->msg->endPoint, &zone ) == ZSuccess )
      {
        status = ZCL_STATUS_SUCCESS;
      }
      else
      {
        // CIE does not permit new zones to enroll at this time
        status = SS_IAS_ZONE_STATUS_ENROLL_RESPONSE_CODE_NO_ENROLL_PERMIT;
      }
    }
    else
    {
      // CIE reached its limit of number of enrolled zones
      status = SS_IAS_ZONE_STATUS_ENROLL_RESPONSE_CODE_TOO_MANY_ZONES;
    }
  }
  else
  {
    // Zone type is not known to CIE and is not supported
    status = SS_IAS_ZONE_STATUS_ENROLL_RESPONSE_CODE_NOT_SUPPORTED;
  }
  
  // Send a response back
  zclSS_IAS_Send_ZoneStatusEnrollResponseCmd( pInMsg->msg->endPoint, &(pInMsg->msg->srcAddr),
                                              status, zoneID, true, pInMsg->hdr.transSeqNum );
  if ( status == ZCL_STATUS_SUCCESS )
  {
    // Callback the application so it can fill in the Device IEEE Address  
    pCBs = zclSS_FindCallbacks( pInMsg->msg->endPoint );
    if ( pCBs && pCBs->pfnEnrollRequest )
    {
      zclZoneEnrollReq_t req;
      
      req.srcAddr = &(pInMsg->msg->srcAddr);
      req.zoneID = zoneID;  
      req.zoneType = zoneType;
      req.manufacturerCode = manuCode;
        
      pCBs->pfnEnrollRequest( &req );
    }
  }
  
  return ( ZCL_STATUS_CMD_HAS_RSP );
}

/*******************************************************************************
 * @fn      zclSS_ProcessInCmd_ZoneStatus_EnrollResponse
 *
 * @brief   Process in the received STATUS_ENROLL_RESPONSE Command.
 *
 * @param   pInMsg - pointer to the incoming message
 *
 * @return  ZStatus_t
 */
static void zclSS_ProcessInCmd_ZoneStatus_EnrollResponse(zclIncoming_t *pInMsg )
{
  zclSS_AppCallbacks_t *pCBs;

  if ( pInMsg->hdr.commandID != COMMAND_SS_IAS_ZONE_STATUS_ENROLL_RESPONSE )
    return;   // Error ignore the command

  pCBs = zclSS_FindCallbacks( pInMsg->msg->endPoint );
  if ( pCBs && pCBs->pfnEnrollResponse )
  {
    zclZoneEnrollRsp_t rsp;
    
    rsp.responseCode = pInMsg->pData[0];
    rsp.zoneID = pInMsg->pData[1];
        
    pCBs->pfnEnrollResponse( &rsp );
  }
}
#endif // ZCL_ZONE

#ifdef ZCL_ACE
/*********************************************************************
 * @fn      zclSS_ProcessInCmd_ACE_Arm
 *
 * @brief   Process in the received Arm Command.
 *
 * @param   pInMsg - pointer to the incoming message
 *
 */
static ZStatus_t zclSS_ProcessInCmd_ACE_Arm( zclIncoming_t *pInMsg )
{
  zclSS_AppCallbacks_t *pCBs;
  uint8 armNotification;

  if  ( pInMsg->hdr.commandID != COMMAND_SS_IAS_ACE_ARM )
    return ( ZFailure );   // Error ignore the command

  pCBs = zclSS_FindCallbacks( pInMsg->msg->endPoint );
  if ( pCBs && pCBs->pfnACE_Arm )
  {
    armNotification = pCBs->pfnACE_Arm( pInMsg->pData[0] );
  
    // Send a response back
    zclSS_Send_IAS_ACE_ArmResponse( pInMsg->msg->endPoint, &(pInMsg->msg->srcAddr),
                                    armNotification, true, pInMsg->hdr.transSeqNum );
  }
  
  return ( ZCL_STATUS_CMD_HAS_RSP );
}

/*********************************************************************
 * @fn      zclSS_ProcessInCmd_ACE_Bypass
 *
 * @brief   Process in the received Bypass Command.
 *
 * @param   pInMsg - pointer to the incoming message
 *
 */
static void zclSS_ProcessInCmd_ACE_Bypass( zclIncoming_t *pInMsg )
{
  zclSS_AppCallbacks_t *pCBs;

  if  ( pInMsg->hdr.commandID != COMMAND_SS_IAS_ACE_BYPASS )
    return;   // Error ignore the command

  pCBs = zclSS_FindCallbacks( pInMsg->msg->endPoint );
  if ( pCBs && pCBs->pfnACE_Bypass )
  {
    zclACEBypass_t cmd;
    
    cmd.numberOfZones = pInMsg->pData[0];
    cmd.bypassBuf = &(pInMsg->pData[1]);
    
    pCBs->pfnACE_Bypass( &cmd ) ;
  }
}

/*********************************************************************
 * @fn      zclSS_ProcessInCmd_ACE_Emergency
 *
 * @brief   Process in the received Emergency Command.
 *
 * @param   pInMsg - pointer to the incoming message
 *
 */
static void zclSS_ProcessInCmd_ACE_Emergency( zclIncoming_t *pInMsg )
{
  zclSS_AppCallbacks_t *pCBs;

  if  ( pInMsg->hdr.commandID != COMMAND_SS_IAS_ACE_EMERGENCY )
    return;   // Error ignore the command

  pCBs = zclSS_FindCallbacks( pInMsg->msg->endPoint );
  if ( pCBs && pCBs->pfnACE_Emergency )
    pCBs->pfnACE_Emergency();

}

/*********************************************************************
 * @fn      zclSS_ProcessInCmd_ACE_Fire
 *
 * @brief   Process in the received Fire Command.
 *
 * @param   pInMsg - pointer to the incoming message
 *
 */
static void zclSS_ProcessInCmd_ACE_Fire( zclIncoming_t *pInMsg )
{
  zclSS_AppCallbacks_t *pCBs;

  if  ( pInMsg->hdr.commandID != COMMAND_SS_IAS_ACE_FIRE )
    return;   // Error ignore the command

  pCBs = zclSS_FindCallbacks( pInMsg->msg->endPoint );
  if ( pCBs && pCBs->pfnACE_Fire )
    pCBs->pfnACE_Fire();
}

/*********************************************************************
 * @fn      zclSS_ProcessInCmd_ACE_Panic
 *
 * @brief   Process in the received Panic Command.
 *
 * @param   pInMsg - pointer to the incoming message
 *
 */
static void zclSS_ProcessInCmd_ACE_Panic( zclIncoming_t *pInMsg )
{
  zclSS_AppCallbacks_t *pCBs;

  if  ( pInMsg->hdr.commandID != COMMAND_SS_IAS_ACE_PANIC )
    return;   // Error ignore the command

  pCBs = zclSS_FindCallbacks( pInMsg->msg->endPoint );
  if ( pCBs && pCBs->pfnACE_Panic )
    pCBs->pfnACE_Panic();
}

/*********************************************************************
 * @fn      zclSS_ProcessInCmd_ACE_GetZoneIDMap
 *
 * @brief   Process in the received GetZoneIDMap Command.
 *
 * @param   pInMsg - pointer to the incoming message
 *
 */
static ZStatus_t zclSS_ProcessInCmd_ACE_GetZoneIDMap( zclIncoming_t *pInMsg )
{
  uint16 zoneIDMap[16];
  uint16 mapSection;
  uint8 zoneID;
  uint8 i, j;

  if  ( pInMsg->hdr.commandID != COMMAND_SS_IAS_ACE_GET_ZONE_ID_MAP )
    return ( ZFailure );   // Error ignore the command

  for ( i = 0; i < 16; i++ )
  {
    mapSection = 0;

    // Find out Zone IDs that are allocated for this map section
    for ( j = 0; j < 16; j++ )
    {
      zoneID = 16 * i + j;
      if ( zclSS_FindZone( pInMsg->msg->endPoint, zoneID ) != NULL )
      {
        // Set the corresponding bit
        mapSection |= (0x01 << j );
      }
    }
    zoneIDMap[i] = mapSection;
  }
  
  // Send a response back
  zclSS_Send_IAS_ACE_GetZoneIDMapResponseCmd( pInMsg->msg->endPoint, &(pInMsg->msg->srcAddr),
                                              zoneIDMap, true, pInMsg->hdr.transSeqNum );
  return ( ZCL_STATUS_CMD_HAS_RSP );
}

/*********************************************************************
 * @fn      zclSS_ProcessInCmd_ACE_GetZoneInformation
 *
 * @brief   Process in the received GetZoneInformation Command.
 *
 * @param   pInMsg - pointer to the incoming message
 *
 */
static ZStatus_t zclSS_ProcessInCmd_ACE_GetZoneInformation( zclIncoming_t *pInMsg )
{
  IAS_ACE_ZoneTable_t zone;
  IAS_ACE_ZoneTable_t *pZone;

  if  ( pInMsg->hdr.commandID != COMMAND_SS_IAS_ACE_GET_ZONE_INFORMATION )
    return ( ZFailure );   // Error ignore the command
  
  pZone = zclSS_FindZone( pInMsg->msg->endPoint, pInMsg->pData[0] );
  if ( pZone == NULL )
  {
    // Zone not found
    pZone = &zone;
    pZone->zoneID = pInMsg->pData[0];
    pZone->zoneType = SS_IAS_ZONE_TYPE_INVALID_ZONE_TYPE;
    osal_cpyExtAddr( pZone->zoneAddress, (void *)zclSS_UknownIeeeAddress );
  }
  
  // Send a response back
  zclSS_Send_IAS_ACE_GetZoneInformationResponseCmd( pInMsg->msg->endPoint, &(pInMsg->msg->srcAddr),
                                                    pZone->zoneID, pZone->zoneType,
                                                    pZone->zoneAddress, true, pInMsg->hdr.transSeqNum );
  return ( ZCL_STATUS_CMD_HAS_RSP );
}

/*********************************************************************
 * @fn      zclSS_ProcessInCmd_ACE_ArmResponse
 *
 * @brief   Process in the received Arm Response Command.
 *
 * @param   pInMsg - pointer to the incoming message
 *
 */
static void zclSS_ProcessInCmd_ACE_ArmResponse( zclIncoming_t *pInMsg )
{
  zclSS_AppCallbacks_t *pCBs;

  if  ( pInMsg->hdr.commandID != COMMAND_SS_IAS_ACE_ARM_RESPONSE )
    return;   // Error ignore the command

  pCBs = zclSS_FindCallbacks( pInMsg->msg->endPoint );
  if ( pCBs && pCBs->pfnACE_ArmResponse )
    pCBs->pfnACE_ArmResponse(pInMsg->pData[0]);
}


/*********************************************************************
 * @fn      zclSS_ProcessInCmd_ACE_GetZoneIDMapResponse
 *
 * @brief   Process in the received GetZoneIDMapResponse Command.
 *
 * @param   pInMsg - pointer to the incoming message
 *
 */
static void zclSS_ProcessInCmd_ACE_GetZoneIDMapResponse( zclIncoming_t *pInMsg )
{
  zclSS_AppCallbacks_t *pCBs;

  uint16 *buf;
  uint16 *pIndex;
  uint8 *pData;
  uint8 i, len = 32; // 16 fields of 2 octects

  if ( pInMsg->hdr.commandID != COMMAND_SS_IAS_ACE_GET_ZONE_ID_MAP_RESPONSE )
    return;   // Error ignore the command

  buf = osal_mem_alloc( len );

  if ( buf )
  {
    pIndex = buf;
    pData = pInMsg->pData;
    
    for ( i = 0; i < 16; i++ )
    {
      *pIndex++ = BUILD_UINT16( pData[0], pData[1] );
      pData += 2;
    }

    pCBs = zclSS_FindCallbacks( pInMsg->msg->endPoint );
    if ( pCBs && pCBs->pfnACE_GetZoneIDMapResponse )
      pCBs->pfnACE_GetZoneIDMapResponse( buf );

    osal_mem_free( buf );
  }
}

/*********************************************************************
 * @fn      zclSS_ProcessInCmd_ACE_GetZoneInformationResponse
 *
 * @brief   Process in the received GetZoneInformationResponse Command.
 *
 * @param   pInMsg - pointer to the incoming message
 *
 */
static void zclSS_ProcessInCmd_ACE_GetZoneInformationResponse( zclIncoming_t *pInMsg )
{
  zclSS_AppCallbacks_t *pCBs;

  if  ( pInMsg->hdr.commandID != COMMAND_SS_IAS_ACE_GET_ZONE_INFORMATION_RESPONSE )
    return;   // Error ignore the command

  pCBs = zclSS_FindCallbacks( pInMsg->msg->endPoint );
  if ( pCBs && pCBs->pfnACE_GetZoneInformationResponse )
  {
    zclACEGetZoneInfoRsp_t rsp;
    
    rsp.zoneID = pInMsg->pData[0];
    rsp.zoneType = BUILD_UINT16( pInMsg->pData[1], pInMsg->pData[2] );
    rsp.ieeeAddr = &(pInMsg->pData[3]);
    
    pCBs->pfnACE_GetZoneInformationResponse( &rsp );
  }
}
#endif // ZCL_ACE

#ifdef ZCL_WD
/*********************************************************************
 * @fn      zclSS_ProcessInWDCmds
 *
 * @brief   Callback from ZCL to process incoming Commands specific
 *          to this cluster library on a command ID basis

 * @param   pInMsg - pointer to the incoming message
 *
 * @return  ZStatus_t
 */
static ZStatus_t zclSS_ProcessInWDCmds( zclIncoming_t *pInMsg )
{
  ZStatus_t stat = ZSuccess;

  switch ( pInMsg->hdr.commandID )				
  {
    case COMMAND_SS_IAS_WD_START_WARNING:
      zclSS_ProcessInCmd_WD_StartWarning( pInMsg );
      break;

    case COMMAND_SS_IAS_WD_SQUAWK:
      zclSS_ProcessInCmd_WD_Squawk( pInMsg );
      break;

    default:
      stat = ZFailure;
      break;
  }

  return ( stat );
}

/*********************************************************************
 * @fn      zclSS_ProcessInCmd_WD_StartWarning
 *
 * @brief   Process in the received StartWarning Command.
 *
 * @param   pInMsg - pointer to the incoming message
 *
 */
static void zclSS_ProcessInCmd_WD_StartWarning( zclIncoming_t *pInMsg )
{
  zclSS_AppCallbacks_t *pCBs;

  if  ( pInMsg->hdr.commandID != COMMAND_SS_IAS_WD_START_WARNING )
    return;   // Error ignore the command

  pCBs = zclSS_FindCallbacks( pInMsg->msg->endPoint );
  if ( pCBs && pCBs->pfnWD_StartWarning )
  {
    zclWDStartWarning_t cmd;
    
    cmd.warnings.warningbyte = pInMsg->pData[0];
    cmd.duration = BUILD_UINT16( pInMsg->pData[1], pInMsg->pData[2] );
    
    pCBs->pfnWD_StartWarning( &cmd );
  }
}

/*********************************************************************
 * @fn      zclSS_ProcessInCmd_WD_Squawk
 *
 * @brief   Process in the received Squawk Command.
 *
 * @param   pInMsg - pointer to the incoming message
 *
 */
static void zclSS_ProcessInCmd_WD_Squawk( zclIncoming_t *pInMsg )
{
  zclSS_AppCallbacks_t *pCBs;
  zclCmdSSWDSquawkPayload_t squawk;

  if  ( pInMsg->hdr.commandID != COMMAND_SS_IAS_WD_SQUAWK )
    return;   // Error ignore the command

  pCBs = zclSS_FindCallbacks( pInMsg->msg->endPoint );
  if ( pCBs && pCBs->pfnWD_Squawk )
  {
    squawk.squawkbyte = pInMsg->pData[0];
    pCBs->pfnWD_Squawk( squawk );
  }
}
#endif // ZCL_WD

/*******************************************************************************
*******************************************************************************/

