/************************************************************************************************** Filename: zcl_general.c Revised: $Date: 2011-12-14 16:30:16 -0800 (Wed, 14 Dec 2011) $ Revision: $Revision: 28678 $ Description: Zigbee Cluster Library - General. This application receives all ZCL messages and initially parses them before passing to application. Copyright 2006-2011 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 "OSAL_Nv.h" #include "zcl.h" #include "zcl_general.h" #include "ZDApp.h" #if defined ( INTER_PAN ) #include "stub_aps.h" #endif /********************************************************************* * MACROS */ #define locationTypeAbsolute( a ) ( (a) & LOCATION_TYPE_ABSOLUTE ) #define locationType2D( a ) ( (a) & LOCATION_TYPE_2_D ) #define locationTypeCoordinateSystem( a ) ( (a) & LOCATION_TYPE_COORDINATE_SYSTEM ) #ifdef ZCL_SCENES #define zclGeneral_ScenesRemaingCapacity() ( ZCL_GEN_MAX_SCENES - zclGeneral_CountAllScenes() ) #endif // ZCL_SCENES /********************************************************************* * CONSTANTS */ /********************************************************************* * TYPEDEFS */ typedef struct zclGenCBRec { struct zclGenCBRec *next; uint8 endpoint; // Used to link it into the endpoint descriptor zclGeneral_AppCallbacks_t *CBs; // Pointer to Callback function } zclGenCBRec_t; typedef struct zclGenSceneItem { struct zclGenSceneItem *next; uint8 endpoint; // Used to link it into the endpoint descriptor zclGeneral_Scene_t scene; // Scene info } zclGenSceneItem_t; typedef struct zclGenAlarmItem { struct zclGenAlarmItem *next; uint8 endpoint; // Used to link it into the endpoint descriptor zclGeneral_Alarm_t alarm; // Alarm info } zclGenAlarmItem_t; // Scene NV types typedef struct { uint16 numRecs; } nvGenScenesHdr_t; typedef struct zclGenSceneNVItem { uint8 endpoint; zclGeneral_Scene_t scene; } zclGenSceneNVItem_t; /********************************************************************* * GLOBAL VARIABLES */ /********************************************************************* * GLOBAL FUNCTIONS */ /********************************************************************* * LOCAL VARIABLES */ static zclGenCBRec_t *zclGenCBs = (zclGenCBRec_t *)NULL; static uint8 zclGenPluginRegisted = FALSE; #ifdef ZCL_SCENES static zclGenSceneItem_t *zclGenSceneTable = (zclGenSceneItem_t *)NULL; #endif // ZCL_SCENES #ifdef ZCL_ALARMS static zclGenAlarmItem_t *zclGenAlarmTable = (zclGenAlarmItem_t *)NULL; #endif // ZCL_ALARMS /********************************************************************* * LOCAL FUNCTIONS */ static ZStatus_t zclGeneral_HdlIncoming( zclIncoming_t *pInMsg ); static ZStatus_t zclGeneral_HdlInSpecificCommands( zclIncoming_t *pInMsg ); static zclGeneral_AppCallbacks_t *zclGeneral_FindCallbacks( uint8 endpoint ); // Device Configuration and Installation clusters #ifdef ZCL_BASIC static ZStatus_t zclGeneral_ProcessInBasic( zclIncoming_t *pInMsg, zclGeneral_AppCallbacks_t *pCBs ); #endif // ZCL_BASIC #ifdef ZCL_IDENTIFY static ZStatus_t zclGeneral_ProcessInIdentity( zclIncoming_t *pInMsg, zclGeneral_AppCallbacks_t *pCBs ); #endif // ZCL_IDENTIFY // Groups and Scenes clusters #ifdef ZCL_GROUPS static ZStatus_t zclGeneral_ProcessInGroupsServer( zclIncoming_t *pInMsg ); static ZStatus_t zclGeneral_ProcessInGroupsClient( zclIncoming_t *pInMsg, zclGeneral_AppCallbacks_t *pCBs ); static ZStatus_t zclGeneral_AddGroup( uint8 endPoint, aps_Group_t *group, uint8 *pData ); #endif // ZCL_GROUPS #ifdef ZCL_SCENES static ZStatus_t zclGeneral_ProcessInScenesServer( zclIncoming_t *pInMsg, zclGeneral_AppCallbacks_t *pCBs ); static ZStatus_t zclGeneral_ProcessInScenesClient( zclIncoming_t *pInMsg, zclGeneral_AppCallbacks_t *pCBs ); #endif // ZCL_SCENES // On/Off and Level Control Configuration clusters #ifdef ZCL_ON_OFF static ZStatus_t zclGeneral_ProcessInOnOff( zclIncoming_t *pInMsg, zclGeneral_AppCallbacks_t *pCBs ); #endif // ZCL_ONOFF #ifdef ZCL_LEVEL_CTRL static ZStatus_t zclGeneral_ProcessInLevelControl( zclIncoming_t *pInMsg, zclGeneral_AppCallbacks_t *pCBs ); #endif // ZCL_LEVEL_CTRL // Alarms cluster #ifdef ZCL_ALARMS static ZStatus_t zclGeneral_ProcessInAlarmsServer( zclIncoming_t *pInMsg, zclGeneral_AppCallbacks_t *pCBs ); static ZStatus_t zclGeneral_ProcessInAlarmsClient( zclIncoming_t *pInMsg, zclGeneral_AppCallbacks_t *pCBs ); #endif // ZCL_ALARMS // Location cluster #ifdef ZCL_LOCATION static ZStatus_t zclGeneral_ProcessInLocationServer( zclIncoming_t *pInMsg, zclGeneral_AppCallbacks_t *pCBs ); static ZStatus_t zclGeneral_ProcessInLocationClient( zclIncoming_t *pInMsg, zclGeneral_AppCallbacks_t *pCBs ); #endif // ZCL_LOCATION #ifdef ZCL_SCENES static uint8 zclGeneral_ScenesInitNV( void ); static void zclGeneral_ScenesSetDefaultNV( void ); static void zclGeneral_ScenesWriteNV( void ); static uint16 zclGeneral_ScenesRestoreFromNV( void ); #endif // ZCL_SCENES /********************************************************************* * @fn zclGeneral_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 zclGeneral_RegisterCmdCallbacks( uint8 endpoint, zclGeneral_AppCallbacks_t *callbacks ) { zclGenCBRec_t *pNewItem; zclGenCBRec_t *pLoop; // Register as a ZCL Plugin if ( zclGenPluginRegisted == FALSE ) { zcl_registerPlugin( ZCL_CLUSTER_ID_GEN_BASIC, ZCL_CLUSTER_ID_GEN_MULTISTATE_VALUE_BASIC, zclGeneral_HdlIncoming ); #ifdef ZCL_SCENES // Initialize NV items zclGeneral_ScenesInitNV(); // Restore the Scene table zclGeneral_ScenesRestoreFromNV(); #endif // ZCL_SCENES zclGenPluginRegisted = TRUE; } // Fill in the new profile list pNewItem = osal_mem_alloc( sizeof( zclGenCBRec_t ) ); if ( pNewItem == NULL ) return (ZMemError); pNewItem->next = (zclGenCBRec_t *)NULL; pNewItem->endpoint = endpoint; pNewItem->CBs = callbacks; // Find spot in list if ( zclGenCBs == NULL ) { zclGenCBs = pNewItem; } else { // Look for end of list pLoop = zclGenCBs; while ( pLoop->next != NULL ) pLoop = pLoop->next; // Put new item at end of list pLoop->next = pNewItem; } return ( ZSuccess ); } #ifdef ZCL_IDENTIFY /********************************************************************* * @fn zclGeneral_SendIdentify * * @brief Call to send out an Identify Command * * @param srcEP - Sending application's endpoint * @param dstAddr - where you want the message to go * @param identifyTime - how long the device will continue to identify itself (in seconds) * @param seqNum - identification number for the transaction * * @return ZStatus_t */ ZStatus_t zclGeneral_SendIdentify( uint8 srcEP, afAddrType_t *dstAddr, uint16 identifyTime, uint8 disableDefaultRsp, uint8 seqNum ) { uint8 buf[2]; buf[0] = LO_UINT16( identifyTime ); buf[1] = HI_UINT16( identifyTime ); return zcl_SendCommand( srcEP, dstAddr, ZCL_CLUSTER_ID_GEN_IDENTIFY, COMMAND_IDENTIFY, TRUE, ZCL_FRAME_CLIENT_SERVER_DIR, disableDefaultRsp, 0, seqNum, 2, buf ); } /********************************************************************* * @fn zclGeneral_SendIdentifyQueryResponse * * @brief Call to send out an Identify Query Response Command * * @param srcEP - Sending application's endpoint * @param dstAddr - where you want the message to go * @param timeout - how long the device will continue to identify itself (in seconds) * @param seqNum - identification number for the transaction * * @return ZStatus_t */ ZStatus_t zclGeneral_SendIdentifyQueryResponse( uint8 srcEP, afAddrType_t *dstAddr, uint16 timeout, uint8 disableDefaultRsp, uint8 seqNum ) { uint8 buf[2]; buf[0] = LO_UINT16( timeout ); buf[1] = HI_UINT16( timeout ); return zcl_SendCommand( srcEP, dstAddr, ZCL_CLUSTER_ID_GEN_IDENTIFY, COMMAND_IDENTIFY_QUERY_RSP, TRUE, ZCL_FRAME_SERVER_CLIENT_DIR, disableDefaultRsp, 0, seqNum, 2, buf ); } #endif // ZCL_IDENTIFY #ifdef ZCL_GROUPS /********************************************************************* * @fn zclGeneral_SendGroupRequest * * @brief Send a Group Request to a device. You can also use the * appropriate macro. * * @param srcEP - Sending Apps endpoint * @param dstAddr - where to send the request * @param cmd - one of the following: * COMMAND_GROUP_VIEW * COMMAND_GROUP_REMOVE * @param groupID - * * @return ZStatus_t */ ZStatus_t zclGeneral_SendGroupRequest( uint8 srcEP, afAddrType_t *dstAddr, uint8 cmd, uint16 groupID, uint8 disableDefaultRsp, uint8 seqNum ) { uint8 buf[2]; buf[0] = LO_UINT16( groupID ); buf[1] = HI_UINT16( groupID ); return ( zcl_SendCommand( srcEP, dstAddr, ZCL_CLUSTER_ID_GEN_GROUPS, cmd, TRUE, ZCL_FRAME_CLIENT_SERVER_DIR, disableDefaultRsp, 0, seqNum, 2, buf ) ); } /********************************************************************* * @fn zclGeneral_SendAddGroupRequest * * @brief Send the Add Group Request to a device * * @param srcEP - Sending Apps endpoint * @param dstAddr - where to send the request * @param cmd - one of the following: * COMMAND_GROUP_ADD * COMMAND_GROUP_ADD_IF_IDENTIFYING * @param groupID - pointer to the group structure * @param groupName - pointer to Group Name. This is a Zigbee * string data type, so the first byte is the length of the * name (in bytes), then the name. * * @return ZStatus_t */ ZStatus_t zclGeneral_SendAddGroupRequest( uint8 srcEP, afAddrType_t *dstAddr, uint8 cmd, uint16 groupID, uint8 *groupName, uint8 disableDefaultRsp, uint8 seqNum ) { uint8 *buf; uint8 *pBuf; uint8 len; ZStatus_t status; len = 2; // Group ID len += groupName[0] + 1; // String + 1 for length buf = osal_mem_alloc( len ); if ( buf ) { pBuf = buf; *pBuf++ = LO_UINT16( groupID ); *pBuf++ = HI_UINT16( groupID ); *pBuf++ = groupName[0]; // string length osal_memcpy( pBuf, &(groupName[1]), groupName[0] ); status = zcl_SendCommand( srcEP, dstAddr, ZCL_CLUSTER_ID_GEN_GROUPS, cmd, TRUE, ZCL_FRAME_CLIENT_SERVER_DIR, disableDefaultRsp, 0, seqNum, len, buf ); osal_mem_free( buf ); } else status = ZMemError; return ( status ); } /********************************************************************* * @fn zclGeneral_SendGroupGetMembershipRequest * * @brief Send a Get Group Membership (Resposne) Command to a device * * @param srcEP - Sending Apps endpoint * @param dstAddr - where to send the request * @param cmd - one of the following: * COMMAND_GROUP_GET_MEMBERSHIP * COMMAND_GROUP_GET_MEMBERSHIP_RSP * @param groupID - pointer to the group structure * @param groupName - pointer to Group Name. This is a Zigbee * string data type, so the first byte is the length of the * name (in bytes), then the name. * * @return ZStatus_t */ ZStatus_t zclGeneral_SendGroupGetMembershipRequest( uint8 srcEP, afAddrType_t *dstAddr, uint8 cmd, uint8 rspCmd, uint8 direction, uint8 capacity, uint8 grpCnt, uint16 *grpList, uint8 disableDefaultRsp, uint8 seqNum ) { uint8 *buf; uint8 *pBuf; uint8 len = 0; uint8 i; ZStatus_t status; if ( rspCmd ) len++; // Capacity len++; // Group Count len += sizeof ( uint16 ) * grpCnt; // Group List buf = osal_mem_alloc( len ); if ( buf ) { pBuf = buf; if ( rspCmd ) *pBuf++ = capacity; *pBuf++ = grpCnt; for ( i = 0; i < grpCnt; i++ ) { *pBuf++ = LO_UINT16( grpList[i] ); *pBuf++ = HI_UINT16( grpList[i] ); } status = zcl_SendCommand( srcEP, dstAddr, ZCL_CLUSTER_ID_GEN_GROUPS, cmd, TRUE, direction, disableDefaultRsp, 0, seqNum, len, buf ); osal_mem_free( buf ); } else status = ZMemError; return ( status ); } /********************************************************************* * @fn zclGeneral_SendGroupResponse * * @brief Send Group Response (not Group View Response) * * @param srcEP - Sending application's endpoint * @param dstAddr - where you want the message to go * @param cmd - either COMMAND_GROUP_ADD_RSP or COMMAND_GROUP_REMOVE_RSP * @param status - group command status * @param groupID - what group * * @return ZStatus_t */ ZStatus_t zclGeneral_SendGroupResponse( uint8 srcEP, afAddrType_t *dstAddr, uint8 cmd, uint8 status, uint16 groupID, uint8 disableDefaultRsp, uint8 seqNum ) { uint8 buf[3]; buf[0] = status; buf[1] = LO_UINT16( groupID ); buf[2] = HI_UINT16( groupID ); return zcl_SendCommand( srcEP, dstAddr, ZCL_CLUSTER_ID_GEN_GROUPS, cmd, TRUE, ZCL_FRAME_SERVER_CLIENT_DIR, disableDefaultRsp, 0, seqNum, 3, buf ); } /********************************************************************* * @fn zclGeneral_SendGroupViewResponse * * @brief Call to send Group Response Command * * @param srcEP - Sending application's endpoint * @param dstAddr - where you want the message to go * @param cmd - either COMMAND_GROUP_ADD_RSP or COMMAND_GROUP_REMOVE_RSP * @param status - group command status * @param grp - group info * * @return ZStatus_t */ ZStatus_t zclGeneral_SendGroupViewResponse( uint8 srcEP, afAddrType_t *dstAddr, uint8 status, aps_Group_t *grp, uint8 disableDefaultRsp, uint8 seqNum ) { uint8 *buf; uint8 len; ZStatus_t stat; len = 1 + 2; // Status + Group ID if ( status == ZCL_STATUS_SUCCESS ) len += grp->name[0] + 1; // String + 1 for length buf = osal_mem_alloc( len ); if ( buf ) { buf[0] = status; buf[1] = LO_UINT16( grp->ID ); buf[2] = HI_UINT16( grp->ID ); if ( status == ZCL_STATUS_SUCCESS ) { buf[3] = grp->name[0]; // string length osal_memcpy( &buf[4], (&grp->name[1]), grp->name[0] ); } stat = zcl_SendCommand( srcEP, dstAddr, ZCL_CLUSTER_ID_GEN_GROUPS, COMMAND_GROUP_VIEW_RSP, TRUE, ZCL_FRAME_SERVER_CLIENT_DIR, disableDefaultRsp, 0, seqNum, len, buf ); osal_mem_free( buf ); } else stat = ZMemError; return ( stat ); } #endif // ZCL_GROUPS #ifdef ZCL_SCENES /********************************************************************* * @fn zclGeneral_SendAddScene * * @brief Send the Add Scene Request to a device * * @param srcEP - Sending Apps endpoint * @param dstAddr - where to send the request * @param scene - pointer to the scene structure * * @return ZStatus_t */ ZStatus_t zclGeneral_SendAddScene( uint8 srcEP, afAddrType_t *dstAddr, zclGeneral_Scene_t *scene, uint8 disableDefaultRsp, uint8 seqNum ) { uint8 *buf; uint8 *pBuf; uint8 len; ZStatus_t status; len = 2 + 1 + 2; // Group ID + Scene ID + transition time len += scene->name[0] + 1; // String + 1 for length // Add something for the extension field length len += scene->extLen; buf = osal_mem_alloc( len ); if ( buf ) { pBuf = buf; *pBuf++ = LO_UINT16( scene->groupID ); *pBuf++ = HI_UINT16( scene->groupID ); *pBuf++ = scene->ID; *pBuf++ = LO_UINT16( scene->transTime ); *pBuf++ = HI_UINT16( scene->transTime ); *pBuf++ = scene->name[0]; // string length osal_memcpy( pBuf, &(scene->name[1]), scene->name[0] ); pBuf += scene->name[0]; // move pass name // Add the extension fields if ( scene->extLen > 0 ) osal_memcpy( pBuf, scene->extField, scene->extLen ); status = zcl_SendCommand( srcEP, dstAddr, ZCL_CLUSTER_ID_GEN_SCENES, COMMAND_SCENE_ADD, TRUE, ZCL_FRAME_CLIENT_SERVER_DIR, disableDefaultRsp, 0, seqNum, len, buf ); osal_mem_free( buf ); } else status = ZMemError; return ( status ); } /********************************************************************* * @fn zclGeneral_SendSceneRequest * * @brief Send a Scene Request to a device. You can also use the * appropriate macro. * * @param srcEP - Sending Apps endpoint * @param dstAddr - where to send the request * @param cmd - one of the following: * COMMAND_SCENE_VIEW * COMMAND_SCENE_REMOVE * COMMAND_SCENE_REMOVE_ALL * COMMAND_SCENE_STORE * COMMAND_SCENE_RECALL * COMMAND_SCENE_GET_MEMBERSHIP * @param groupID - group ID * @param sceneID - scene ID (not applicable to COMMAND_SCENE_REMOVE_ALL and * COMMAND_SCENE_GET_MEMBERSHIP) * @return ZStatus_t */ ZStatus_t zclGeneral_SendSceneRequest( uint8 srcEP, afAddrType_t *dstAddr, uint8 cmd, uint16 groupID, uint8 sceneID, uint8 disableDefaultRsp, uint8 seqNum ) { uint8 buf[3]; uint8 len = 2; buf[0] = LO_UINT16( groupID ); buf[1] = HI_UINT16( groupID ); if ( cmd != COMMAND_SCENE_REMOVE_ALL && cmd != COMMAND_SCENE_GET_MEMBERSHIP ) { buf[2] = sceneID; len++; } return ( zcl_SendCommand( srcEP, dstAddr, ZCL_CLUSTER_ID_GEN_SCENES, cmd, TRUE, ZCL_FRAME_CLIENT_SERVER_DIR, disableDefaultRsp, 0, seqNum, len, buf ) ); } /********************************************************************* * @fn zclGeneral_SendSceneResponse * * @brief Send Group Response (not Group View Response) * * @param srcEP - Sending application's endpoint * @param dstAddr - where you want the message to go * @param cmd - either COMMAND_SCENE_ADD_RSP, COMMAND_SCENE_REMOVE_RSP * COMMAND_SCENE_STORE_RSP, or COMMAND_SCENE_REMOVE_ALL_RSP * @param status - scene command status * @param groupID - what group * @param sceneID - what scene (not applicable to COMMAND_SCENE_REMOVE_ALL_RSP) * * @return ZStatus_t */ ZStatus_t zclGeneral_SendSceneResponse( uint8 srcEP, afAddrType_t *dstAddr, uint8 cmd, uint8 status, uint16 groupID, uint8 sceneID, uint8 disableDefaultRsp, uint8 seqNum ) { uint8 buf[4]; uint8 len = 1 + 2; // Status + Group ID buf[0] = status; buf[1] = LO_UINT16( groupID ); buf[2] = HI_UINT16( groupID ); if ( cmd != COMMAND_SCENE_REMOVE_ALL_RSP ) { buf[3] = sceneID; len++; } return zcl_SendCommand( srcEP, dstAddr, ZCL_CLUSTER_ID_GEN_SCENES, cmd, TRUE, ZCL_FRAME_SERVER_CLIENT_DIR, disableDefaultRsp, 0, seqNum, len, buf ); } /********************************************************************* * @fn zclGeneral_SendSceneViewResponse * * @brief Call to send Scene Response Command * * @param srcEP - Sending application's endpoint * @param dstAddr - where you want the message to go * @param status - scene command status * @param scene - scene info * * @return ZStatus_t */ ZStatus_t zclGeneral_SendSceneViewResponse( uint8 srcEP, afAddrType_t *dstAddr, uint8 status, zclGeneral_Scene_t *scene, uint8 disableDefaultRsp, uint8 seqNum ) { uint8 *buf; uint8 *pBuf; uint8 len = 1 + 2 + 1; // Status + Group ID + Scene ID ZStatus_t stat; if ( status == ZCL_STATUS_SUCCESS ) { len += 2; // Transition Time len += scene->name[0] + 1; // string + 1 for length // Add something for the extension field length len += scene->extLen; } buf = osal_mem_alloc( len ); if ( buf ) { pBuf = buf; *pBuf++ = status; *pBuf++ = LO_UINT16( scene->groupID ); *pBuf++ = HI_UINT16( scene->groupID ); *pBuf++ = scene->ID; if ( status == ZCL_STATUS_SUCCESS ) { *pBuf++ = LO_UINT16( scene->transTime ); *pBuf++ = HI_UINT16( scene->transTime ); *pBuf++ = scene->name[0]; // string length if ( scene->name[0] != 0 ) { osal_memcpy( pBuf, &(scene->name[1]), scene->name[0] ); pBuf += scene->name[0]; // move pass name } // Add the extension fields if ( scene->extLen > 0 ) osal_memcpy( pBuf, scene->extField, scene->extLen ); } stat = zcl_SendCommand( srcEP, dstAddr, ZCL_CLUSTER_ID_GEN_SCENES, COMMAND_SCENE_VIEW_RSP, TRUE, ZCL_FRAME_SERVER_CLIENT_DIR, disableDefaultRsp, 0, seqNum, len, buf ); osal_mem_free( buf ); } else stat = ZMemError; return ( stat ); } /********************************************************************* * @fn zclGeneral_SendSceneGetMembershipResponse * * @brief Call to send Scene Get Membership Response Command * * @param srcEP - Sending application's endpoint * @param dstAddr - where you want the message to go * @param status - scene command status * @param capacity - remaining capacity of the scene table * @param sceneCnt - number of scenes in the scene list * @param sceneList - list of scene IDs * @param groupID - group ID that scene belongs to * @param seqNum - sequence number * * @return ZStatus_t */ ZStatus_t zclGeneral_SendSceneGetMembershipResponse( uint8 srcEP, afAddrType_t *dstAddr, uint8 status, uint8 capacity, uint8 sceneCnt, uint8 *sceneList, uint16 groupID, uint8 disableDefaultRsp, uint8 seqNum ) { uint8 *buf; uint8 *pBuf; uint8 len = 1 + 1 + 2; // Status + Capacity + Group ID; uint8 i; ZStatus_t stat; if ( status == ZCL_STATUS_SUCCESS ) { len++; // Scene Count len += sceneCnt; // Scene List (Scene ID is a single octet) } buf = osal_mem_alloc( len ); if ( buf ) { pBuf = buf; *pBuf++ = status; *pBuf++ = capacity; *pBuf++ = LO_UINT16( groupID ); *pBuf++ = HI_UINT16( groupID ); if ( status == ZCL_STATUS_SUCCESS ) { *pBuf++ = sceneCnt; for ( i = 0; i < sceneCnt; i++ ) *pBuf++ = sceneList[i]; } stat = zcl_SendCommand( srcEP, dstAddr, ZCL_CLUSTER_ID_GEN_SCENES, COMMAND_SCENE_GET_MEMBERSHIP_RSP, TRUE, ZCL_FRAME_SERVER_CLIENT_DIR, disableDefaultRsp, 0, seqNum, len, buf ); osal_mem_free( buf ); } else stat = ZMemError; return ( stat ); } #endif // ZCL_SCENES #ifdef ZCL_LEVEL_CTRL /********************************************************************* * @fn zclGeneral_SendLevelControlMoveToLevelRequest * * @brief Call to send out a Level Control Request. You can also use * the appropriate macro. * * @param srcEP - Sending application's endpoint * @param dstAddr - where you want the message to go * @param cmd - one of the following: * COMMAND_LEVEL_MOVE_TO_LEVEL or * COMMAND_LEVEL_MOVE_TO_LEVEL_WITH_ON_OFF * @param level - what level to move to * @param transitionTime - how long to take to get to the level (in seconds) * * @return ZStatus_t */ ZStatus_t zclGeneral_SendLevelControlMoveToLevelRequest( uint8 srcEP, afAddrType_t *dstAddr, uint8 cmd, uint8 level, uint16 transTime, uint8 disableDefaultRsp, uint8 seqNum ) { uint8 buf[3]; buf[0] = level; buf[1] = LO_UINT16( transTime ); buf[2] = HI_UINT16( transTime ); return zcl_SendCommand( srcEP, dstAddr, ZCL_CLUSTER_ID_GEN_LEVEL_CONTROL, cmd, TRUE, ZCL_FRAME_CLIENT_SERVER_DIR, disableDefaultRsp, 0, seqNum, 3, buf ); } /********************************************************************* * @fn zclGeneral_SendLevelControlMoveRequest * * @brief Call to send out a Level Control Request. You can also use * the appropriate macro. * * @param srcEP - Sending application's endpoint * @param dstAddr - where you want the message to go * @param cmd - one of the following: * COMMAND_LEVEL_MOVE or * COMMAND_LEVEL_MOVE_WITH_ON_OFF * @param moveMode - LEVEL_MOVE_UP or * LEVEL_MOVE_DOWN * @param rate - number of steps to take per second * * @return ZStatus_t */ ZStatus_t zclGeneral_SendLevelControlMoveRequest( uint8 srcEP, afAddrType_t *dstAddr, uint8 cmd, uint8 moveMode, uint8 rate, uint8 disableDefaultRsp, uint8 seqNum ) { uint8 buf[2]; buf[0] = moveMode; buf[1] = rate; return zcl_SendCommand( srcEP, dstAddr, ZCL_CLUSTER_ID_GEN_LEVEL_CONTROL, cmd, TRUE, ZCL_FRAME_CLIENT_SERVER_DIR, disableDefaultRsp, 0, seqNum, 2, buf ); } /********************************************************************* * @fn zclGeneral_SendLevelControlStepRequest * * @brief Call to send out a Level Control Request. You can also use * the appropriate macro. * * @param srcEP - Sending application's endpoint * @param dstAddr - where you want the message to go * @param cmd - one of the following: * COMMAND_LEVEL_STEP * COMMAND_LEVEL_STEP_WITH_ON_OFF * @param stepMode - LEVEL_STEP_UP or * LEVEL_STEP_DOWN * @param amount - number of levels to step * @param transitionTime - time, in 1/10ths of a second, to take to perform the step * * @return ZStatus_t */ ZStatus_t zclGeneral_SendLevelControlStepRequest( uint8 srcEP, afAddrType_t *dstAddr, uint8 cmd, uint8 stepMode, uint8 stepSize, uint16 transTime, uint8 disableDefaultRsp, uint8 seqNum ) { uint8 buf[4]; buf[0] = stepMode; buf[1] = stepSize; buf[2] = LO_UINT16( transTime ); buf[3] = HI_UINT16( transTime ); return zcl_SendCommand( srcEP, dstAddr, ZCL_CLUSTER_ID_GEN_LEVEL_CONTROL, cmd, TRUE, ZCL_FRAME_CLIENT_SERVER_DIR, disableDefaultRsp, 0, seqNum, 4, buf ); } #endif // ZCL_LEVEL_CTRL #ifdef ZCL_ALARMS /********************************************************************* * @fn zclGeneral_SendAlarmRequest * * @brief Call to send out an Alarm Request Command * * @param srcEP - Sending application's endpoint * @param dstAddr - where you want the message to go * @param cmd - either COMMAND_ALARMS_RESET or COMMAND_ALARMS_ALARM * @param alarmCode - code for the cause of the alarm * @param clusterID - cluster whose attribute generate the alarm * * @return ZStatus_t */ ZStatus_t zclGeneral_SendAlarmRequest( uint8 srcEP, afAddrType_t *dstAddr, uint8 cmd, uint8 alarmCode, uint16 clusterID, uint8 disableDefaultRsp, uint8 seqNum ) { uint8 buf[3]; buf[0] = alarmCode; buf[1] = LO_UINT16( clusterID ); buf[2] = HI_UINT16( clusterID ); return zcl_SendCommand( srcEP, dstAddr, ZCL_CLUSTER_ID_GEN_ALARMS, cmd, TRUE, ZCL_FRAME_CLIENT_SERVER_DIR, disableDefaultRsp, 0, seqNum, 3, buf ); } /********************************************************************* * @fn zclGeneral_SendAlarmGetRespnose * * @brief Call to send out an Alarm Get Response Command * * @param srcEP - Sending application's endpoint * @param dstAddr - where you want the message to go * @param status - SUCCESS or NOT_FOUND * @param alarmCode - code for the cause of the alarm * @param clusterID - cluster whose attribute generate the alarm * @param timeStamp - time at which the alarm occured * * @return ZStatus_t */ ZStatus_t zclGeneral_SendAlarmGetRespnose( uint8 srcEP, afAddrType_t *dstAddr, uint8 status, uint8 alarmCode, uint16 clusterID, uint32 timeStamp, uint8 disableDefaultRsp, uint8 seqNum ) { uint8 buf[8]; uint8 len = 1; // Status buf[0] = status; if ( status == ZCL_STATUS_SUCCESS ) { len += 1 + 2 + 4; // Alarm code + Cluster ID + Time stamp buf[1] = alarmCode; buf[2] = LO_UINT16( clusterID ); buf[3] = HI_UINT16( clusterID ); osal_buffer_uint32( &buf[4], timeStamp ); } return zcl_SendCommand( srcEP, dstAddr, ZCL_CLUSTER_ID_GEN_ALARMS, COMMAND_ALARMS_GET_RSP, TRUE, ZCL_FRAME_SERVER_CLIENT_DIR, disableDefaultRsp, 0, seqNum, len, buf ); } #ifdef SE_UK_EXT /********************************************************************* * @fn zclGeneral_SendAlarmGetEventLog * * @brief Call to send out an Alarm Get Event Log Command * * @param srcEP - Sending application's endpoint * @param dstAddr - where you want the message to go * @param pEventLog - pointer to Get Event Log Command * @param disableDefaultRsp - disable default response * @param seqNum - ZCL sequence number * * @return ZStatus_t */ ZStatus_t zclGeneral_SendAlarmGetEventLog( uint8 srcEP, afAddrType_t *dstAddr, zclGetEventLog_t *pEventLog, uint8 disableDefaultRsp, uint8 seqNum ) { uint8 buf[10]; buf[0] = pEventLog->logID; osal_buffer_uint32( &buf[1], pEventLog->startTime ); osal_buffer_uint32( &buf[5], pEventLog->endTime ); buf[9] = pEventLog->numEvents; return zcl_SendCommand( srcEP, dstAddr, ZCL_CLUSTER_ID_GEN_ALARMS, COMMAND_ALARMS_GET_EVENT_LOG, TRUE, ZCL_FRAME_SERVER_CLIENT_DIR, disableDefaultRsp, 0, seqNum, 10, buf ); } /********************************************************************* * @fn zclGeneral_SendAlarmPublishEventLog * * @brief Call to send out an Alarm Publish Event Log Command * * @param srcEP - Sending application's endpoint * @param dstAddr - where you want the message to go * @param pEventLog - pointer to Publish Event Log Command * @param disableDefaultRsp - disable default response * @param seqNum - ZCL sequence number * * @return ZStatus_t */ ZStatus_t zclGeneral_SendAlarmPublishEventLog( uint8 srcEP, afAddrType_t *dstAddr, zclPublishEventLog_t *pEventLog, uint8 disableDefaultRsp, uint8 seqNum ) { uint8 *buf; uint8 *pBuf; uint8 bufLen; // Log ID + Command Index + Total Commands + (numSubLogs * ( Event ID + Event Time)) bufLen = 1 + 1 + 1 + (pEventLog->numSubLogs * (1 + 4)); buf = osal_mem_alloc( bufLen ); if ( buf == NULL ) { return (ZMemError); } pBuf = buf; *pBuf++ = pEventLog->logID; *pBuf++ = pEventLog->cmdIndex; *pBuf++ = pEventLog->totalCmds; for ( uint8 i = 0; i < pEventLog->numSubLogs; i++ ) { zclEventLogPayload_t *pLogs = &(pEventLog->pLogs[i]); *pBuf++ = pLogs->eventId; pBuf = osal_buffer_uint32( pBuf, pLogs->eventTime ); } return zcl_SendCommand( srcEP, dstAddr, ZCL_CLUSTER_ID_GEN_ALARMS, COMMAND_ALARMS_PUBLISH_EVENT_LOG, TRUE, ZCL_FRAME_CLIENT_SERVER_DIR, disableDefaultRsp, 0, seqNum, bufLen, buf ); } #endif // SE_UK_EXT #endif // ZCL_ALARMS #ifdef ZCL_LOCATION /********************************************************************* * @fn zclGeneral_SendLocationSetAbsolute * * @brief Call to send out a Set Absolute Location Command * * @param srcEP - Sending application's endpoint * @param dstAddr - where you want the message to go * @param absLoc - absolute location info * * @return ZStatus_t */ ZStatus_t zclGeneral_SendLocationSetAbsolute( uint8 srcEP, afAddrType_t *dstAddr, zclLocationAbsolute_t *absLoc, uint8 disableDefaultRsp, uint8 seqNum ) { uint8 buf[10]; // 5 fields (2 octects each) buf[0] = LO_UINT16( absLoc->coordinate1 ); buf[1] = HI_UINT16( absLoc->coordinate1 ); buf[2] = LO_UINT16( absLoc->coordinate2 ); buf[3] = HI_UINT16( absLoc->coordinate2 ); buf[4] = LO_UINT16( absLoc->coordinate3 ); buf[5] = HI_UINT16( absLoc->coordinate3 ); buf[6] = LO_UINT16( absLoc->power ); buf[7] = HI_UINT16( absLoc->power ); buf[8] = LO_UINT16( absLoc->pathLossExponent ); buf[9] = HI_UINT16( absLoc->pathLossExponent ); return zcl_SendCommand( srcEP, dstAddr, ZCL_CLUSTER_ID_GEN_LOCATION, COMMAND_LOCATION_SET_ABSOLUTE, TRUE, ZCL_FRAME_CLIENT_SERVER_DIR, disableDefaultRsp, 0, seqNum, 10, buf ); } /********************************************************************* * @fn zclGeneral_SendLocationSetDevCfg * * @brief Call to send out a Set Device Configuration Command * * @param srcEP - Sending application's endpoint * @param dstAddr - where you want the message to go * @param devCfg - device configuration info * * @return ZStatus_t */ ZStatus_t zclGeneral_SendLocationSetDevCfg( uint8 srcEP, afAddrType_t *dstAddr, zclLocationDevCfg_t *devCfg, uint8 disableDefaultRsp, uint8 seqNum ) { uint8 buf[9]; // 4 fields (2 octects each) + 1 field with 1 octect buf[0] = LO_UINT16( devCfg->power ); buf[1] = HI_UINT16( devCfg->power ); buf[2] = LO_UINT16( devCfg->pathLossExponent ); buf[3] = HI_UINT16( devCfg->pathLossExponent ); buf[4] = LO_UINT16( devCfg->calcPeriod ); buf[5] = HI_UINT16( devCfg->calcPeriod ); buf[6] = devCfg->numMeasurements; buf[7] = LO_UINT16( devCfg->reportPeriod ); buf[8] = HI_UINT16( devCfg->reportPeriod ); return zcl_SendCommand( srcEP, dstAddr, ZCL_CLUSTER_ID_GEN_LOCATION, COMMAND_LOCATION_SET_DEV_CFG, TRUE, ZCL_FRAME_CLIENT_SERVER_DIR, disableDefaultRsp, 0, seqNum, 9, buf ); } /********************************************************************* * @fn zclGeneral_SendLocationGetDevCfg * * @brief Call to send out a Get Device Configuration Command * * @param srcEP - Sending application's endpoint * @param dstAddr - where you want the message to go * @param targetAddr - device for which location parameters are being requested * * @return ZStatus_t */ ZStatus_t zclGeneral_SendLocationGetDevCfg( uint8 srcEP, afAddrType_t *dstAddr, uint8 *targetAddr, uint8 disableDefaultRsp, uint8 seqNum ) { uint8 buf[8]; osal_cpyExtAddr( buf, targetAddr ); return zcl_SendCommand( srcEP, dstAddr, ZCL_CLUSTER_ID_GEN_LOCATION, COMMAND_LOCATION_GET_DEV_CFG, TRUE, ZCL_FRAME_CLIENT_SERVER_DIR, disableDefaultRsp, 0, seqNum, 8, buf ); } /********************************************************************* * @fn zclGeneral_SendLocationGetData * * @brief Call to send out a Get Location Data Command * * @param srcEP - Sending application's endpoint * @param dstAddr - where you want the message to go * @param locaData - location information and channel parameters that are requested. * * @return ZStatus_t */ ZStatus_t zclGeneral_SendLocationGetData( uint8 srcEP, afAddrType_t *dstAddr, zclLocationGetData_t *locData, uint8 disableDefaultRsp, uint8 seqNum ) { uint8 buf[10]; // bitmap (1) + number responses (1) + IEEE Address (8) uint8 *pBuf = buf; uint8 len = 2; // bitmap + number responses *pBuf = locData->absoluteOnly; *pBuf |= locData->recalculate << 1; *pBuf |= locData->brdcastIndicator << 2; *pBuf |= locData->brdcastResponse << 3; *pBuf |= locData->compactResponse << 4; pBuf++; // move past the bitmap field *pBuf++ = locData->numResponses; if ( locData->brdcastIndicator == 0 ) { osal_cpyExtAddr( pBuf, locData->targetAddr ); len += 8; // ieee addr } return zcl_SendCommand( srcEP, dstAddr, ZCL_CLUSTER_ID_GEN_LOCATION, COMMAND_LOCATION_GET_DATA, TRUE, ZCL_FRAME_CLIENT_SERVER_DIR, disableDefaultRsp, 0, seqNum, len, buf ); } /********************************************************************* * @fn zclGeneral_SendLocationDevCfgResponse * * @brief Call to send out a Device Configuration Response Command * * @param srcEP - Sending application's endpoint * @param dstAddr - where you want the message to go * @param devCfg - device's location parameters that are requested * * @return ZStatus_t */ ZStatus_t zclGeneral_SendLocationDevCfgResponse( uint8 srcEP, afAddrType_t *dstAddr, zclLocationDevCfgRsp_t *devCfg, uint8 disableDefaultRsp, uint8 seqNum ) { uint8 buf[10]; // 4 fields (2 octects each) + 2 fields (1 octect each) uint8 len = 1; // Status buf[0] = devCfg->status; if ( devCfg->status == ZCL_STATUS_SUCCESS ) { buf[1] = LO_UINT16( devCfg->data.power ); buf[2] = HI_UINT16( devCfg->data.power ); buf[3] = LO_UINT16( devCfg->data.pathLossExponent ); buf[4] = HI_UINT16( devCfg->data.pathLossExponent ); buf[5] = LO_UINT16( devCfg->data.calcPeriod ); buf[6] = HI_UINT16( devCfg->data.calcPeriod ); buf[7] = devCfg->data.numMeasurements; buf[8] = LO_UINT16( devCfg->data.reportPeriod ); buf[9] = HI_UINT16( devCfg->data.reportPeriod ); len += 9; } return zcl_SendCommand( srcEP, dstAddr, ZCL_CLUSTER_ID_GEN_LOCATION, COMMAND_LOCATION_DEV_CFG_RSP, TRUE, ZCL_FRAME_SERVER_CLIENT_DIR, disableDefaultRsp, 0, seqNum, len, buf ); } /********************************************************************* * @fn zclGeneral_SendLocationData * * @brief Call to send out location data * * @param srcEP - Sending application's endpoint * @param dstAddr - where you want the message to go * @param status - indicates whether response to request was successful or not * @param locData - location information and channel parameters being sent * * @return ZStatus_t */ ZStatus_t zclGeneral_SendLocationData( uint8 srcEP, afAddrType_t *dstAddr, uint8 cmd, uint8 status, zclLocationData_t *locData, uint8 disableDefaultRsp, uint8 seqNum ) { uint8 buf[16]; uint8 *pBuf = buf; uint8 len = 0; if ( cmd == COMMAND_LOCATION_DATA_RSP ) { // Only response command includes a status field *pBuf++ = status; len++; } if ( cmd != COMMAND_LOCATION_DATA_RSP || status == ZCL_STATUS_SUCCESS ) { // Notification or Response with successful status *pBuf++ = locData->type; *pBuf++ = LO_UINT16( locData->absLoc.coordinate1 ); *pBuf++ = HI_UINT16( locData->absLoc.coordinate1 ); *pBuf++ = LO_UINT16( locData->absLoc.coordinate2 ); *pBuf++ = HI_UINT16( locData->absLoc.coordinate2 ); len += 5; if ( locationType2D(locData->type) == 0 ) { // 2D location doesn't have coordinate 3 *pBuf++ = LO_UINT16( locData->absLoc.coordinate3 ); *pBuf++ = HI_UINT16( locData->absLoc.coordinate3 ); len += 2; } if ( cmd != COMMAND_LOCATION_COMPACT_DATA_NOTIF ) { // Compact notification doesn't include these fields *pBuf++ = LO_UINT16( locData->absLoc.power ); *pBuf++ = HI_UINT16( locData->absLoc.power ); *pBuf++ = LO_UINT16( locData->absLoc.pathLossExponent ); *pBuf++ = HI_UINT16( locData->absLoc.pathLossExponent ); len += 4; } if ( locationTypeAbsolute(locData->type) == 0 ) { // Absolute location doesn't include these fields if ( cmd != COMMAND_LOCATION_COMPACT_DATA_NOTIF ) { // Compact notification doesn't include this field *pBuf++ = locData->calcLoc.locationMethod; len++; } *pBuf++ = locData->calcLoc.qualityMeasure; *pBuf++ = LO_UINT16( locData->calcLoc.locationAge ); *pBuf++ = HI_UINT16( locData->calcLoc.locationAge ); len += 3; } } return zcl_SendCommand( srcEP, dstAddr, ZCL_CLUSTER_ID_GEN_LOCATION, cmd, TRUE, ZCL_FRAME_SERVER_CLIENT_DIR, disableDefaultRsp, 0, seqNum, len, buf ); } #endif // ZCL_LOCATION /********************************************************************* * @fn zclGeneral_FindCallbacks * * @brief Find the callbacks for an endpoint * * @param endpoint - endpoint to find the application callbacks for * * @return pointer to the callbacks */ static zclGeneral_AppCallbacks_t *zclGeneral_FindCallbacks( uint8 endpoint ) { zclGenCBRec_t *pCBs; pCBs = zclGenCBs; while ( pCBs ) { if ( pCBs->endpoint == endpoint ) return ( pCBs->CBs ); pCBs = pCBs->next; } return ( (zclGeneral_AppCallbacks_t *)NULL ); } /********************************************************************* * @fn zclGeneral_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 * * @return ZStatus_t */ static ZStatus_t zclGeneral_HdlIncoming( zclIncoming_t *pInMsg ) { ZStatus_t stat = ZSuccess; #if defined ( INTER_PAN ) if ( StubAPS_InterPan( pInMsg->msg->srcAddr.panId, pInMsg->msg->srcAddr.endPoint ) ) return ( stat ); // Cluster not supported thru Inter-PAN #endif if ( zcl_ClusterCmd( pInMsg->hdr.fc.type ) ) { // Is this a manufacturer specific command? if ( pInMsg->hdr.fc.manuSpecific == 0 ) { stat = zclGeneral_HdlInSpecificCommands( pInMsg ); } else { // We don't support any manufacturer specific command. stat = ZFailure; } } else { // Handle all the normal (Read, Write...) commands -- should never get here stat = ZFailure; } return ( stat ); } /********************************************************************* * @fn zclGeneral_HdlInSpecificCommands * * @brief Callback from ZCL to process incoming Commands specific * to this cluster library * @param pInMsg - pointer to the incoming message * * @return ZStatus_t */ static ZStatus_t zclGeneral_HdlInSpecificCommands( zclIncoming_t *pInMsg ) { ZStatus_t stat; zclGeneral_AppCallbacks_t *pCBs; // make sure endpoint exists pCBs = zclGeneral_FindCallbacks( pInMsg->msg->endPoint ); if ( pCBs == NULL ) return ( ZFailure ); switch ( pInMsg->msg->clusterId ) { #ifdef ZCL_BASIC case ZCL_CLUSTER_ID_GEN_BASIC: stat = zclGeneral_ProcessInBasic( pInMsg, pCBs ); break; #endif // ZCL_BASIC #ifdef ZCL_IDENTIFY case ZCL_CLUSTER_ID_GEN_IDENTIFY: stat = zclGeneral_ProcessInIdentity( pInMsg, pCBs ); break; #endif // ZCL_IDENTIFY #ifdef ZCL_GROUPS case ZCL_CLUSTER_ID_GEN_GROUPS: if ( zcl_ServerCmd( pInMsg->hdr.fc.direction ) ) stat = zclGeneral_ProcessInGroupsServer( pInMsg ); else stat = zclGeneral_ProcessInGroupsClient( pInMsg, pCBs ); break; #endif // ZCL_GROUPS #ifdef ZCL_SCENES case ZCL_CLUSTER_ID_GEN_SCENES: if ( zcl_ServerCmd( pInMsg->hdr.fc.direction ) ) stat = zclGeneral_ProcessInScenesServer( pInMsg, pCBs ); else stat = zclGeneral_ProcessInScenesClient( pInMsg, pCBs ); break; #endif // ZCL_SCENES #ifdef ZCL_ON_OFF case ZCL_CLUSTER_ID_GEN_ON_OFF: stat = zclGeneral_ProcessInOnOff( pInMsg, pCBs ); break; #endif // ZCL_ON_OFF #ifdef ZCL_LEVEL_CTRL case ZCL_CLUSTER_ID_GEN_LEVEL_CONTROL: stat = zclGeneral_ProcessInLevelControl( pInMsg, pCBs ); break; #endif // ZCL_LEVEL_CTRL #ifdef ZCL_ALARMS case ZCL_CLUSTER_ID_GEN_ALARMS: if ( zcl_ServerCmd( pInMsg->hdr.fc.direction ) ) stat = zclGeneral_ProcessInAlarmsServer( pInMsg, pCBs ); else stat = zclGeneral_ProcessInAlarmsClient( pInMsg, pCBs ); break; #endif // ZCL_ALARMS #ifdef ZCL_LOCATION case ZCL_CLUSTER_ID_GEN_LOCATION: if ( zcl_ServerCmd( pInMsg->hdr.fc.direction ) ) stat = zclGeneral_ProcessInLocationServer( pInMsg, pCBs ); else stat = zclGeneral_ProcessInLocationClient( pInMsg, pCBs ); break; #endif // ZCL_LOCATION case ZCL_CLUSTER_ID_GEN_POWER_CFG: case ZCL_CLUSTER_ID_GEN_DEVICE_TEMP_CONFIG: case ZCL_CLUSTER_ID_GEN_ON_OFF_SWITCH_CONFIG: case ZCL_CLUSTER_ID_GEN_TIME: default: stat = ZFailure; break; } return ( stat ); } #ifdef ZCL_BASIC /********************************************************************* * @fn zclGeneral_ProcessInBasic * * @brief Process in the received Basic Command. * * @param pInMsg - pointer to the incoming message * * @return ZStatus_t */ static ZStatus_t zclGeneral_ProcessInBasic( zclIncoming_t *pInMsg, zclGeneral_AppCallbacks_t *pCBs ) { if ( zcl_ServerCmd( pInMsg->hdr.fc.direction ) ) { if ( pInMsg->hdr.commandID > COMMAND_BASIC_RESET_FACT_DEFAULT ) return ( ZFailure ); // Error ignore the command if ( pCBs->pfnBasicReset ) pCBs->pfnBasicReset(); } // no Client command return ( ZSuccess ); } #endif // ZCL_BASIC #ifdef ZCL_IDENTIFY /********************************************************************* * @fn zclGeneral_ProcessInIdentity * * @brief Process in the received Identity Command. * * @param pInMsg - pointer to the incoming message * * @return ZStatus_t */ static ZStatus_t zclGeneral_ProcessInIdentity( zclIncoming_t *pInMsg, zclGeneral_AppCallbacks_t *pCBs ) { if ( zcl_ServerCmd( pInMsg->hdr.fc.direction ) ) { if ( pInMsg->hdr.commandID > COMMAND_IDENTIFY_QUERY ) return ( ZFailure ); // Error ignore the command if ( pInMsg->hdr.commandID == COMMAND_IDENTIFY ) { if ( pCBs->pfnIdentify ) { zclIdentify_t cmd; cmd.srcAddr = &(pInMsg->msg->srcAddr); cmd.identifyTime = BUILD_UINT16( pInMsg->pData[0], pInMsg->pData[1] ); pCBs->pfnIdentify( &cmd ); } } else { zclAttrRec_t attrRec; uint16 identifyTime = 0; // Retrieve Identify Time if ( zclFindAttrRec( pInMsg->msg->endPoint, pInMsg->msg->clusterId, ATTRID_IDENTIFY_TIME, &attrRec ) ) zclReadAttrData( (uint8 *)&identifyTime, &attrRec, NULL ); // Is device identifying itself? if ( identifyTime > 0 ) { zclGeneral_SendIdentifyQueryResponse( pInMsg->msg->endPoint, &pInMsg->msg->srcAddr, identifyTime, true, pInMsg->hdr.transSeqNum ); } return ( ZCL_STATUS_CMD_HAS_RSP ); } } else // Client Command { if ( pInMsg->hdr.commandID > COMMAND_IDENTIFY_QUERY_RSP ) return ( ZFailure ); // Error ignore the command if ( pCBs->pfnIdentifyQueryRsp ) { zclIdentifyQueryRsp_t rsp; rsp.srcAddr = &(pInMsg->msg->srcAddr); rsp.timeout = BUILD_UINT16( pInMsg->pData[0], pInMsg->pData[1] ); pCBs->pfnIdentifyQueryRsp( &rsp ); } } return ( ZSuccess ); } #endif // ZCL_IDENTIFY #ifdef ZCL_GROUPS /********************************************************************* * @fn zclGeneral_AddGroup * * @brief Add a Group. * * @param endPoint - application endpoint * @param group - group to be added * @param pData - pointer to the group info * * @return ZStatus_t */ static ZStatus_t zclGeneral_AddGroup( uint8 endPoint, aps_Group_t *group, uint8 *pData ) { zclAttrRec_t attrRec; uint8 nameLen; uint8 nameSupport = FALSE; pData += 2; // Move past group ID nameLen = *pData++; // Retrieve Name Support attribute if ( zclFindAttrRec( endPoint, ZCL_CLUSTER_ID_GEN_GROUPS, ATTRID_GROUP_NAME_SUPPORT, &attrRec ) ) zclReadAttrData( &nameSupport, &attrRec, NULL ); if ( nameSupport ) { if ( nameLen > (APS_GROUP_NAME_LEN-1) ) nameLen = (APS_GROUP_NAME_LEN-1); group->name[0] = nameLen; osal_memcpy( &(group->name[1]), pData, nameLen ); } return ( aps_AddGroup( endPoint, group ) ); } /********************************************************************* * @fn zclGeneral_ProcessInGroupsServer * * @brief Process in the received Groups Command. * * @param pInMsg - pointer to the incoming message * * @return ZStatus_t */ static ZStatus_t zclGeneral_ProcessInGroupsServer( zclIncoming_t *pInMsg ) { zclAttrRec_t attrRec; aps_Group_t group; aps_Group_t *pGroup; uint8 *pData; uint8 status; uint8 grpCnt; uint8 grpRspCnt = 0; uint16 *grpList; uint16 identifyTime = 0; uint8 i; ZStatus_t stat = ZSuccess; osal_memset( (uint8*)&group, 0, sizeof( aps_Group_t ) ); pData = pInMsg->pData; group.ID = BUILD_UINT16( pData[0], pData[1] ); switch ( pInMsg->hdr.commandID ) { case COMMAND_GROUP_ADD: status = zclGeneral_AddGroup( pInMsg->msg->endPoint, &group, pData ); if ( status != ZSuccess ) { if ( status == ZApsDuplicateEntry ) status = ZCL_STATUS_DUPLICATE_EXISTS; else status = ZCL_STATUS_INSUFFICIENT_SPACE; } zclGeneral_SendGroupAddResponse( pInMsg->msg->endPoint, &pInMsg->msg->srcAddr, status, group.ID, true, pInMsg->hdr.transSeqNum ); stat = ZCL_STATUS_CMD_HAS_RSP; break; case COMMAND_GROUP_VIEW: pGroup = aps_FindGroup( pInMsg->msg->endPoint, group.ID ); if ( pGroup ) { status = ZCL_STATUS_SUCCESS; } else { // Group not found status = ZCL_STATUS_NOT_FOUND; pGroup = &group; } zclGeneral_SendGroupViewResponse( pInMsg->msg->endPoint, &pInMsg->msg->srcAddr, status, pGroup, true, pInMsg->hdr.transSeqNum ); stat = ZCL_STATUS_CMD_HAS_RSP; break; case COMMAND_GROUP_GET_MEMBERSHIP: grpCnt = *pData++; // Allocate space for the group list grpList = osal_mem_alloc( sizeof( uint16 ) * APS_MAX_GROUPS ); if ( grpList != NULL ) { if ( grpCnt == 0 ) { // Find out all the groups of which the endpoint is a member. grpRspCnt = aps_FindAllGroupsForEndpoint( pInMsg->msg->endPoint, grpList ); } else { // Find out the groups (in the list) of which the endpoint is a member. for ( i = 0; i < grpCnt; i++ ) { group.ID = BUILD_UINT16( pData[0], pData[1] ); pData += 2; if ( aps_FindGroup( pInMsg->msg->endPoint, group.ID ) ) grpList[grpRspCnt++] = group.ID; } } if ( grpCnt == 0 || grpRspCnt != 0 ) { zclGeneral_SendGroupGetMembershipResponse( pInMsg->msg->endPoint, &pInMsg->msg->srcAddr, aps_GroupsRemaingCapacity(), grpRspCnt, grpList, true, pInMsg->hdr.transSeqNum ); } osal_mem_free( grpList ); } else { // Couldn't allocate space for the group list -- send a Default Response command back. zclDefaultRspCmd_t defaultRspCmd; defaultRspCmd.commandID = pInMsg->hdr.commandID; defaultRspCmd.statusCode = ZCL_STATUS_INSUFFICIENT_SPACE; zcl_SendDefaultRspCmd( pInMsg->msg->endPoint, &(pInMsg->msg->srcAddr), pInMsg->msg->clusterId, &defaultRspCmd, ZCL_FRAME_SERVER_CLIENT_DIR, true, 0, pInMsg->hdr.transSeqNum ); } stat = ZCL_STATUS_CMD_HAS_RSP; break; case COMMAND_GROUP_REMOVE: if ( aps_RemoveGroup( pInMsg->msg->endPoint, group.ID ) ) status = ZCL_STATUS_SUCCESS; else status = ZCL_STATUS_NOT_FOUND; zclGeneral_SendGroupRemoveResponse( pInMsg->msg->endPoint, &pInMsg->msg->srcAddr, status, group.ID, true, pInMsg->hdr.transSeqNum ); stat = ZCL_STATUS_CMD_HAS_RSP; break; case COMMAND_GROUP_REMOVE_ALL: aps_RemoveAllGroup( pInMsg->msg->endPoint ); break; case COMMAND_GROUP_ADD_IF_IDENTIFYING: // Retrieve Identify Time if ( zclFindAttrRec( pInMsg->msg->endPoint, ZCL_CLUSTER_ID_GEN_IDENTIFY, ATTRID_IDENTIFY_TIME, &attrRec ) ) zclReadAttrData( (uint8 *)&identifyTime, &attrRec, NULL ); // Is device identifying itself? if ( identifyTime > 0 ) zclGeneral_AddGroup( pInMsg->msg->endPoint, &group, pData ); break; default: stat = ZFailure; break; } return ( stat ); } /********************************************************************* * @fn zclGeneral_ProcessInGroupsClient * * @brief Process in the received Groups Command. * * @param pInMsg - pointer to the incoming message * * @return ZStatus_t */ static ZStatus_t zclGeneral_ProcessInGroupsClient( zclIncoming_t *pInMsg, zclGeneral_AppCallbacks_t *pCBs ) { aps_Group_t group; uint8 *pData = pInMsg->pData; uint8 grpCnt; uint8 nameLen; zclGroupRsp_t rsp; uint8 i; ZStatus_t stat = ZSuccess; osal_memset( (uint8*)&group, 0, sizeof( aps_Group_t ) ); osal_memset( (uint8*)&rsp, 0, sizeof( zclGroupRsp_t ) ); switch ( pInMsg->hdr.commandID ) { case COMMAND_GROUP_ADD_RSP: case COMMAND_GROUP_VIEW_RSP: case COMMAND_GROUP_REMOVE_RSP: rsp.status = *pData++; group.ID = BUILD_UINT16( pData[0], pData[1] ); if ( rsp.status == ZCL_STATUS_SUCCESS && pInMsg->hdr.commandID == COMMAND_GROUP_VIEW_RSP ) { pData += 2; // Move past ID nameLen = *pData++; if ( nameLen > (APS_GROUP_NAME_LEN-1) ) nameLen = (APS_GROUP_NAME_LEN-1); group.name[0] = nameLen; osal_memcpy( &(group.name[1]), pData, nameLen ); rsp.grpName = group.name; } if ( pCBs->pfnGroupRsp ) { rsp.srcAddr = &(pInMsg->msg->srcAddr); rsp.cmdID = pInMsg->hdr.commandID; rsp.grpCnt = 1; rsp.grpList = &group.ID; rsp.capacity = 0; pCBs->pfnGroupRsp( &rsp ); } break; case COMMAND_GROUP_GET_MEMBERSHIP_RSP: { uint16 *grpList = NULL; rsp.capacity = *pData++; grpCnt = *pData++; if ( grpCnt > 0 ) { // Allocate space for the group list grpList = osal_mem_alloc( sizeof( uint16 ) * grpCnt ); if ( grpList != NULL ) { rsp.grpCnt = grpCnt; for ( i = 0; i < grpCnt; i++ ) { grpList[i] = BUILD_UINT16( pData[0], pData[1] ); pData += 2; } } } if ( pCBs->pfnGroupRsp ) { rsp.srcAddr = &(pInMsg->msg->srcAddr); rsp.cmdID = pInMsg->hdr.commandID; rsp.grpList = grpList; pCBs->pfnGroupRsp( &rsp ); } if ( grpList != NULL ) { osal_mem_free( grpList ); } } break; default: stat = ZFailure; break; } return ( stat ); } #endif // ZCL_GROUPS #ifdef ZCL_SCENES /********************************************************************* * @fn zclGeneral_AddScene * * @brief Add a scene for an endpoint * * @param endpoint - * @param scene - new scene item * * @return ZStatus_t */ ZStatus_t zclGeneral_AddScene( uint8 endpoint, zclGeneral_Scene_t *scene ) { zclGenSceneItem_t *pNewItem; zclGenSceneItem_t *pLoop; // Fill in the new profile list pNewItem = osal_mem_alloc( sizeof( zclGenSceneItem_t ) ); if ( pNewItem == NULL ) return ( ZMemError ); // Fill in the plugin record. pNewItem->next = (zclGenSceneItem_t *)NULL; pNewItem->endpoint = endpoint; osal_memcpy( (uint8*)&(pNewItem->scene), (uint8*)scene, sizeof ( zclGeneral_Scene_t )); // Find spot in list if ( zclGenSceneTable == NULL ) { zclGenSceneTable = pNewItem; } else { // Look for end of list pLoop = zclGenSceneTable; while ( pLoop->next != NULL ) pLoop = pLoop->next; // Put new item at end of list pLoop->next = pNewItem; } // Update NV zclGeneral_ScenesWriteNV(); return ( ZSuccess ); } /********************************************************************* * @fn zclGeneral_FindScene * * @brief Find a scene with endpoint and sceneID * * @param endpoint - * @param groupID - what group the scene belongs to * @param sceneID - ID to look for scene * * @return a pointer to the scene information, NULL if not found */ zclGeneral_Scene_t *zclGeneral_FindScene( uint8 endpoint, uint16 groupID, uint8 sceneID ) { zclGenSceneItem_t *pLoop; // Look for end of list pLoop = zclGenSceneTable; while ( pLoop ) { if ( (pLoop->endpoint == endpoint || endpoint == 0xFF) && pLoop->scene.groupID == groupID && pLoop->scene.ID == sceneID ) { return ( &(pLoop->scene) ); } pLoop = pLoop->next; } return ( (zclGeneral_Scene_t *)NULL ); } /********************************************************************* * @fn aps_FindAllScensForGroup * * @brief Find all the scenes with groupID * * @param endpoint - endpoint to look for * @param sceneList - List to hold scene IDs (should hold APS_MAX_SCENES entries) * * @return number of scenes copied to sceneList */ uint8 zclGeneral_FindAllScenesForGroup( uint8 endpoint, uint16 groupID, uint8 *sceneList ) { zclGenSceneItem_t *pLoop; uint8 cnt = 0; // Look for end of list pLoop = zclGenSceneTable; while ( pLoop ) { if ( pLoop->endpoint == endpoint && pLoop->scene.groupID == groupID ) sceneList[cnt++] = pLoop->scene.ID; pLoop = pLoop->next; } return ( cnt ); } /********************************************************************* * @fn zclGeneral_RemoveScene * * @brief Remove a scene with endpoint and sceneID * * @param endpoint - * @param groupID - what group the scene belongs to * @param sceneID - ID to look for scene * * @return TRUE if removed, FALSE if not found */ uint8 zclGeneral_RemoveScene( uint8 endpoint, uint16 groupID, uint8 sceneID ) { zclGenSceneItem_t *pLoop; zclGenSceneItem_t *pPrev; // Look for end of list pLoop = zclGenSceneTable; pPrev = NULL; while ( pLoop ) { if ( pLoop->endpoint == endpoint && pLoop->scene.groupID == groupID && pLoop->scene.ID == sceneID ) { if ( pPrev == NULL ) zclGenSceneTable = pLoop->next; else pPrev->next = pLoop->next; // Free the memory osal_mem_free( pLoop ); // Update NV zclGeneral_ScenesWriteNV(); return ( TRUE ); } pPrev = pLoop; pLoop = pLoop->next; } return ( FALSE ); } /********************************************************************* * @fn zclGeneral_RemoveAllScenes * * @brief Remove all scenes with endpoint and group Id * * @param endpoint - * @param groupID - ID to look for group * * @return none */ void zclGeneral_RemoveAllScenes( uint8 endpoint, uint16 groupID ) { zclGenSceneItem_t *pLoop; zclGenSceneItem_t *pPrev; zclGenSceneItem_t *pNext; // Look for end of list pLoop = zclGenSceneTable; pPrev = NULL; while ( pLoop ) { if ( pLoop->endpoint == endpoint && pLoop->scene.groupID == groupID ) { if ( pPrev == NULL ) zclGenSceneTable = pLoop->next; else pPrev->next = pLoop->next; pNext = pLoop->next; // Free the memory osal_mem_free( pLoop ); pLoop = pNext; } else { pPrev = pLoop; pLoop = pLoop->next; } } // Update NV zclGeneral_ScenesWriteNV(); } /********************************************************************* * @fn zclGeneral_CountScenes * * @brief Count the number of scenes for an endpoint * * @param endpoint - * * @return number of scenes assigned to an endpoint */ uint8 zclGeneral_CountScenes( uint8 endpoint ) { zclGenSceneItem_t *pLoop; uint8 cnt = 0; // Look for end of list pLoop = zclGenSceneTable; while ( pLoop ) { if ( pLoop->endpoint == endpoint ) cnt++; pLoop = pLoop->next; } return ( cnt ); } /********************************************************************* * @fn zclGeneral_CountAllScenes * * @brief Count the total number of scenes * * @param none * * @return number of scenes */ uint8 zclGeneral_CountAllScenes( void ) { zclGenSceneItem_t *pLoop; uint8 cnt = 0; // Look for end of list pLoop = zclGenSceneTable; while ( pLoop ) { cnt++; pLoop = pLoop->next; } return ( cnt ); } /********************************************************************* * @fn zclGeneral_ReadSceneCountCB * * @brief Read the number of scenes currently in the device's * scene table (i.e., the Scene Count attribute). * * Note: This function gets called only when the pointer * 'dataPtr' to the Scene Count attribute value is * NULL in the attribute database registered with * the ZCL. * * @param clusterId - cluster that attribute belongs to * @param attrId - attribute to be read * @param oper - ZCL_OPER_LEN, ZCL_OPER_READ * @param pValue - pointer to attribute value * @param pLen - pointer to length of attribute value read * * @return status */ ZStatus_t zclGeneral_ReadSceneCountCB( uint16 clusterId, uint16 attrId, uint8 oper, uint8 *pValue, uint16 *pLen ) { ZStatus_t status = ZCL_STATUS_SUCCESS; // This callback function should only be called for the Scene Count attribute switch ( oper ) { case ZCL_OPER_LEN: *pLen = 1; // uint8 break; case ZCL_OPER_READ: *pValue = zclGeneral_CountAllScenes(); if ( pLen != NULL ) { *pLen = 1; } break; default: status = ZCL_STATUS_SOFTWARE_FAILURE; // should never get here! break; } return ( status ); } /********************************************************************* * @fn zclGeneral_ProcessInScenesServer * * @brief Process in the received Scenes Command. * * @param pInMsg - pointer to the incoming message * * @return ZStatus_t */ static ZStatus_t zclGeneral_ProcessInScenesServer( zclIncoming_t *pInMsg, zclGeneral_AppCallbacks_t *pCBs ) { zclAttrRec_t attrRec; zclGeneral_Scene_t scene; zclGeneral_Scene_t *pScene; uint8 *pData = pInMsg->pData; uint8 nameLen; uint8 status; uint8 sceneCnt = 0; uint8 *sceneList = NULL; uint8 sendRsp = FALSE; uint8 nameSupport = FALSE; ZStatus_t stat = ZSuccess; osal_memset( (uint8*)&scene, 0, sizeof( zclGeneral_Scene_t ) ); scene.groupID = BUILD_UINT16( pData[0], pData[1] ); pData += 2; // Move past group ID scene.ID = *pData++; switch ( pInMsg->hdr.commandID ) { case COMMAND_SCENE_ADD: // Parse the rest of the incoming message scene.transTime = BUILD_UINT16( pData[0], pData[1] ); pData += 2; nameLen= *pData++; // Name length // Retrieve Name Support attribute if ( zclFindAttrRec( pInMsg->msg->endPoint, ZCL_CLUSTER_ID_GEN_SCENES, ATTRID_SCENES_NAME_SUPPORT, &attrRec ) ) { zclReadAttrData( &nameSupport, &attrRec, NULL ); } if ( nameSupport ) { if ( nameLen > (ZCL_GEN_SCENE_NAME_LEN-1) ) { // truncate to maximum size scene.name[0] = ZCL_GEN_SCENE_NAME_LEN-1; } else { scene.name[0] = nameLen; } osal_memcpy( &(scene.name[1]), pData, scene.name[0] ); } pData += nameLen; // move past name, use original length scene.extLen = pInMsg->pDataLen - ( (uint16)( pData - pInMsg->pData ) ); if ( scene.extLen > 0 ) { // Copy the extention field(s) if ( scene.extLen > ZCL_GEN_SCENE_EXT_LEN ) { scene.extLen = ZCL_GEN_SCENE_EXT_LEN; } osal_memcpy( scene.extField, pData, scene.extLen ); } if ( scene.groupID == 0x0000 || aps_FindGroup( pInMsg->msg->endPoint, scene.groupID ) != NULL ) { // Either the Scene doesn't belong to a Group (Group ID = 0x0000) or it // does and the corresponding Group exits pScene = zclGeneral_FindScene( pInMsg->msg->endPoint, scene.groupID, scene.ID ); if ( pScene || ( zclGeneral_CountAllScenes() < ZCL_GEN_MAX_SCENES ) ) { status = ZCL_STATUS_SUCCESS; if ( pScene != NULL ) { // The Scene already exists so update it pScene->transTime = scene.transTime; osal_memcpy( pScene->name, scene.name, ZCL_GEN_SCENE_NAME_LEN ); // Use the new extention field(s) osal_memcpy( pScene->extField, scene.extField, scene.extLen ); pScene->extLen = scene.extLen; // Update NV zclGeneral_ScenesWriteNV(); } else { // The Scene doesn't exist so add it zclGeneral_AddScene( pInMsg->msg->endPoint, &scene ); } } else { status = ZCL_STATUS_INSUFFICIENT_SPACE; // The Scene Table is full } } else { status = ZCL_STATUS_INVALID_FIELD; // The Group is not in the Group Table } zclGeneral_SendSceneAddResponse( pInMsg->msg->endPoint, &pInMsg->msg->srcAddr, status, scene.groupID, scene.ID, true, pInMsg->hdr.transSeqNum ); stat = ZCL_STATUS_CMD_HAS_RSP; break; case COMMAND_SCENE_VIEW: pScene = zclGeneral_FindScene( pInMsg->msg->endPoint, scene.groupID, scene.ID ); if ( pScene != NULL ) { status = ZCL_STATUS_SUCCESS; } else { // Scene not found if ( scene.groupID != 0x0000 && aps_FindGroup( pInMsg->msg->endPoint, scene.groupID ) == NULL ) { status = ZCL_STATUS_INVALID_FIELD; // The Group is not in the Group Table } else { status = ZCL_STATUS_NOT_FOUND; } pScene = &scene; } zclGeneral_SendSceneViewResponse( pInMsg->msg->endPoint, &pInMsg->msg->srcAddr, status, pScene, true, pInMsg->hdr.transSeqNum ); stat = ZCL_STATUS_CMD_HAS_RSP; break; case COMMAND_SCENE_REMOVE: if ( zclGeneral_RemoveScene( pInMsg->msg->endPoint, scene.groupID, scene.ID ) ) { status = ZCL_STATUS_SUCCESS; } else { // Scene not found if ( aps_FindGroup( pInMsg->msg->endPoint, scene.groupID ) == NULL ) { // The Group is not in the Group Table status = ZCL_STATUS_INVALID_FIELD; } else { status = ZCL_STATUS_NOT_FOUND; } } if ( UNICAST_MSG( pInMsg->msg ) ) { // Addressed to this device (not to a group) - send a response back zclGeneral_SendSceneRemoveResponse( pInMsg->msg->endPoint, &pInMsg->msg->srcAddr, status, scene.groupID, scene.ID, true, pInMsg->hdr.transSeqNum ); } stat = ZCL_STATUS_CMD_HAS_RSP; break; case COMMAND_SCENE_REMOVE_ALL: if ( scene.groupID == 0x0000 || aps_FindGroup( pInMsg->msg->endPoint, scene.groupID ) != NULL ) { zclGeneral_RemoveAllScenes( pInMsg->msg->endPoint, scene.groupID ); status = ZCL_STATUS_SUCCESS; } else { status = ZCL_STATUS_INVALID_FIELD; } if ( UNICAST_MSG( pInMsg->msg ) ) { // Addressed to this device (not to a group) - send a response back zclGeneral_SendSceneRemoveAllResponse( pInMsg->msg->endPoint, &pInMsg->msg->srcAddr, status, scene.groupID, true, pInMsg->hdr.transSeqNum ); } stat = ZCL_STATUS_CMD_HAS_RSP; break; case COMMAND_SCENE_STORE: if ( scene.groupID == 0x0000 || aps_FindGroup( pInMsg->msg->endPoint, scene.groupID ) != NULL ) { // Either the Scene doesn't belong to a Group (Group ID = 0x0000) or it // does and the corresponding Group exits pScene = zclGeneral_FindScene( pInMsg->msg->endPoint, scene.groupID, scene.ID ); if ( pScene || ( zclGeneral_CountAllScenes() < ZCL_GEN_MAX_SCENES ) ) { uint8 sceneChanged = FALSE; status = ZCL_STATUS_SUCCESS; if ( pScene == NULL ) { // Haven't been added yet pScene = &scene; } if ( pCBs->pfnSceneStoreReq ) { zclSceneReq_t req; req.srcAddr = &(pInMsg->msg->srcAddr); req.scene = pScene; // Get the latest Scene info if ( pCBs->pfnSceneStoreReq( &req ) ) { sceneChanged = TRUE; } } if ( pScene == &scene ) { // The Scene doesn't exist so add it zclGeneral_AddScene( pInMsg->msg->endPoint, &scene ); } else if ( sceneChanged ) { // The Scene already exists so update only NV zclGeneral_ScenesWriteNV(); } } else { status = ZCL_STATUS_INSUFFICIENT_SPACE; // The Scene Table is full } } else { status = ZCL_STATUS_INVALID_FIELD; // The Group is not in the Group Table } if ( UNICAST_MSG( pInMsg->msg ) ) { // Addressed to this device (not to a group) - send a response back zclGeneral_SendSceneStoreResponse( pInMsg->msg->endPoint, &pInMsg->msg->srcAddr, status, scene.groupID, scene.ID, true, pInMsg->hdr.transSeqNum ); } stat = ZCL_STATUS_CMD_HAS_RSP; break; case COMMAND_SCENE_RECALL: pScene = zclGeneral_FindScene( pInMsg->msg->endPoint, scene.groupID, scene.ID ); if ( pScene && pCBs->pfnSceneRecallReq ) { zclSceneReq_t req; req.srcAddr = &(pInMsg->msg->srcAddr); req.scene = pScene; pCBs->pfnSceneRecallReq( &req ); } // No response break; case COMMAND_SCENE_GET_MEMBERSHIP: // Find all the Scenes corresponding to the Group ID if ( scene.groupID == 0x0000 || aps_FindGroup( pInMsg->msg->endPoint, scene.groupID ) != NULL ) { // Allocate space for the scene list sceneList = osal_mem_alloc( ZCL_GEN_MAX_SCENES ); if ( sceneList != NULL ) { sceneCnt = zclGeneral_FindAllScenesForGroup( pInMsg->msg->endPoint, scene.groupID, sceneList ); status = ZCL_STATUS_SUCCESS; if ( UNICAST_MSG( pInMsg->msg ) ) { // Addressed only to this device - send a response back sendRsp = TRUE; } else { // Addressed to the Group - ONLY send a response if an entry within the // Scene Table corresponds to the Group ID if ( sceneCnt != 0 ) { sendRsp = TRUE; } } } else { // Couldn't allocate space for the scene list! status = ZCL_STATUS_INSUFFICIENT_SPACE; sendRsp = TRUE; } } else { // The Group is not in the Group Table - send a response back status = ZCL_STATUS_INVALID_FIELD; sendRsp = TRUE; } if ( sendRsp ) { zclGeneral_SendSceneGetMembershipResponse( pInMsg->msg->endPoint, &pInMsg->msg->srcAddr, status, zclGeneral_ScenesRemaingCapacity(), sceneCnt, sceneList, scene.groupID, true, pInMsg->hdr.transSeqNum ); } if ( sceneList != NULL ) { osal_mem_free( sceneList ); } stat = ZCL_STATUS_CMD_HAS_RSP; break; default: stat = ZFailure; break; } return ( stat ); } /********************************************************************* * @fn zclGeneral_ProcessInScenesClient * * @brief Process in the received Scenes Command. * * @param pInMsg - pointer to the incoming message * * @return ZStatus_t */ static ZStatus_t zclGeneral_ProcessInScenesClient( zclIncoming_t *pInMsg, zclGeneral_AppCallbacks_t *pCBs ) { zclGeneral_Scene_t scene; uint8 *pData = pInMsg->pData; uint8 nameLen; zclSceneRsp_t rsp; uint8 i; ZStatus_t stat = ZSuccess; osal_memset( (uint8*)&scene, 0, sizeof( zclGeneral_Scene_t ) ); osal_memset( (uint8*)&rsp, 0, sizeof( zclSceneRsp_t ) ); // Get the status field first rsp.status = *pData++; if ( pInMsg->hdr.commandID == COMMAND_SCENE_GET_MEMBERSHIP_RSP ) { rsp.capacity = *pData++; } scene.groupID = BUILD_UINT16( pData[0], pData[1] ); pData += 2; // Move past group ID switch ( pInMsg->hdr.commandID ) { case COMMAND_SCENE_VIEW_RSP: // Parse the rest of the incoming message scene.ID = *pData++; // Not applicable to Remove All Response command scene.transTime = BUILD_UINT16( pData[0], pData[1] ); pData += 2; nameLen = *pData++; // Name length if ( nameLen > (ZCL_GEN_SCENE_NAME_LEN-1) ) { // truncate to maximum size scene.name[0] = ZCL_GEN_SCENE_NAME_LEN-1; } else { scene.name[0] = nameLen; } osal_memcpy( &(scene.name[1]), pData, scene.name[0] ); pData += nameLen; // move past name, use original length //*** Do something with the extension field(s) // Fall through to callback - break is left off intentionally case COMMAND_SCENE_ADD_RSP: case COMMAND_SCENE_REMOVE_RSP: case COMMAND_SCENE_REMOVE_ALL_RSP: case COMMAND_SCENE_STORE_RSP: if ( pCBs->pfnSceneRsp ) { rsp.srcAddr = &(pInMsg->msg->srcAddr); rsp.cmdID = pInMsg->hdr.commandID; rsp.scene = &scene; pCBs->pfnSceneRsp( &rsp ); } break; case COMMAND_SCENE_GET_MEMBERSHIP_RSP: { uint8 *sceneList = NULL; if ( rsp.status == ZCL_STATUS_SUCCESS ) { uint8 sceneCnt = *pData++; if ( sceneCnt > 0 ) { // Allocate space for the scene list sceneList = osal_mem_alloc( sceneCnt ); if ( sceneList != NULL ) { rsp.sceneCnt = sceneCnt; for ( i = 0; i < sceneCnt; i++ ) sceneList[i] = *pData++; } } } if ( pCBs->pfnSceneRsp ) { rsp.srcAddr = &(pInMsg->msg->srcAddr); rsp.cmdID = pInMsg->hdr.commandID; rsp.sceneList = sceneList; rsp.scene = &scene; pCBs->pfnSceneRsp( &rsp); } if ( sceneList != NULL ) { osal_mem_free( sceneList ); } } break; default: stat = ZFailure; break; } return ( stat ); } #endif // ZCL_SCENES #ifdef ZCL_ON_OFF /********************************************************************* * @fn zclGeneral_ProcessInCmdOnOff * * @brief Process in the received On/Off Command. * * @param pInMsg - pointer to the incoming message * * @return ZStatus_t */ static ZStatus_t zclGeneral_ProcessInOnOff( zclIncoming_t *pInMsg, zclGeneral_AppCallbacks_t *pCBs ) { if ( zcl_ServerCmd( pInMsg->hdr.fc.direction ) ) { if ( pInMsg->hdr.commandID > COMMAND_TOGGLE ) return ( ZFailure ); // Error ignore the command if ( pCBs->pfnOnOff ) pCBs->pfnOnOff( pInMsg->hdr.commandID ); } // no Client command return ( ZSuccess ); } #endif // ZCL_ON_OFF #ifdef ZCL_LEVEL_CTRL /********************************************************************* * @fn zclGeneral_ProcessInLevelControl * * @brief Process in the received Level Control Command. * * @param pInMsg - pointer to the incoming message * * @return ZStatus_t */ static ZStatus_t zclGeneral_ProcessInLevelControl( zclIncoming_t *pInMsg, zclGeneral_AppCallbacks_t *pCBs ) { uint8 withOnOff = FALSE; ZStatus_t stat = ZSuccess; if ( zcl_ServerCmd( pInMsg->hdr.fc.direction ) ) { switch ( pInMsg->hdr.commandID ) { case COMMAND_LEVEL_MOVE_TO_LEVEL_WITH_ON_OFF: withOnOff = TRUE; // fall through case COMMAND_LEVEL_MOVE_TO_LEVEL: if ( pCBs->pfnLevelControlMoveToLevel ) { zclLCMoveToLevel_t cmd; cmd.level = pInMsg->pData[0]; cmd.transitionTime = BUILD_UINT16( pInMsg->pData[1], pInMsg->pData[2] ); cmd.withOnOff = withOnOff; pCBs->pfnLevelControlMoveToLevel( &cmd ); } break; case COMMAND_LEVEL_MOVE_WITH_ON_OFF: withOnOff = TRUE; // fall through case COMMAND_LEVEL_MOVE: if ( pCBs->pfnLevelControlMove ) { zclLCMove_t cmd; cmd.moveMode = pInMsg->pData[0]; cmd.rate = pInMsg->pData[1]; cmd.withOnOff = withOnOff; pCBs->pfnLevelControlMove( &cmd ); } break; case COMMAND_LEVEL_STEP_WITH_ON_OFF: withOnOff = TRUE; // fall through case COMMAND_LEVEL_STEP: if ( pCBs->pfnLevelControlStep ) { zclLCStep_t cmd; cmd.stepMode = pInMsg->pData[0]; cmd.amount = pInMsg->pData[1]; cmd.transitionTime = BUILD_UINT16( pInMsg->pData[2], pInMsg->pData[3] ); cmd.withOnOff = withOnOff; pCBs->pfnLevelControlStep( &cmd ); } break; case COMMAND_LEVEL_STOP: case COMMAND_LEVEL_STOP_WITH_ON_OFF: // Both Stop commands are identical if ( pCBs->pfnLevelControlStop ) { pCBs->pfnLevelControlStop(); } break; default: stat = ZFailure; break; } } // no Client command return ( stat ); } #endif // ZCL_LEVEL_CTRL #ifdef ZCL_ALARMS /********************************************************************* * @fn zclGeneral_AddAlarm * * @brief Add an alarm for a cluster * * @param endpoint - * @param alarm - new alarm item * * @return ZStatus_t */ ZStatus_t zclGeneral_AddAlarm( uint8 endpoint, zclGeneral_Alarm_t *alarm ) { zclGenAlarmItem_t *pNewItem; zclGenAlarmItem_t *pLoop; // Fill in the new profile list pNewItem = osal_mem_alloc( sizeof( zclGenAlarmItem_t ) ); if ( pNewItem == NULL ) return ( ZMemError ); // Fill in the plugin record. pNewItem->next = (zclGenAlarmItem_t *)NULL; pNewItem->endpoint = endpoint; osal_memcpy( (uint8*)(&pNewItem->alarm), (uint8*)alarm, sizeof ( zclGeneral_Alarm_t ) ); // Find spot in list if ( zclGenAlarmTable == NULL ) { zclGenAlarmTable = pNewItem; } else { // Look for end of list pLoop = zclGenAlarmTable; while ( pLoop->next != NULL ) pLoop = pLoop->next; // Put new item at end of list pLoop->next = pNewItem; } return ( ZSuccess ); } /********************************************************************* * @fn zclGeneral_FindAlarm * * @brief Find an alarm with alarmCode and clusterID * * @param endpoint - * @param groupID - what group the scene belongs to * @param sceneID - ID to look for scene * * @return a pointer to the alarm information, NULL if not found */ zclGeneral_Alarm_t *zclGeneral_FindAlarm( uint8 endpoint, uint8 alarmCode, uint16 clusterID ) { zclGenAlarmItem_t *pLoop; // Look for the alarm pLoop = zclGenAlarmTable; while ( pLoop ) { if ( pLoop->endpoint == endpoint && pLoop->alarm.code == alarmCode && pLoop->alarm.clusterID == clusterID ) { return ( &(pLoop->alarm) ); } pLoop = pLoop->next; } return ( (zclGeneral_Alarm_t *)NULL ); } /********************************************************************* * @fn zclGeneral_FindEarliestAlarm * * @brief Find an alarm with the earliest timestamp * * @param endpoint - * * @return a pointer to the alarm information, NULL if not found */ zclGeneral_Alarm_t *zclGeneral_FindEarliestAlarm( uint8 endpoint ) { zclGenAlarmItem_t *pLoop; zclGenAlarmItem_t earliestAlarm; zclGenAlarmItem_t *pEarliestAlarm = &earliestAlarm; pEarliestAlarm->alarm.timeStamp = 0xFFFFFFFF; // Look for alarm with earliest time pLoop = zclGenAlarmTable; while ( pLoop ) { if ( pLoop->endpoint == endpoint && pLoop->alarm.timeStamp < pEarliestAlarm->alarm.timeStamp ) { pEarliestAlarm = pLoop; } pLoop = pLoop->next; } if ( pEarliestAlarm->alarm.timeStamp != 0xFFFFFFFF ) return ( &(pEarliestAlarm->alarm) ); // No alarm return ( (zclGeneral_Alarm_t *)NULL ); } /********************************************************************* * @fn zclGeneral_ResetAlarm * * @brief Remove a scene with endpoint and sceneID * * @param endpoint - * @param alarmCode - * @param clusterID - * * @return TRUE if removed, FALSE if not found */ void zclGeneral_ResetAlarm( uint8 endpoint, uint8 alarmCode, uint16 clusterID ) { zclGenAlarmItem_t *pLoop; zclGenAlarmItem_t *pPrev; // Look for end of list pLoop = zclGenAlarmTable; pPrev = NULL; while ( pLoop ) { if ( pLoop->endpoint == endpoint && pLoop->alarm.code == alarmCode && pLoop->alarm.clusterID == clusterID ) { if ( pPrev == NULL ) zclGenAlarmTable = pLoop->next; else pPrev->next = pLoop->next; // Free the memory osal_mem_free( pLoop ); // Notify the Application so that if the alarm condition still active then // a new notification will be generated, and a new alarm record will be // added to the alarm log // zclGeneral_NotifyReset( alarmCode, clusterID ); // callback function? return; } pPrev = pLoop; pLoop = pLoop->next; } } /********************************************************************* * @fn zclGeneral_ResetAllAlarms * * @brief Remove all alarms with endpoint * * @param endpoint - * @param notifyApp - * * @return none */ void zclGeneral_ResetAllAlarms( uint8 endpoint, uint8 notifyApp ) { zclGenAlarmItem_t *pLoop; zclGenAlarmItem_t *pPrev; zclGenAlarmItem_t *pNext; // Look for end of list pLoop = zclGenAlarmTable; pPrev = NULL; while ( pLoop ) { if ( pLoop->endpoint == endpoint ) { if ( pPrev == NULL ) zclGenAlarmTable = pLoop->next; else pPrev->next = pLoop->next; pNext = pLoop->next; // Free the memory osal_mem_free( pLoop ); pLoop = pNext; } else { pPrev = pLoop; pLoop = pLoop->next; } } if ( notifyApp ) { // Notify the Application so that if any alarm conditions still active then // a new notification will be generated, and a new alarm record will be // added to the alarm log // zclGeneral_NotifyResetAll(); // callback function? } } /********************************************************************* * @fn zclGeneral_ProcessInAlarmsServer * * @brief Process in the received Alarms Command. * * @param pInMsg - pointer to the incoming message * * @return ZStatus_t */ static ZStatus_t zclGeneral_ProcessInAlarmsServer( zclIncoming_t *pInMsg, zclGeneral_AppCallbacks_t *pCBs ) { zclGeneral_Alarm_t *pAlarm; uint8 *pData = pInMsg->pData; ZStatus_t stat = ZSuccess; switch ( pInMsg->hdr.commandID ) { case COMMAND_ALARMS_RESET: zclGeneral_ResetAlarm( pInMsg->msg->endPoint, pData[0], BUILD_UINT16( pData[1], pData[2] ) ); break; case COMMAND_ALARMS_RESET_ALL: zclGeneral_ResetAllAlarms( pInMsg->msg->endPoint, TRUE ); break; case COMMAND_ALARMS_GET: pAlarm = zclGeneral_FindEarliestAlarm( pInMsg->msg->endPoint ); if ( pAlarm ) { // Send a response back zclGeneral_SendAlarmGetRespnose( pInMsg->msg->endPoint, &pInMsg->msg->srcAddr, ZCL_STATUS_SUCCESS, pAlarm->code, pAlarm->clusterID, pAlarm->timeStamp, true, pInMsg->hdr.transSeqNum ); // Remove the entry from the Alarm table zclGeneral_ResetAlarm( pInMsg->msg->endPoint, pAlarm->code, pAlarm->clusterID ); } else { // Send a response back zclGeneral_SendAlarmGetRespnose( pInMsg->msg->endPoint, &pInMsg->msg->srcAddr, ZCL_STATUS_NOT_FOUND, 0, 0, 0, true, pInMsg->hdr.transSeqNum ); } stat = ZCL_STATUS_CMD_HAS_RSP; break; case COMMAND_ALARMS_RESET_LOG: zclGeneral_ResetAllAlarms( pInMsg->msg->endPoint, FALSE ); break; #ifdef SE_UK_EXT case COMMAND_ALARMS_PUBLISH_EVENT_LOG: if ( pCBs->pfnPublishEventLog ) { zclPublishEventLog_t eventLog; eventLog.logID = *pData++; eventLog.cmdIndex = *pData++; eventLog.totalCmds = *pData++; // First try to find out number of Sub Log Payloads eventLog.numSubLogs = (pInMsg->pDataLen-3)/(1+4); // event ID + event time if ( eventLog.numSubLogs > 0 ) { // Try to alloc space for Log Payload eventLog.pLogs = (zclEventLogPayload_t *)osal_mem_alloc( sizeof( zclEventLogPayload_t ) * eventLog.numSubLogs ); if ( eventLog.pLogs != NULL ) { // Copy Log Payload for ( uint8 i = 0; i < eventLog.numSubLogs; i++ ) { eventLog.pLogs[i].eventId = *pData++; eventLog.pLogs[i].eventTime = osal_build_uint32( pData, 4 ); pData += 4; } } else { stat = ZCL_STATUS_SOFTWARE_FAILURE; } } else { eventLog.pLogs = NULL; } if ( stat == ZSuccess ) { pCBs->pfnPublishEventLog( &(pInMsg->msg->srcAddr), &eventLog ); } if ( eventLog.pLogs != NULL ) { osal_mem_free( eventLog.pLogs ); } } break; #endif // SE_UK_EXT default: stat = ZFailure; break; } return ( stat ); } /********************************************************************* * @fn zclGeneral_ProcessInAlarmsClient * * @brief Process in the received Alarms Command. * * @param pInMsg - pointer to the incoming message * * @return ZStatus_t */ static ZStatus_t zclGeneral_ProcessInAlarmsClient( zclIncoming_t *pInMsg, zclGeneral_AppCallbacks_t *pCBs ) { uint8 *pData = pInMsg->pData; zclAlarm_t alarm; ZStatus_t stat = ZSuccess; osal_memset( (uint8*)&alarm, 0, sizeof( zclAlarm_t ) ); switch ( pInMsg->hdr.commandID ) { case COMMAND_ALARMS_ALARM: if ( pCBs->pfnAlarm ) { alarm.srcAddr = &(pInMsg->msg->srcAddr); alarm.cmdID = pInMsg->hdr.commandID; alarm.status = *pData++; alarm.alarmCode = *pData++; alarm.clusterID = BUILD_UINT16( pData[0], pData[1] ); pData += 2; alarm.timeStamp = osal_build_uint32( pData, 4 ); pCBs->pfnAlarm( &alarm ); } break; case COMMAND_ALARMS_GET_RSP: if ( pCBs->pfnAlarm ) { alarm.srcAddr = &(pInMsg->msg->srcAddr); alarm.cmdID = pInMsg->hdr.commandID; alarm.alarmCode = *pData++; alarm.clusterID = BUILD_UINT16( pData[0], pData[1] ); pCBs->pfnAlarm( &alarm ); } break; #ifdef SE_UK_EXT case COMMAND_ALARMS_GET_EVENT_LOG: if ( pCBs->pfnGetEventLog ) { zclGetEventLog_t eventLog; eventLog.logID = *pData++; eventLog.startTime = osal_build_uint32( pData, 4 ); pData += 4; eventLog.endTime = osal_build_uint32( pData, 4 ); pData += 4; eventLog.numEvents = *pData; pCBs->pfnGetEventLog( pInMsg->msg->endPoint, &(pInMsg->msg->srcAddr), &eventLog, pInMsg->hdr.transSeqNum ); } break; #endif // SE_UK_EXT default: stat = ZFailure; break; } return ( stat ); } #endif // ZCL_ALARMS #ifdef ZCL_LOCATION /********************************************************************* * @fn zclGeneral_ProcessInLocationServer * * @brief Process in the received Location Command. * * @param pInMsg - pointer to the incoming message * * @return ZStatus_t */ static ZStatus_t zclGeneral_ProcessInLocationServer( zclIncoming_t *pInMsg, zclGeneral_AppCallbacks_t *pCBs ) { uint8 *pData = pInMsg->pData; zclLocation_t cmd; ZStatus_t stat = ZSuccess; osal_memset( (uint8*)&cmd, 0, sizeof( zclLocation_t ) ); switch ( pInMsg->hdr.commandID ) { case COMMAND_LOCATION_SET_ABSOLUTE: cmd.un.absLoc.coordinate1 = BUILD_UINT16( pData[0], pData[1] ); pData += 2; cmd.un.absLoc.coordinate2 = BUILD_UINT16( pData[0], pData[1] ); pData += 2; cmd.un.absLoc.coordinate3 = BUILD_UINT16( pData[0], pData[1] ); pData += 2; cmd.un.absLoc.power = BUILD_UINT16( pData[0], pData[1] ); pData += 2; cmd.un.absLoc.pathLossExponent = BUILD_UINT16( pData[0], pData[1] ); if ( pCBs->pfnLocation ) { cmd.srcAddr = &(pInMsg->msg->srcAddr); cmd.cmdID = pInMsg->hdr.commandID; // Update the absolute location info pCBs->pfnLocation( &cmd ); } break; case COMMAND_LOCATION_SET_DEV_CFG: cmd.un.devCfg.power = BUILD_UINT16( pData[0], pData[1] ); pData += 2; cmd.un.devCfg.pathLossExponent = BUILD_UINT16( pData[0], pData[1] ); pData += 2; cmd.un.devCfg.calcPeriod = BUILD_UINT16( pData[0], pData[1] ); pData += 2; cmd.un.devCfg.numMeasurements = *pData++; cmd.un.devCfg.reportPeriod = BUILD_UINT16( pData[0], pData[1] ); if ( pCBs->pfnLocation ) { cmd.srcAddr = &(pInMsg->msg->srcAddr); cmd.cmdID = pInMsg->hdr.commandID; // Update the device configuration info pCBs->pfnLocation( &cmd ); } break; case COMMAND_LOCATION_GET_DEV_CFG: cmd.un.ieeeAddr = pData; if ( pCBs->pfnLocation ) { cmd.srcAddr = &(pInMsg->msg->srcAddr); cmd.cmdID = pInMsg->hdr.commandID; cmd.seqNum = pInMsg->hdr.transSeqNum; // Retreive the Device Configuration pCBs->pfnLocation( &cmd ); } stat = ZCL_STATUS_CMD_HAS_RSP; break; case COMMAND_LOCATION_GET_DATA: cmd.un.loc.bitmap.locByte = *pData++; cmd.un.loc.numResponses = *pData++; if ( cmd.un.loc.brdcastResponse == 0 ) // command is sent as a unicast osal_cpyExtAddr( cmd.un.loc.targetAddr, pData ); if ( pCBs->pfnLocation ) { cmd.srcAddr = &(pInMsg->msg->srcAddr); cmd.cmdID = pInMsg->hdr.commandID; cmd.seqNum = pInMsg->hdr.transSeqNum; // Retreive the Location Data pCBs->pfnLocation( &cmd ); } stat = ZCL_STATUS_CMD_HAS_RSP; break; default: stat = ZFailure; break; } return ( stat ); } /********************************************************************* * @fn zclGeneral_ProcessInLocationDataRsp * * @brief Process in the received Location Command. * * @param pInMsg - pointer to the incoming message * * @return ZStatus_t */ static void zclGeneral_ProcessInLocationDataRsp( zclIncoming_t *pInMsg, zclGeneral_AppCallbacks_t *pCBs ) { uint8 *pData = pInMsg->pData; zclLocationRsp_t rsp; osal_memset( (uint8*)&rsp, 0, sizeof( zclLocationRsp_t ) ); if ( pCBs->pfnLocationRsp ) { if ( pInMsg->hdr.commandID == COMMAND_LOCATION_DATA_RSP ) rsp.un.loc.status = *pData++; if ( pInMsg->hdr.commandID != COMMAND_LOCATION_DATA_RSP || rsp.un.loc.status == ZCL_STATUS_SUCCESS ) { rsp.un.loc.data.type = *pData++; rsp.un.loc.data.absLoc.coordinate1 = BUILD_UINT16( pData[0], pData[1] ); pData += 2; rsp.un.loc.data.absLoc.coordinate2 = BUILD_UINT16( pData[0], pData[1] ); pData += 2; if ( locationType2D( rsp.un.loc.data.type ) == 0 ) { rsp.un.loc.data.absLoc.coordinate3 = BUILD_UINT16( pData[0], pData[1] ); pData += 2; } if ( pInMsg->hdr.commandID != COMMAND_LOCATION_COMPACT_DATA_NOTIF ) { rsp.un.loc.data.absLoc.power = BUILD_UINT16( pData[0], pData[1] ); pData += 2; rsp.un.loc.data.absLoc.pathLossExponent = BUILD_UINT16( pData[0], pData[1] ); pData += 2; } if ( locationTypeAbsolute( rsp.un.loc.data.type ) == 0 ) { if ( pInMsg->hdr.commandID != COMMAND_LOCATION_COMPACT_DATA_NOTIF ) rsp.un.loc.data.calcLoc.locationMethod = *pData++; rsp.un.loc.data.calcLoc.qualityMeasure = *pData++; rsp.un.loc.data.calcLoc.locationAge = BUILD_UINT16( pData[0], pData[1] ); } } rsp.srcAddr = &(pInMsg->msg->srcAddr); rsp.cmdID = pInMsg->hdr.commandID; // Notify the Application pCBs->pfnLocationRsp( &rsp ); } } /********************************************************************* * @fn zclGeneral_ProcessInLocationClient * * @brief Process in the received Location Command. * * @param pInMsg - pointer to the incoming message * * @return ZStatus_t */ static ZStatus_t zclGeneral_ProcessInLocationClient( zclIncoming_t *pInMsg, zclGeneral_AppCallbacks_t *pCBs ) { uint8 *pData = pInMsg->pData; zclLocationRsp_t rsp; ZStatus_t stat = ZSuccess; osal_memset( (uint8*)&rsp, 0, sizeof( zclLocationRsp_t ) ); switch ( pInMsg->hdr.commandID ) { case COMMAND_LOCATION_DEV_CFG_RSP: if ( pCBs->pfnLocationRsp ) { rsp.un.devCfg.status = *pData++; if ( rsp.un.devCfg.status == ZCL_STATUS_SUCCESS ) { rsp.un.devCfg.data.power = BUILD_UINT16( pData[0], pData[1] ); pData += 2; rsp.un.devCfg.data.pathLossExponent = BUILD_UINT16( pData[0], pData[1] ); pData += 2; rsp.un.devCfg.data.calcPeriod = BUILD_UINT16( pData[0], pData[1] ); pData += 2; rsp.un.devCfg.data.numMeasurements = *pData++; rsp.un.devCfg.data.reportPeriod = BUILD_UINT16( pData[0], pData[1] ); rsp.srcAddr = &(pInMsg->msg->srcAddr); rsp.cmdID = pInMsg->hdr.commandID; // Notify the Application pCBs->pfnLocationRsp( &rsp ); } } break; case COMMAND_LOCATION_DATA_RSP: case COMMAND_LOCATION_DATA_NOTIF: case COMMAND_LOCATION_COMPACT_DATA_NOTIF: zclGeneral_ProcessInLocationDataRsp( pInMsg, pCBs ); break; case COMMAND_LOCATION_RSSI_PING: if ( pCBs->pfnLocationRsp ) { rsp.un.locationType = *pData; rsp.srcAddr = &(pInMsg->msg->srcAddr); rsp.cmdID = pInMsg->hdr.commandID; // Notify the Application pCBs->pfnLocationRsp( &rsp ); } break; default: stat = ZFailure; break; } return ( stat ); } #endif // ZCL_LOCATION #ifdef ZCL_SCENES /********************************************************************* * @fn zclGeneral_ScenesInitNV * * @brief Initialize the NV Scene Table Items * * @param none * * @return number of scenes */ static uint8 zclGeneral_ScenesInitNV( void ) { uint8 status; uint16 size; size = (uint16)((sizeof ( nvGenScenesHdr_t )) + ( sizeof( zclGenSceneNVItem_t ) * ZCL_GEN_MAX_SCENES )); status = osal_nv_item_init( ZCD_NV_SCENE_TABLE, size, NULL ); if ( status != ZSUCCESS ) { zclGeneral_ScenesSetDefaultNV(); } return status; } /********************************************************************* * @fn zclGeneral_ScenesSetDefaultNV * * @brief Write the defaults to NV * * @param none * * @return none */ static void zclGeneral_ScenesSetDefaultNV( void ) { nvGenScenesHdr_t hdr; // Initialize the header hdr.numRecs = 0; // Save off the header osal_nv_write( ZCD_NV_SCENE_TABLE, 0, sizeof( nvGenScenesHdr_t ), &hdr ); } /********************************************************************* * @fn zclGeneral_ScenesWriteNV * * @brief Save the Scene Table in NV * * @param none * * @return none */ static void zclGeneral_ScenesWriteNV( void ) { nvGenScenesHdr_t hdr; zclGenSceneItem_t *pLoop; zclGenSceneNVItem_t item; hdr.numRecs = 0; // Look for end of list pLoop = zclGenSceneTable; while ( pLoop ) { // Build the record item.endpoint = pLoop->endpoint; osal_memcpy( &(item.scene), &(pLoop->scene), sizeof ( zclGeneral_Scene_t ) ); // Save the record to NV osal_nv_write( ZCD_NV_SCENE_TABLE, (uint16)((sizeof( nvGenScenesHdr_t )) + (hdr.numRecs * sizeof ( zclGenSceneNVItem_t ))), sizeof ( zclGenSceneNVItem_t ), &item ); hdr.numRecs++; pLoop = pLoop->next; } // Save off the header osal_nv_write( ZCD_NV_SCENE_TABLE, 0, sizeof( nvGenScenesHdr_t ), &hdr ); } /********************************************************************* * @fn zclGeneral_ScenesRestoreFromNV * * @brief Restore the Scene table from NV * * @param none * * @return Number of entries restored */ static uint16 zclGeneral_ScenesRestoreFromNV( void ) { uint16 x; nvGenScenesHdr_t hdr; zclGenSceneNVItem_t item; uint16 numAdded = 0; if ( osal_nv_read( ZCD_NV_SCENE_TABLE, 0, sizeof(nvGenScenesHdr_t), &hdr ) == ZSuccess ) { // Read in the device list for ( x = 0; x < hdr.numRecs; x++ ) { if ( osal_nv_read( ZCD_NV_SCENE_TABLE, (uint16)(sizeof(nvGenScenesHdr_t) + (x * sizeof ( zclGenSceneNVItem_t ))), sizeof ( zclGenSceneNVItem_t ), &item ) == ZSUCCESS ) { // Add the scene if ( zclGeneral_AddScene( item.endpoint, &(item.scene) ) == ZSuccess ) { numAdded++; } } } } return ( numAdded ); } #endif // ZCL_SCENES /*************************************************************************** ****************************************************************************/