/************************************************************************************************** Filename: zcl.c Revised: $Date: 2012-01-30 10:40:08 -0800 (Mon, 30 Jan 2012) $ Revision: $Revision: 29096 $ Description: This file contains the Zigbee Cluster Library Foundation functions. Copyright 2006-2012 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_Tasks.h" #include "AF.h" #include "ZDConfig.h" #include "zcl.h" #include "zcl_general.h" #if defined ( INTER_PAN ) #include "stub_aps.h" #endif /********************************************************************* * MACROS */ /*** Frame Control ***/ #define zcl_FCType( a ) ( (a) & ZCL_FRAME_CONTROL_TYPE ) #define zcl_FCManuSpecific( a ) ( (a) & ZCL_FRAME_CONTROL_MANU_SPECIFIC ) #define zcl_FCDirection( a ) ( (a) & ZCL_FRAME_CONTROL_DIRECTION ) #define zcl_FCDisableDefaultRsp( a ) ( (a) & ZCL_FRAME_CONTROL_DISABLE_DEFAULT_RSP ) /*** Attribute Access Control ***/ #define zcl_AccessCtrlRead( a ) ( (a) & ACCESS_CONTROL_READ ) #define zcl_AccessCtrlWrite( a ) ( (a) & ACCESS_CONTROL_WRITE ) #define zcl_AccessCtrlCmd( a ) ( (a) & ACCESS_CONTROL_CMD ) #define zcl_AccessCtrlAuthRead( a ) ( (a) & ACCESS_CONTROL_AUTH_READ ) #define zcl_AccessCtrlAuthWrite( a ) ( (a) & ACCESS_CONTROL_AUTH_WRITE ) #define zclParseCmd( a, b ) zclCmdTable[(a)].pfnParseInProfile( (b) ) #define zclProcessCmd( a, b ) zclCmdTable[(a)].pfnProcessInProfile( (b) ) #define zcl_DefaultRspCmd( zclHdr ) ( zcl_ProfileCmd( (zclHdr).fc.type ) && \ (zclHdr).fc.manuSpecific == 0 && \ (zclHdr).commandID == ZCL_CMD_DEFAULT_RSP ) // Commands that have corresponding responses #define CMD_HAS_RSP( cmd ) ( (cmd) == ZCL_CMD_READ || \ (cmd) == ZCL_CMD_WRITE || \ (cmd) == ZCL_CMD_WRITE_UNDIVIDED || \ (cmd) == ZCL_CMD_CONFIG_REPORT || \ (cmd) == ZCL_CMD_READ_REPORT_CFG || \ (cmd) == ZCL_CMD_DISCOVER || \ (cmd) == ZCL_CMD_DEFAULT_RSP ) // exception /********************************************************************* * CONSTANTS */ /********************************************************************* * TYPEDEFS */ typedef struct zclLibPlugin { struct zclLibPlugin *next; uint16 startClusterID; // starting cluster ID uint16 endClusterID; // ending cluster ID zclInHdlr_t pfnIncomingHdlr; // function to handle incoming message } zclLibPlugin_t; // Attribute record list item typedef struct zclAttrRecsList { struct zclAttrRecsList *next; uint8 endpoint; // Used to link it into the endpoint descriptor zclReadWriteCB_t pfnReadWriteCB;// Read or Write attribute value callback function zclAuthorizeCB_t pfnAuthorizeCB;// Authorize Read or Write operation uint8 numAttributes; // Number of the following records CONST zclAttrRec_t *attrs; // attribute records } zclAttrRecsList; // Cluster option list item typedef struct zclClusterOptionList { struct zclClusterOptionList *next; uint8 endpoint; // Used to link it into the endpoint descriptor uint8 numOptions; // Number of the following records zclOptionRec_t *options; // option records } zclClusterOptionList; typedef void *(*zclParseInProfileCmd_t)( zclParseCmd_t *pCmd ); typedef uint8 (*zclProcessInProfileCmd_t)( zclIncoming_t *pInMsg ); typedef struct { zclParseInProfileCmd_t pfnParseInProfile; zclProcessInProfileCmd_t pfnProcessInProfile; } zclCmdItems_t; /********************************************************************* * GLOBAL VARIABLES */ uint8 zcl_TaskID; // The task Id of the Application where the unprocessed Foundation // Command/Response messages will be sent to. uint8 zcl_RegisteredMsgTaskID = TASK_NO_TASK; // The Application should register its attribute data validation function zclValidateAttrData_t zcl_ValidateAttrDataCB = NULL; // ZCL Sequence number uint8 zcl_SeqNum = 0x00; /********************************************************************* * EXTERNAL VARIABLES */ /********************************************************************* * EXTERNAL FUNCTIONS */ /********************************************************************* * LOCAL VARIABLES */ static zclLibPlugin_t *plugins; static zclAttrRecsList *attrList; static zclClusterOptionList *clusterOptionList; static uint8 zcl_TransID = 0; // This is the unique message ID (counter) static afIncomingMSGPacket_t *rawAFMsg = NULL; /********************************************************************* * LOCAL FUNCTIONS */ void zclProcessMessageMSG( afIncomingMSGPacket_t *pkt ); // Not static for ZNP build. static uint8 *zclBuildHdr( zclFrameHdr_t *hdr, uint8 *pData ); static uint8 zclCalcHdrSize( zclFrameHdr_t *hdr ); static zclLibPlugin_t *zclFindPlugin( uint16 clusterID, uint16 profileID ); static zclAttrRecsList *zclFindAttrRecsList( uint8 endpoint ); static zclOptionRec_t *zclFindClusterOption( uint8 endpoint, uint16 clusterID ); static uint8 zclGetClusterOption( uint8 endpoint, uint16 clusterID ); static void zclSetSecurityOption( uint8 endpoint, uint16 clusterID, uint8 enable ); static uint8 zcl_DeviceOperational( uint8 srcEP, uint16 clusterID, uint8 frameType, uint8 cmd, uint16 profileID ); #if defined ( ZCL_READ ) || defined ( ZCL_WRITE ) static zclReadWriteCB_t zclGetReadWriteCB( uint8 endpoint ); static zclAuthorizeCB_t zclGetAuthorizeCB( uint8 endpoint ); #endif // ZCL_READ || ZCL_WRITE #ifdef ZCL_READ static uint16 zclGetAttrDataLengthUsingCB( uint8 endpoint, uint16 clusterID, uint16 attrId ); static ZStatus_t zclReadAttrDataUsingCB( uint8 endpoint, uint16 clusterId, uint16 attrId, uint8 *pAttrData, uint16 *pDataLen ); static ZStatus_t zclAuthorizeRead( uint8 endpoint, afAddrType_t *srcAddr, zclAttrRec_t *pAttr ); static void *zclParseInReadRspCmd( zclParseCmd_t *pCmd ); static uint8 zclProcessInReadCmd( zclIncoming_t *pInMsg ); #endif // ZCL_READ #ifdef ZCL_WRITE static ZStatus_t zclWriteAttrData( uint8 endpoint, afAddrType_t *srcAddr, zclAttrRec_t *pAttr, zclWriteRec_t *pWriteRec ); static ZStatus_t zclWriteAttrDataUsingCB( uint8 endpoint, afAddrType_t *srcAddr, zclAttrRec_t *pAttr, uint8 *pAttrData ); static ZStatus_t zclAuthorizeWrite( uint8 endpoint, afAddrType_t *srcAddr, zclAttrRec_t *pAttr ); static void *zclParseInWriteRspCmd( zclParseCmd_t *pCmd ); static uint8 zclProcessInWriteCmd( zclIncoming_t *pInMsg ); static uint8 zclProcessInWriteUndividedCmd( zclIncoming_t *pInMsg ); #endif // ZCL_WRITE #ifdef ZCL_REPORT static void *zclParseInConfigReportRspCmd( zclParseCmd_t *pCmd ); static void *zclParseInReadReportCfgRspCmd( zclParseCmd_t *pCmd ); #endif // ZCL_REPORT static void *zclParseInDefaultRspCmd( zclParseCmd_t *pCmd ); #ifdef ZCL_DISCOVER static uint8 zclFindNextAttrRec( uint8 endpoint, uint16 clusterID, uint16 *attrId, zclAttrRec_t *pAttr ); static void *zclParseInDiscRspCmd( zclParseCmd_t *pCmd ); static uint8 zclProcessInDiscCmd( zclIncoming_t *pInMsg ); #endif // ZCL_DISCOVER static uint8 zclSendMsg( zclIncoming_t *pInMsg ); /********************************************************************* * Parse Profile Command Function Table */ static CONST zclCmdItems_t zclCmdTable[] = { #ifdef ZCL_READ /* ZCL_CMD_READ */ { zclParseInReadCmd, zclProcessInReadCmd }, /* ZCL_CMD_READ_RSP */ { zclParseInReadRspCmd, zclSendMsg }, #else /* ZCL_CMD_READ */ { NULL, NULL }, /* ZCL_CMD_READ_RSP */ { NULL, NULL }, #endif // ZCL_READ #ifdef ZCL_WRITE /* ZCL_CMD_WRITE */ { zclParseInWriteCmd, zclProcessInWriteCmd }, /* ZCL_CMD_WRITE_UNDIVIDED */ { zclParseInWriteCmd, zclProcessInWriteUndividedCmd }, /* ZCL_CMD_WRITE_RSP */ { zclParseInWriteRspCmd, zclSendMsg }, /* ZCL_CMD_WRITE_NO_RSP */ { zclParseInWriteCmd, zclProcessInWriteCmd }, #else /* ZCL_CMD_WRITE */ { NULL, NULL }, /* ZCL_CMD_WRITE_UNDIVIDED */ { NULL, NULL }, /* ZCL_CMD_WRITE_RSP */ { NULL, NULL }, /* ZCL_CMD_WRITE_NO_RSP */ { NULL, NULL }, #endif // ZCL_WRITE #ifdef ZCL_REPORT /* ZCL_CMD_CONFIG_REPORT */ { zclParseInConfigReportCmd, zclSendMsg }, /* ZCL_CMD_CONFIG_REPORT_RSP */ { zclParseInConfigReportRspCmd, zclSendMsg }, /* ZCL_CMD_READ_REPORT_CFG */ { zclParseInReadReportCfgCmd, zclSendMsg }, /* ZCL_CMD_READ_REPORT_CFG_RSP */ { zclParseInReadReportCfgRspCmd, zclSendMsg }, /* ZCL_CMD_REPORT */ { zclParseInReportCmd, zclSendMsg }, #else /* ZCL_CMD_CONFIG_REPORT */ { NULL, NULL }, /* ZCL_CMD_CONFIG_REPORT_RSP */ { NULL, NULL }, /* ZCL_CMD_READ_REPORT_CFG */ { NULL, NULL }, /* ZCL_CMD_READ_REPORT_CFG_RSP */ { NULL, NULL }, /* ZCL_CMD_REPORT */ { NULL, NULL }, #endif // ZCL_REPORT /* ZCL_CMD_DEFAULT_RSP */ { zclParseInDefaultRspCmd, zclSendMsg }, #ifdef ZCL_DISCOVER /* ZCL_CMD_DISCOVER */ { zclParseInDiscCmd, zclProcessInDiscCmd }, /* ZCL_CMD_DISCOVER_RSP */ { zclParseInDiscRspCmd, zclSendMsg } #else /* ZCL_CMD_DISCOVER */ { NULL, NULL }, /* ZCL_CMD_DISCOVER_RSP */ { NULL, NULL } #endif // ZCL_DISCOVER }; /********************************************************************* * PUBLIC FUNCTIONS *********************************************************************/ /********************************************************************* * @fn zcl_Init * * @brief Initialization function for the zcl layer. * * @param task_id - ZCL task id * * @return none */ void zcl_Init( uint8 task_id ) { zcl_TaskID = task_id; plugins = (zclLibPlugin_t *)NULL; attrList = (zclAttrRecsList *)NULL; clusterOptionList = (zclClusterOptionList *)NULL; } /********************************************************************* * @fn zcl_event_loop * * @brief Event Loop Processor for zcl. * * @param task_id - task id * @param events - event bitmap * * @return unprocessed events */ uint16 zcl_event_loop( uint8 task_id, uint16 events ) { uint8 *msgPtr; (void)task_id; // Intentionally unreferenced parameter if ( events & SYS_EVENT_MSG ) { msgPtr = osal_msg_receive( zcl_TaskID ); while ( msgPtr != NULL ) { uint8 dealloc = TRUE; if ( *msgPtr == AF_INCOMING_MSG_CMD ) { rawAFMsg = (afIncomingMSGPacket_t *)msgPtr; zclProcessMessageMSG( rawAFMsg ); rawAFMsg = NULL; } else if ( zcl_RegisteredMsgTaskID != TASK_NO_TASK ) { // send it to another task to process. osal_msg_send( zcl_RegisteredMsgTaskID, msgPtr ); dealloc = FALSE; } // Release the memory if ( dealloc ) { osal_msg_deallocate( msgPtr ); } // Next msgPtr = osal_msg_receive( zcl_TaskID ); } // return unprocessed events return (events ^ SYS_EVENT_MSG); } // Discard unknown events return 0; } /********************************************************************* * @fn zcl_getRawAFMsg * * @brief Call to get original unprocessed AF message * (not parsed by ZCL). * * NOTE: This function can only be called during a ZCL callback function * and the calling function must NOT change any data in the message. * * @param none * * @return pointer to original AF message, NULL if not processing * AF message. */ afIncomingMSGPacket_t *zcl_getRawAFMsg( void ) { return ( rawAFMsg ); } /********************************************************************* * @fn zcl_registerPlugin * * @brief Add a Cluster Library handler * * @param startClusterID - starting cluster ID * @param endClusterID - ending cluster ID * @param pfnHdlr - function pointer to incoming message handler * * @return ZSuccess if OK */ ZStatus_t zcl_registerPlugin( uint16 startClusterID, uint16 endClusterID, zclInHdlr_t pfnIncomingHdlr ) { zclLibPlugin_t *pNewItem; zclLibPlugin_t *pLoop; // Fill in the new profile list pNewItem = osal_mem_alloc( sizeof( zclLibPlugin_t ) ); if ( pNewItem == NULL ) { return (ZMemError); } // Fill in the plugin record. pNewItem->next = (zclLibPlugin_t *)NULL; pNewItem->startClusterID = startClusterID; pNewItem->endClusterID = endClusterID; pNewItem->pfnIncomingHdlr = pfnIncomingHdlr; // Find spot in list if ( plugins == NULL ) { plugins = pNewItem; } else { // Look for end of list pLoop = plugins; while ( pLoop->next != NULL ) { pLoop = pLoop->next; } // Put new item at end of list pLoop->next = pNewItem; } return ( ZSuccess ); } /********************************************************************* * @fn zcl_registerAttrList * * @brief Register an Attribute List with ZCL Foundation * * @param endpoint - endpoint the attribute list belongs to * @param numAttr - number of attributes in list * @param newAttrList - array of Attribute records. * NOTE: THE ATTRIBUTE IDs (FOR A CLUSTER) MUST BE IN * ASCENDING ORDER. OTHERWISE, THE DISCOVERY RESPONSE * COMMAND WILL NOT HAVE THE RIGHT ATTRIBUTE INFO * * @return ZSuccess if OK */ ZStatus_t zcl_registerAttrList( uint8 endpoint, uint8 numAttr, CONST zclAttrRec_t newAttrList[] ) { zclAttrRecsList *pNewItem; zclAttrRecsList *pLoop; // Fill in the new profile list pNewItem = osal_mem_alloc( sizeof( zclAttrRecsList ) ); if ( pNewItem == NULL ) { return (ZMemError); } pNewItem->next = (zclAttrRecsList *)NULL; pNewItem->endpoint = endpoint; pNewItem->pfnReadWriteCB = NULL; pNewItem->numAttributes = numAttr; pNewItem->attrs = newAttrList; // Find spot in list if ( attrList == NULL ) { attrList = pNewItem; } else { // Look for end of list pLoop = attrList; while ( pLoop->next != NULL ) { pLoop = pLoop->next; } // Put new item at end of list pLoop->next = pNewItem; } return ( ZSuccess ); } /********************************************************************* * @fn zcl_registerClusterOptionList * * @brief Register a Cluster Option List with ZCL Foundation * * @param endpoint - endpoint the option list belongs to * @param numOption - number of options in list * @param optionList - array of cluster option records. * * NOTE: This API should be called to enable 'Application * Link Key' security and/or 'APS ACK' for a specific * Cluster. The 'Application Link Key' is discarded * if security isn't enabled on the device. * The default behavior is 'Network Key' when security * is enabled and no 'APS ACK' for the ZCL messages. * * @return ZSuccess if OK */ ZStatus_t zcl_registerClusterOptionList( uint8 endpoint, uint8 numOption, zclOptionRec_t optionList[] ) { zclClusterOptionList *pNewItem; zclClusterOptionList *pLoop; // Fill in the new profile list pNewItem = osal_mem_alloc( sizeof( zclClusterOptionList ) ); if ( pNewItem == NULL ) { return (ZMemError); } pNewItem->next = (zclClusterOptionList *)NULL; pNewItem->endpoint = endpoint; pNewItem->numOptions = numOption; pNewItem->options = optionList; // Find spot in list if ( clusterOptionList == NULL ) { clusterOptionList = pNewItem; } else { // Look for end of list pLoop = clusterOptionList; while ( pLoop->next != NULL ) { pLoop = pLoop->next; } // Put new item at end of list pLoop->next = pNewItem; } return ( ZSuccess ); } /********************************************************************* * @fn zcl_registerValidateAttrData * * @brief Add a validation function for attribute data * * @param pfnValidateAttrData - function pointer to validate routine * * @return ZSuccess if OK */ ZStatus_t zcl_registerValidateAttrData( zclValidateAttrData_t pfnValidateAttrData ) { zcl_ValidateAttrDataCB = pfnValidateAttrData; return ( ZSuccess ); } /********************************************************************* * @fn zcl_registerReadWriteCB * * @brief Register the application's callback function to read/write * attribute data, and authorize read/write operation. * * Note: The pfnReadWriteCB callback function is only required * when the attribute data format is unknown to ZCL. The * callback function gets called when the pointer 'dataPtr' * to the attribute value is NULL in the attribute database * registered with the ZCL. * * Note: The pfnAuthorizeCB callback function is only required * when the Read/Write operation on an attribute requires * authorization (i.e., attributes with ACCESS_CONTROL_AUTH_READ * or ACCESS_CONTROL_AUTH_WRITE access permissions). * * @param endpoint - application's endpoint * @param pfnReadWriteCB - function pointer to read/write routine * @param pfnAuthorizeCB - function pointer to authorize read/write operation * * @return ZSuccess if successful. ZFailure, otherwise. */ ZStatus_t zcl_registerReadWriteCB( uint8 endpoint, zclReadWriteCB_t pfnReadWriteCB, zclAuthorizeCB_t pfnAuthorizeCB ) { zclAttrRecsList *pRec = zclFindAttrRecsList( endpoint ); if ( pRec != NULL ) { pRec->pfnReadWriteCB = pfnReadWriteCB; pRec->pfnAuthorizeCB = pfnAuthorizeCB; return ( ZSuccess ); } return ( ZFailure ); } /********************************************************************* * @fn zcl_registerForMsg * * @brief The ZCL is setup to send all incoming Foundation Command/Response * messages that aren't processed to one task (if a task is * registered). * * @param taskId - task Id of the Application where commands will be sent to * * @return TRUE if task registeration successful, FALSE otherwise *********************************************************************/ uint8 zcl_registerForMsg( uint8 taskId ) { // Allow only the first task if ( zcl_RegisteredMsgTaskID == TASK_NO_TASK ) { zcl_RegisteredMsgTaskID = taskId; return ( true ); } return ( false ); } /********************************************************************* * @fn zcl_DeviceOperational * * @brief Used to see whether or not the device can send or respond * to application level commands. * * @param srcEP - source endpoint * @param clusterID - cluster ID * @param frameType - command type * @param cmd - command ID * * @return TRUE if device is operational, FALSE otherwise */ static uint8 zcl_DeviceOperational( uint8 srcEP, uint16 clusterID, uint8 frameType, uint8 cmd, uint16 profileID ) { zclAttrRec_t attrRec; uint8 deviceEnabled = DEVICE_ENABLED; // default value (void)profileID; // Intentionally unreferenced parameter // If the device is Disabled (DeviceEnabled attribute is set to Disabled), it // cannot send or respond to application level commands, other than commands // to read or write attributes. Note that the Identify cluster cannot be // disabled, and remains functional regardless of this setting. if ( zcl_ProfileCmd( frameType ) && cmd <= ZCL_CMD_WRITE_NO_RSP ) { return ( TRUE ); } if ( clusterID == ZCL_CLUSTER_ID_GEN_IDENTIFY ) { return ( TRUE ); } // Is device enabled? if ( zclFindAttrRec( srcEP, ZCL_CLUSTER_ID_GEN_BASIC, ATTRID_BASIC_DEVICE_ENABLED, &attrRec ) ) { zclReadAttrData( &deviceEnabled, &attrRec, NULL ); } return ( deviceEnabled == DEVICE_ENABLED ? TRUE : FALSE ); } /********************************************************************* * @fn zcl_SendCommand * * @brief Used to send Profile and Cluster Specific Command messages. * * NOTE: The calling application is responsible for incrementing * the Sequence Number. * * @param srcEp - source endpoint * @param destAddr - destination address * @param clusterID - cluster ID * @param cmd - command ID * @param specific - whether the command is Cluster Specific * @param direction - client/server direction of the command * @param disableDefaultRsp - disable Default Response command * @param manuCode - manufacturer code for proprietary extensions to a profile * @param seqNumber - identification number for the transaction * @param cmdFormatLen - length of the command to be sent * @param cmdFormat - command to be sent * * @return ZSuccess if OK */ ZStatus_t zcl_SendCommand( uint8 srcEP, afAddrType_t *destAddr, uint16 clusterID, uint8 cmd, uint8 specific, uint8 direction, uint8 disableDefaultRsp, uint16 manuCode, uint8 seqNum, uint16 cmdFormatLen, uint8 *cmdFormat ) { endPointDesc_t *epDesc; zclFrameHdr_t hdr; uint8 *msgBuf; uint16 msgLen; uint8 *pBuf; uint8 options; ZStatus_t status; epDesc = afFindEndPointDesc( srcEP ); if ( epDesc == NULL ) { return ( ZInvalidParameter ); // EMBEDDED RETURN } #if defined ( INTER_PAN ) if ( StubAPS_InterPan( destAddr->panId, destAddr->endPoint ) ) { options = AF_TX_OPTIONS_NONE; } else #endif { options = zclGetClusterOption( srcEP, clusterID ); } osal_memset( &hdr, 0, sizeof( zclFrameHdr_t ) ); // Not Profile wide command (like READ, WRITE) if ( specific ) { hdr.fc.type = ZCL_FRAME_TYPE_SPECIFIC_CMD; } else { hdr.fc.type = ZCL_FRAME_TYPE_PROFILE_CMD; } if ( ( epDesc->simpleDesc == NULL ) || ( zcl_DeviceOperational( srcEP, clusterID, hdr.fc.type, cmd, epDesc->simpleDesc->AppProfId ) == FALSE ) ) { return ( ZFailure ); // EMBEDDED RETURN } // Fill in the Maufacturer Code if ( manuCode != 0 ) { hdr.fc.manuSpecific = 1; hdr.manuCode = manuCode; } // Set the Command Direction if ( direction ) { hdr.fc.direction = ZCL_FRAME_SERVER_CLIENT_DIR; } else { hdr.fc.direction = ZCL_FRAME_CLIENT_SERVER_DIR; } // Set the Disable Default Response field if ( disableDefaultRsp ) { hdr.fc.disableDefaultRsp = 1; } else { hdr.fc.disableDefaultRsp = 0; } // Fill in the Transaction Sequence Number hdr.transSeqNum = seqNum; // Fill in the command hdr.commandID = cmd; // calculate the needed buffer size msgLen = zclCalcHdrSize( &hdr ); msgLen += cmdFormatLen; // Allocate the buffer needed msgBuf = osal_mem_alloc( msgLen ); if ( msgBuf != NULL ) { // Fill in the ZCL Header pBuf = zclBuildHdr( &hdr, msgBuf ); // Fill in the command frame osal_memcpy( pBuf, cmdFormat, cmdFormatLen ); status = AF_DataRequest( destAddr, epDesc, clusterID, msgLen, msgBuf, &zcl_TransID, options, AF_DEFAULT_RADIUS ); osal_mem_free ( msgBuf ); } else { status = ZMemError; } return ( status ); } #ifdef ZCL_READ /********************************************************************* * @fn zcl_SendRead * * @brief Send a Read command * * @param srcEP - Application's endpoint * @param dstAddr - destination address * @param clusterID - cluster ID * @param readCmd - read command to be sent * @param direction - direction of the command * @param seqNum - transaction sequence number * * @return ZSuccess if OK */ ZStatus_t zcl_SendRead( uint8 srcEP, afAddrType_t *dstAddr, uint16 clusterID, zclReadCmd_t *readCmd, uint8 direction, uint8 disableDefaultRsp, uint8 seqNum) { uint16 dataLen; uint8 *buf; uint8 *pBuf; ZStatus_t status; dataLen = readCmd->numAttr * 2; // Attribute ID buf = osal_mem_alloc( dataLen ); if ( buf != NULL ) { uint8 i; // Load the buffer - serially pBuf = buf; for (i = 0; i < readCmd->numAttr; i++) { *pBuf++ = LO_UINT16( readCmd->attrID[i] ); *pBuf++ = HI_UINT16( readCmd->attrID[i] ); } status = zcl_SendCommand( srcEP, dstAddr, clusterID, ZCL_CMD_READ, FALSE, direction, disableDefaultRsp, 0, seqNum, dataLen, buf ); osal_mem_free( buf ); } else { status = ZMemError; } return ( status ); } /********************************************************************* * @fn zcl_SendReadRsp * * @brief Send a Read Response command. * * @param srcEP - Application's endpoint * @param dstAddr - destination address * @param clusterID - cluster ID * @param readRspCmd - read response command to be sent * @param direction - direction of the command * @param seqNum - transaction sequence number * * @return ZSuccess if OK */ ZStatus_t zcl_SendReadRsp( uint8 srcEP, afAddrType_t *dstAddr, uint16 clusterID, zclReadRspCmd_t *readRspCmd, uint8 direction, uint8 disableDefaultRsp, uint8 seqNum ) { uint8 *buf; uint16 len = 0; ZStatus_t status; // calculate the size of the command for ( uint8 i = 0; i < readRspCmd->numAttr; i++ ) { zclReadRspStatus_t *statusRec = &(readRspCmd->attrList[i]); len += 2 + 1; // Attribute ID + Status if ( statusRec->status == ZCL_STATUS_SUCCESS ) { len++; // Attribute Data Type length // Attribute Data length if ( statusRec->data != NULL ) { len += zclGetAttrDataLength( statusRec->dataType, statusRec->data ); } else { len += zclGetAttrDataLengthUsingCB( srcEP, clusterID, statusRec->attrID ); } } } buf = osal_mem_alloc( len ); if ( buf != NULL ) { // Load the buffer - serially uint8 *pBuf = buf; for ( uint8 i = 0; i < readRspCmd->numAttr; i++ ) { zclReadRspStatus_t *statusRec = &(readRspCmd->attrList[i]); *pBuf++ = LO_UINT16( statusRec->attrID ); *pBuf++ = HI_UINT16( statusRec->attrID ); *pBuf++ = statusRec->status; if ( statusRec->status == ZCL_STATUS_SUCCESS ) { *pBuf++ = statusRec->dataType; if ( statusRec->data != NULL ) { // Copy attribute data to the buffer to be sent out pBuf = zclSerializeData( statusRec->dataType, statusRec->data, pBuf ); } else { uint16 dataLen; // Read attribute data directly into the buffer to be sent out zclReadAttrDataUsingCB( srcEP, clusterID, statusRec->attrID, pBuf, &dataLen ); pBuf += dataLen; } } } // for loop status = zcl_SendCommand( srcEP, dstAddr, clusterID, ZCL_CMD_READ_RSP, FALSE, direction, disableDefaultRsp, 0, seqNum, len, buf ); osal_mem_free( buf ); } else { status = ZMemError; } return ( status ); } #endif // ZCL_READ #ifdef ZCL_WRITE /********************************************************************* * @fn sendWriteRequest * * @brief Send a Write command * * @param dstAddr - destination address * @param clusterID - cluster ID * @param writeCmd - write command to be sent * @param cmd - ZCL_CMD_WRITE, ZCL_CMD_WRITE_UNDIVIDED or ZCL_CMD_WRITE_NO_RSP * @param direction - direction of the command * @param seqNum - transaction sequence number * * @return ZSuccess if OK */ ZStatus_t zcl_SendWriteRequest( uint8 srcEP, afAddrType_t *dstAddr, uint16 clusterID, zclWriteCmd_t *writeCmd, uint8 cmd, uint8 direction, uint8 disableDefaultRsp, uint8 seqNum ) { uint8 *buf; uint16 dataLen = 0; ZStatus_t status; for ( uint8 i = 0; i < writeCmd->numAttr; i++ ) { zclWriteRec_t *statusRec = &(writeCmd->attrList[i]); dataLen += 2 + 1; // Attribute ID + Attribute Type // Attribute Data dataLen += zclGetAttrDataLength( statusRec->dataType, statusRec->attrData ); } buf = osal_mem_alloc( dataLen ); if ( buf != NULL ) { // Load the buffer - serially uint8 *pBuf = buf; for ( uint8 i = 0; i < writeCmd->numAttr; i++ ) { zclWriteRec_t *statusRec = &(writeCmd->attrList[i]); *pBuf++ = LO_UINT16( statusRec->attrID ); *pBuf++ = HI_UINT16( statusRec->attrID ); *pBuf++ = statusRec->dataType; pBuf = zclSerializeData( statusRec->dataType, statusRec->attrData, pBuf ); } status = zcl_SendCommand( srcEP, dstAddr, clusterID, cmd, FALSE, direction, disableDefaultRsp, 0, seqNum, dataLen, buf ); osal_mem_free( buf ); } else { status = ZMemError; } return ( status); } /********************************************************************* * @fn zcl_SendWriteRsp * * @brief Send a Write Response command * * @param dstAddr - destination address * @param clusterID - cluster ID * @param wrtieRspCmd - write response command to be sent * @param direction - direction of the command * @param seqNum - transaction sequence number * * @return ZSuccess if OK */ ZStatus_t zcl_SendWriteRsp( uint8 srcEP, afAddrType_t *dstAddr, uint16 clusterID, zclWriteRspCmd_t *writeRspCmd, uint8 direction, uint8 disableDefaultRsp, uint8 seqNum ) { uint16 dataLen; uint8 *buf; ZStatus_t status; dataLen = writeRspCmd->numAttr * ( 1 + 2 ); // status + attribute id buf = osal_mem_alloc( dataLen ); if ( buf != NULL ) { // Load the buffer - serially uint8 *pBuf = buf; for ( uint8 i = 0; i < writeRspCmd->numAttr; i++ ) { *pBuf++ = writeRspCmd->attrList[i].status; *pBuf++ = LO_UINT16( writeRspCmd->attrList[i].attrID ); *pBuf++ = HI_UINT16( writeRspCmd->attrList[i].attrID ); } // If there's only a single status record and its status field is set to // SUCCESS then omit the attribute ID field. if ( writeRspCmd->numAttr == 1 && writeRspCmd->attrList[0].status == ZCL_STATUS_SUCCESS ) { dataLen = 1; } status = zcl_SendCommand( srcEP, dstAddr, clusterID, ZCL_CMD_WRITE_RSP, FALSE, direction, disableDefaultRsp, 0, seqNum, dataLen, buf ); osal_mem_free( buf ); } else { status = ZMemError; } return ( status ); } #endif // ZCL_WRITE #ifdef ZCL_REPORT /********************************************************************* * @fn zcl_SendConfigReportCmd * * @brief Send a Configure Reporting command * * @param dstAddr - destination address * @param clusterID - cluster ID * @param cfgReportCmd - configure reporting command to be sent * @param direction - direction of the command * @param seqNum - transaction sequence number * * @return ZSuccess if OK */ ZStatus_t zcl_SendConfigReportCmd( uint8 srcEP, afAddrType_t *dstAddr, uint16 clusterID, zclCfgReportCmd_t *cfgReportCmd, uint8 direction, uint8 disableDefaultRsp, uint8 seqNum ) { uint8 *buf; uint16 dataLen = 0; ZStatus_t status; // Find out the data length for ( uint8 i = 0; i < cfgReportCmd->numAttr; i++ ) { zclCfgReportRec_t *reportRec = &(cfgReportCmd->attrList[i]); dataLen += 1 + 2; // Direction + Attribute ID if ( reportRec->direction == ZCL_SEND_ATTR_REPORTS ) { dataLen += 1 + 2 + 2; // Data Type + Min + Max Reporting Intervals // Find out the size of the Reportable Change field (for Analog data types) if ( zclAnalogDataType( reportRec->dataType ) ) { dataLen += zclGetDataTypeLength( reportRec->dataType ); } } else { dataLen += 2; // Timeout Period } } buf = osal_mem_alloc( dataLen ); if ( buf != NULL ) { // Load the buffer - serially uint8 *pBuf = buf; for ( uint8 i = 0; i < cfgReportCmd->numAttr; i++ ) { zclCfgReportRec_t *reportRec = &(cfgReportCmd->attrList[i]); *pBuf++ = reportRec->direction; *pBuf++ = LO_UINT16( reportRec->attrID ); *pBuf++ = HI_UINT16( reportRec->attrID ); if ( reportRec->direction == ZCL_SEND_ATTR_REPORTS ) { *pBuf++ = reportRec->dataType; *pBuf++ = LO_UINT16( reportRec->minReportInt ); *pBuf++ = HI_UINT16( reportRec->minReportInt ); *pBuf++ = LO_UINT16( reportRec->maxReportInt ); *pBuf++ = HI_UINT16( reportRec->maxReportInt ); if ( zclAnalogDataType( reportRec->dataType ) ) { pBuf = zclSerializeData( reportRec->dataType, reportRec->reportableChange, pBuf ); } } else { *pBuf++ = LO_UINT16( reportRec->timeoutPeriod ); *pBuf++ = HI_UINT16( reportRec->timeoutPeriod ); } } // for loop status = zcl_SendCommand( srcEP, dstAddr, clusterID, ZCL_CMD_CONFIG_REPORT, FALSE, direction, disableDefaultRsp, 0, seqNum, dataLen, buf ); osal_mem_free( buf ); } else { status = ZMemError; } return ( status ); } /********************************************************************* * @fn zcl_SendConfigReportRspCmd * * @brief Send a Configure Reporting Response command * * @param dstAddr - destination address * @param clusterID - cluster ID * @param cfgReportRspCmd - configure reporting response command to be sent * @param direction - direction of the command * @param seqNum - transaction sequence number * * @return ZSuccess if OK */ ZStatus_t zcl_SendConfigReportRspCmd( uint8 srcEP, afAddrType_t *dstAddr, uint16 clusterID, zclCfgReportRspCmd_t *cfgReportRspCmd, uint8 direction, uint8 disableDefaultRsp, uint8 seqNum ) { uint16 dataLen; uint8 *buf; ZStatus_t status; // Atrribute list (Status, Direction and Attribute ID) dataLen = cfgReportRspCmd->numAttr * ( 1 + 1 + 2 ); buf = osal_mem_alloc( dataLen ); if ( buf != NULL ) { // Load the buffer - serially uint8 *pBuf = buf; for ( uint8 i = 0; i < cfgReportRspCmd->numAttr; i++ ) { *pBuf++ = cfgReportRspCmd->attrList[i].status; *pBuf++ = cfgReportRspCmd->attrList[i].direction; *pBuf++ = LO_UINT16( cfgReportRspCmd->attrList[i].attrID ); *pBuf++ = HI_UINT16( cfgReportRspCmd->attrList[i].attrID ); } // If there's only a single status record and its status field is set to // SUCCESS then omit the attribute ID field. if ( cfgReportRspCmd->numAttr == 1 && cfgReportRspCmd->attrList[0].status == ZCL_STATUS_SUCCESS ) { dataLen = 1; } status = zcl_SendCommand( srcEP, dstAddr, clusterID, ZCL_CMD_CONFIG_REPORT_RSP, FALSE, direction, disableDefaultRsp, 0, seqNum, dataLen, buf ); osal_mem_free( buf ); } else { status = ZMemError; } return ( status ); } /********************************************************************* * @fn zcl_SendReadReportCfgCmd * * @brief Send a Read Reporting Configuration command * * @param dstAddr - destination address * @param clusterID - cluster ID * @param readReportCfgCmd - read reporting configuration command to be sent * @param direction - direction of the command * @param seqNum - transaction sequence number * * @return ZSuccess if OK */ ZStatus_t zcl_SendReadReportCfgCmd( uint8 srcEP, afAddrType_t *dstAddr, uint16 clusterID, zclReadReportCfgCmd_t *readReportCfgCmd, uint8 direction, uint8 disableDefaultRsp, uint8 seqNum ) { uint16 dataLen; uint8 *buf; ZStatus_t status; dataLen = readReportCfgCmd->numAttr * ( 1 + 2 ); // Direction + Atrribute ID buf = osal_mem_alloc( dataLen ); if ( buf != NULL ) { // Load the buffer - serially uint8 *pBuf = buf; for ( uint8 i = 0; i < readReportCfgCmd->numAttr; i++ ) { *pBuf++ = readReportCfgCmd->attrList[i].direction; *pBuf++ = LO_UINT16( readReportCfgCmd->attrList[i].attrID ); *pBuf++ = HI_UINT16( readReportCfgCmd->attrList[i].attrID ); } status = zcl_SendCommand( srcEP, dstAddr, clusterID, ZCL_CMD_READ_REPORT_CFG, FALSE, direction, disableDefaultRsp, 0, seqNum, dataLen, buf ); osal_mem_free( buf ); } else { status = ZMemError; } return ( status ); } /********************************************************************* * @fn zcl_SendReadReportCfgRspCmd * * @brief Send a Read Reporting Configuration Response command * * @param dstAddr - destination address * @param clusterID - cluster ID * @param readReportCfgRspCmd - read reporting configuration response command to be sent * @param direction - direction of the command * @param seqNum - transaction sequence number * * @return ZSuccess if OK */ ZStatus_t zcl_SendReadReportCfgRspCmd( uint8 srcEP, afAddrType_t *dstAddr, uint16 clusterID, zclReadReportCfgRspCmd_t *readReportCfgRspCmd, uint8 direction, uint8 disableDefaultRsp, uint8 seqNum ) { uint8 *buf; uint16 dataLen = 0; ZStatus_t status; // Find out the data length for ( uint8 i = 0; i < readReportCfgRspCmd->numAttr; i++ ) { zclReportCfgRspRec_t *reportRspRec = &(readReportCfgRspCmd->attrList[i]); dataLen += 1 + 1 + 2 ; // Status, Direction and Atrribute ID if ( reportRspRec->status == ZCL_STATUS_SUCCESS ) { if ( reportRspRec->direction == ZCL_SEND_ATTR_REPORTS ) { dataLen += 1 + 2 + 2; // Data Type + Min + Max Reporting Intervals // Find out the size of the Reportable Change field (for Analog data types) if ( zclAnalogDataType( reportRspRec->dataType ) ) { dataLen += zclGetDataTypeLength( reportRspRec->dataType ); } } else { dataLen += 2; // Timeout Period } } } buf = osal_mem_alloc( dataLen ); if ( buf != NULL ) { // Load the buffer - serially uint8 *pBuf = buf; for ( uint8 i = 0; i < readReportCfgRspCmd->numAttr; i++ ) { zclReportCfgRspRec_t *reportRspRec = &(readReportCfgRspCmd->attrList[i]); *pBuf++ = reportRspRec->status; *pBuf++ = reportRspRec->direction; *pBuf++ = LO_UINT16( reportRspRec->attrID ); *pBuf++ = HI_UINT16( reportRspRec->attrID ); if ( reportRspRec->status == ZCL_STATUS_SUCCESS ) { if ( reportRspRec->direction == ZCL_SEND_ATTR_REPORTS ) { *pBuf++ = reportRspRec->dataType; *pBuf++ = LO_UINT16( reportRspRec->minReportInt ); *pBuf++ = HI_UINT16( reportRspRec->minReportInt ); *pBuf++ = LO_UINT16( reportRspRec->maxReportInt ); *pBuf++ = HI_UINT16( reportRspRec->maxReportInt ); if ( zclAnalogDataType( reportRspRec->dataType ) ) { pBuf = zclSerializeData( reportRspRec->dataType, reportRspRec->reportableChange, pBuf ); } } else { *pBuf++ = LO_UINT16( reportRspRec->timeoutPeriod ); *pBuf++ = HI_UINT16( reportRspRec->timeoutPeriod ); } } } status = zcl_SendCommand( srcEP, dstAddr, clusterID, ZCL_CMD_READ_REPORT_CFG_RSP, FALSE, direction, disableDefaultRsp, 0, seqNum, dataLen, buf ); osal_mem_free( buf ); } else { status = ZMemError; } return ( status ); } /********************************************************************* * @fn zcl_SendReportCmd * * @brief Send a Report command * * @param dstAddr - destination address * @param clusterID - cluster ID * @param reportCmd - report command to be sent * @param direction - direction of the command * @param seqNum - transaction sequence number * * @return ZSuccess if OK */ ZStatus_t zcl_SendReportCmd( uint8 srcEP, afAddrType_t *dstAddr, uint16 clusterID, zclReportCmd_t *reportCmd, uint8 direction, uint8 disableDefaultRsp, uint8 seqNum ) { uint16 dataLen = 0; uint8 *buf; ZStatus_t status; // calculate the size of the command for ( uint8 i = 0; i < reportCmd->numAttr; i++ ) { zclReport_t *reportRec = &(reportCmd->attrList[i]); dataLen += 2 + 1; // Attribute ID + data type // Attribute Data dataLen += zclGetAttrDataLength( reportRec->dataType, reportRec->attrData ); } buf = osal_mem_alloc( dataLen ); if ( buf != NULL ) { // Load the buffer - serially uint8 *pBuf = buf; for ( uint8 i = 0; i < reportCmd->numAttr; i++ ) { zclReport_t *reportRec = &(reportCmd->attrList[i]); *pBuf++ = LO_UINT16( reportRec->attrID ); *pBuf++ = HI_UINT16( reportRec->attrID ); *pBuf++ = reportRec->dataType; pBuf = zclSerializeData( reportRec->dataType, reportRec->attrData, pBuf ); } status = zcl_SendCommand( srcEP, dstAddr, clusterID, ZCL_CMD_REPORT, FALSE, direction, disableDefaultRsp, 0, seqNum, dataLen, buf ); osal_mem_free( buf ); } else { status = ZMemError; } return ( status ); } #endif // ZCL_REPORT /********************************************************************* * @fn zcl_SendDefaultRspCmd * * @brief Send a Default Response command * * Note: The manufacturer code field should be set if this * command is being sent in response to a manufacturer specific * command. * * @param dstAddr - destination address * @param clusterID - cluster ID * @param defaultRspCmd - default response command to be sent * @param direction - direction of the command * @param manuCode - manufacturer code for proprietary extensions to a profile * @param seqNum - transaction sequence number * * @return ZSuccess if OK */ ZStatus_t zcl_SendDefaultRspCmd( uint8 srcEP, afAddrType_t *dstAddr, uint16 clusterID, zclDefaultRspCmd_t *defaultRspCmd, uint8 direction, uint8 disableDefaultRsp, uint16 manuCode, uint8 seqNum ) { uint8 buf[2]; // Command ID and Status; // Load the buffer - serially buf[0] = defaultRspCmd->commandID; buf[1] = defaultRspCmd->statusCode; return ( zcl_SendCommand( srcEP, dstAddr, clusterID, ZCL_CMD_DEFAULT_RSP, FALSE, direction, disableDefaultRsp, manuCode, seqNum, 2, buf ) ); } #ifdef ZCL_DISCOVER /********************************************************************* * @fn zcl_SendDiscoverCmd * * @brief Send a Discover command * * @param dstAddr - destination address * @param clusterID - cluster ID * @param discoverCmd - discover command to be sent * @param direction - direction of the command * @param seqNum - transaction sequence number * * @return ZSuccess if OK */ ZStatus_t zcl_SendDiscoverCmd( uint8 srcEP, afAddrType_t *dstAddr, uint16 clusterID, zclDiscoverCmd_t *discoverCmd, uint8 direction, uint8 disableDefaultRsp, uint8 seqNum ) { uint16 dataLen = 2 + 1; // Start Attribute ID and Max Attribute IDs uint8 *buf; ZStatus_t status; buf = osal_mem_alloc( dataLen ); if ( buf != NULL ) { // Load the buffer - serially uint8 *pBuf = buf; *pBuf++ = LO_UINT16(discoverCmd->startAttr); *pBuf++ = HI_UINT16(discoverCmd->startAttr); *pBuf++ = discoverCmd->maxAttrIDs; status = zcl_SendCommand( srcEP, dstAddr, clusterID, ZCL_CMD_DISCOVER, FALSE, direction, disableDefaultRsp, 0, seqNum, dataLen, buf ); osal_mem_free( buf ); } else { status = ZMemError; } return ( status ); } /********************************************************************* * @fn zcl_SendDiscoverRspCmd * * @brief Send a Discover Response command * * @param dstAddr - destination address * @param clusterID - cluster ID * @param reportRspCmd - report response command to be sent * @param direction - direction of the command * @param seqNum - transaction sequence number * * @return ZSuccess if OK */ ZStatus_t zcl_SendDiscoverRspCmd( uint8 srcEP, afAddrType_t *dstAddr, uint16 clusterID, zclDiscoverRspCmd_t *discoverRspCmd, uint8 direction, uint8 disableDefaultRsp, uint8 seqNum ) { uint16 dataLen = 1; // Discovery complete uint8 *buf; ZStatus_t status; // calculate the size of the command dataLen += discoverRspCmd->numAttr * (2 + 1); // Attribute ID and Data Type buf = osal_mem_alloc( dataLen ); if ( buf != NULL ) { // Load the buffer - serially uint8 *pBuf = buf; *pBuf++ = discoverRspCmd->discComplete; for ( uint8 i = 0; i < discoverRspCmd->numAttr; i++ ) { *pBuf++ = LO_UINT16(discoverRspCmd->attrList[i].attrID); *pBuf++ = HI_UINT16(discoverRspCmd->attrList[i].attrID); *pBuf++ = discoverRspCmd->attrList[i].dataType; } status = zcl_SendCommand( srcEP, dstAddr, clusterID, ZCL_CMD_DISCOVER_RSP, FALSE, direction, disableDefaultRsp, 0, seqNum, dataLen, buf ); osal_mem_free( buf ); } else { status = ZMemError; } return ( status ); } #endif // ZCL_DISCOVER /********************************************************************* * PRIVATE FUNCTIONS *********************************************************************/ /********************************************************************* * @fn zclProcessMessageMSG * * @brief Data message processor callback. This function processes * any incoming data - probably from other devices. So, based * on cluster ID, perform the intended action. * * @param pkt - incoming message * * @return none */ void zclProcessMessageMSG( afIncomingMSGPacket_t *pkt ) { endPointDesc_t *epDesc; zclIncoming_t inMsg; zclLibPlugin_t *pInPlugin; zclDefaultRspCmd_t defautlRspCmd; uint8 options; uint8 securityEnable; uint8 interPanMsg; ZStatus_t status = ZFailure; if ( pkt->cmd.DataLength == 0 ) { return; // Error, ignore the message } // Initialize inMsg.msg = pkt; inMsg.attrCmd = NULL; inMsg.pData = NULL; inMsg.pDataLen = 0; inMsg.pData = zclParseHdr( &(inMsg.hdr), pkt->cmd.Data ); inMsg.pDataLen = pkt->cmd.DataLength; inMsg.pDataLen -= (uint16)(inMsg.pData - pkt->cmd.Data); // Find the wanted endpoint epDesc = afFindEndPointDesc( pkt->endPoint ); if ( epDesc == NULL ) { return; // Error, ignore the message } if ( ( epDesc->simpleDesc == NULL ) || ( zcl_DeviceOperational( pkt->endPoint, pkt->clusterId, inMsg.hdr.fc.type, inMsg.hdr.commandID, epDesc->simpleDesc->AppProfId ) == FALSE ) ) { return; // Error, ignore the message } #if defined ( INTER_PAN ) if ( StubAPS_InterPan( pkt->srcAddr.panId, pkt->srcAddr.endPoint ) ) { // No foundation command is supported thru Inter-PAN communication. // But the Smart Light cluster uses a different Frame Control format // for it's Inter-PAN messages, where the messages could be confused // with the foundation commands. if ( !ZCL_CLUSTER_ID_SL( pkt->clusterId ) && zcl_ProfileCmd( inMsg.hdr.fc.type ) ) { return; } interPanMsg = TRUE; options = AF_TX_OPTIONS_NONE; } else #endif { interPanMsg = FALSE; options = zclGetClusterOption( pkt->endPoint, pkt->clusterId ); } // Find the appropriate plugin pInPlugin = zclFindPlugin( pkt->clusterId, epDesc->simpleDesc->AppProfId ); // Local and remote Security options must match except for Default Response command if ( ( pInPlugin != NULL ) && !zcl_DefaultRspCmd( inMsg.hdr ) ) { securityEnable = ( options & AF_EN_SECURITY ) ? TRUE : FALSE; if ( pkt->SecurityUse != securityEnable ) { if ( UNICAST_MSG( inMsg.msg ) ) { // Send a Default Response command back with no Application Link Key security if ( securityEnable ) { zclSetSecurityOption( pkt->endPoint, pkt->clusterId, FALSE ); } defautlRspCmd.statusCode = status; defautlRspCmd.commandID = inMsg.hdr.commandID; zcl_SendDefaultRspCmd( inMsg.msg->endPoint, &(inMsg.msg->srcAddr), inMsg.msg->clusterId, &defautlRspCmd, ZCL_FRAME_SERVER_CLIENT_DIR, true, inMsg.hdr.manuCode, inMsg.hdr.transSeqNum ); if ( securityEnable ) { zclSetSecurityOption( pkt->endPoint, pkt->clusterId, TRUE ); } } return; // Error, ignore the message } } // Is this a foundation type message if ( !interPanMsg && zcl_ProfileCmd( inMsg.hdr.fc.type ) ) { if ( inMsg.hdr.fc.manuSpecific ) { // We don't support any manufacturer specific command status = ZCL_STATUS_UNSUP_MANU_GENERAL_COMMAND; } else if ( ( inMsg.hdr.commandID <= ZCL_CMD_MAX ) && ( zclCmdTable[inMsg.hdr.commandID].pfnParseInProfile != NULL ) ) { zclParseCmd_t parseCmd; parseCmd.endpoint = pkt->endPoint; parseCmd.dataLen = inMsg.pDataLen; parseCmd.pData = inMsg.pData; // Parse the command, remember that the return value is a pointer to allocated memory inMsg.attrCmd = zclParseCmd( inMsg.hdr.commandID, &parseCmd ); if ( (inMsg.attrCmd != NULL) && (zclCmdTable[inMsg.hdr.commandID].pfnProcessInProfile != NULL) ) { // Process the command if ( zclProcessCmd( inMsg.hdr.commandID, &inMsg ) == FALSE ) { // Couldn't find attribute in the table. } } // Free the buffer if ( inMsg.attrCmd ) { osal_mem_free( inMsg.attrCmd ); } if ( CMD_HAS_RSP( inMsg.hdr.commandID ) ) { return; // We're done } status = ZSuccess; } else { // Unsupported message status = ZCL_STATUS_UNSUP_GENERAL_COMMAND; } } else // Not a foundation type message, so it must be specific to the cluster ID. { if ( pInPlugin && pInPlugin->pfnIncomingHdlr ) { // The return value of the plugin function will be // ZSuccess - Supported and need default response // ZFailure - Unsupported // ZCL_STATUS_CMD_HAS_RSP - Supported and do not need default rsp // ZCL_STATUS_INVALID_FIELD - Supported, but the incoming msg is wrong formatted // ZCL_STATUS_INVALID_VALUE - Supported, but the request not achievable by the h/w // ZCL_STATUS_SOFTWARE_FAILURE - Supported but ZStack memory allocation fails status = pInPlugin->pfnIncomingHdlr( &inMsg ); if ( status == ZCL_STATUS_CMD_HAS_RSP || ( interPanMsg && status == ZSuccess ) ) { return; // We're done } } if ( status == ZFailure ) { // Unsupported message if ( inMsg.hdr.fc.manuSpecific ) { status = ZCL_STATUS_UNSUP_MANU_CLUSTER_COMMAND; } else { status = ZCL_STATUS_UNSUP_CLUSTER_COMMAND; } } } if ( UNICAST_MSG( inMsg.msg ) && inMsg.hdr.fc.disableDefaultRsp == 0 ) { // Send a Default Response command back defautlRspCmd.statusCode = status; defautlRspCmd.commandID = inMsg.hdr.commandID; zcl_SendDefaultRspCmd( inMsg.msg->endPoint, &(inMsg.msg->srcAddr), inMsg.msg->clusterId, &defautlRspCmd, ZCL_FRAME_SERVER_CLIENT_DIR, true, inMsg.hdr.manuCode, inMsg.hdr.transSeqNum ); } } /********************************************************************* * @fn zclParseHdr * * @brief Parse header of the ZCL format * * @param hdr - place to put the frame control information * @param pData - incoming buffer to parse * * @return pointer past the header */ uint8 *zclParseHdr( zclFrameHdr_t *hdr, uint8 *pData ) { // Clear the header osal_memset( (uint8 *)hdr, 0, sizeof ( zclFrameHdr_t ) ); // Parse the Frame Control hdr->fc.type = zcl_FCType( *pData ); hdr->fc.manuSpecific = zcl_FCManuSpecific( *pData ) ? 1 : 0; if ( zcl_FCDirection( *pData ) ) { hdr->fc.direction = ZCL_FRAME_SERVER_CLIENT_DIR; } else { hdr->fc.direction = ZCL_FRAME_CLIENT_SERVER_DIR; } hdr->fc.disableDefaultRsp = zcl_FCDisableDefaultRsp( *pData ) ? 1 : 0; pData++; // move past the frame control field // parse the manfacturer code if ( hdr->fc.manuSpecific ) { hdr->manuCode = BUILD_UINT16( pData[0], pData[1] ); pData += 2; } // parse the Transaction Sequence Number hdr->transSeqNum = *pData++; // parse the Cluster's command ID hdr->commandID = *pData++; // Should point to the frame payload return ( pData ); } /********************************************************************* * @fn zclBuildHdr * * @brief Build header of the ZCL format * * @param hdr - outgoing header information * @param pData - outgoing header space * * @return pointer past the header */ static uint8 *zclBuildHdr( zclFrameHdr_t *hdr, uint8 *pData ) { // Build the Frame Control byte *pData = hdr->fc.type; *pData |= hdr->fc.manuSpecific << 2; *pData |= hdr->fc.direction << 3; *pData |= hdr->fc.disableDefaultRsp << 4; pData++; // move past the frame control field // Add the manfacturer code if ( hdr->fc.manuSpecific ) { *pData++ = LO_UINT16( hdr->manuCode ); *pData++ = HI_UINT16( hdr->manuCode ); } // Add the Transaction Sequence Number *pData++ = hdr->transSeqNum; // Add the Cluster's command ID *pData++ = hdr->commandID; // Should point to the frame payload return ( pData ); } /********************************************************************* * @fn zclCalcHdrSize * * @brief Calculate the number of bytes needed for an outgoing * ZCL header. * * @param hdr - outgoing header information * * @return returns the number of bytes needed */ static uint8 zclCalcHdrSize( zclFrameHdr_t *hdr ) { uint8 needed = (1 + 1 + 1); // frame control + transaction seq num + cmd ID // Add the manfacturer code if ( hdr->fc.manuSpecific ) { needed += 2; } return ( needed ); } /********************************************************************* * @fn zclFindPlugin * * @brief Find the right plugin for a cluster ID * * @param clusterID - cluster ID to look for * @param profileID - profile ID * * @return pointer to plugin, NULL if not found */ static zclLibPlugin_t *zclFindPlugin( uint16 clusterID, uint16 profileID ) { (void)profileID; // Intentionally unreferenced parameter zclLibPlugin_t *pLoop = plugins; while ( pLoop != NULL ) { if ( ( clusterID >= pLoop->startClusterID ) && ( clusterID <= pLoop->endClusterID ) ) { return ( pLoop ); } pLoop = pLoop->next; } return ( (zclLibPlugin_t *)NULL ); } /********************************************************************* * @fn zclFindAttrRecsList * * @brief Find the right attribute record list for an endpoint * * @param clusterID - endpointto look for * * @return pointer to record list, NULL if not found */ static zclAttrRecsList *zclFindAttrRecsList( uint8 endpoint ) { zclAttrRecsList *pLoop = attrList; while ( pLoop != NULL ) { if ( pLoop->endpoint == endpoint ) { return ( pLoop ); } pLoop = pLoop->next; } return ( NULL ); } /********************************************************************* * @fn zclFindAttrRec * * @brief Find the attribute record that matchs the parameters * * @param endpoint - Application's endpoint * @param clusterID - cluster ID * @param attrId - attribute looking for * @param pAttr - attribute record to be returned * * @return TRUE if record found. FALSE, otherwise. */ uint8 zclFindAttrRec( uint8 endpoint, uint16 clusterID, uint16 attrId, zclAttrRec_t *pAttr ) { uint8 x; zclAttrRecsList *pRec = zclFindAttrRecsList( endpoint ); if ( pRec != NULL ) { for ( x = 0; x < pRec->numAttributes; x++ ) { if ( pRec->attrs[x].clusterID == clusterID && pRec->attrs[x].attr.attrId == attrId ) { *pAttr = pRec->attrs[x]; return ( TRUE ); // EMBEDDED RETURN } } } return ( FALSE ); } #if defined ( ZCL_READ ) || defined ( ZCL_WRITE ) /********************************************************************* * @fn zclGetReadWriteCB * * @brief Get the Read/Write callback function pointer for a given endpoint. * * @param endpoint - Application's endpoint * * @return Read/Write CB, NULL if not found */ static zclReadWriteCB_t zclGetReadWriteCB( uint8 endpoint ) { zclAttrRecsList *pRec = zclFindAttrRecsList( endpoint ); if ( pRec != NULL ) { return ( pRec->pfnReadWriteCB ); } return ( NULL ); } /********************************************************************* * @fn zclGetAuthorizeCB * * @brief Get the Read/Write Authorization callback function pointer * for a given endpoint. * * @param endpoint - Application's endpoint * * @return Authorization CB, NULL if not found */ static zclAuthorizeCB_t zclGetAuthorizeCB( uint8 endpoint ) { zclAttrRecsList *pRec = zclFindAttrRecsList( endpoint ); if ( pRec != NULL ) { return ( pRec->pfnAuthorizeCB ); } return ( NULL ); } #endif // ZCL_READ || ZCL_WRITE /********************************************************************* * @fn zclFindClusterOption * * @brief Find the option record that matchs the cluster id * * @param endpoint - Application's endpoint * @param clusterID - cluster ID looking for * * @return pointer to clutser option, NULL if not found */ static zclOptionRec_t *zclFindClusterOption( uint8 endpoint, uint16 clusterID ) { zclClusterOptionList *pLoop; pLoop = clusterOptionList; while ( pLoop != NULL ) { if ( pLoop->endpoint == endpoint ) { for ( uint8 x = 0; x < pLoop->numOptions; x++ ) { if ( pLoop->options[x].clusterID == clusterID ) { return ( &(pLoop->options[x]) ); // EMBEDDED RETURN } } } pLoop = pLoop->next; } return ( NULL ); } /********************************************************************* * @fn zclGetClusterOption * * @brief Get the option record that matchs the cluster id * * @param endpoint - Application's endpoint * @param clusterID - cluster ID looking for * * @return clutser option, AF_TX_OPTIONS_NONE if not found */ static uint8 zclGetClusterOption( uint8 endpoint, uint16 clusterID ) { uint8 option; zclOptionRec_t *pOption; pOption = zclFindClusterOption( endpoint, clusterID ); if ( pOption != NULL ) { option = pOption->option; if ( !ZG_SECURE_ENABLED ) { option &= (AF_EN_SECURITY ^ 0xFF); // make sure Application Link Key security is off } return ( option ); // EMBEDDED RETURN } return ( AF_TX_OPTIONS_NONE ); } /********************************************************************* * @fn zclSetSecurityOption * * @brief Set the security option for the cluster id * * @param endpoint - Application's endpoint * @param clusterID - cluster ID looking for * @param enable - whether to enable (TRUE) or disable (FALSE) security option * * @return none */ static void zclSetSecurityOption( uint8 endpoint, uint16 clusterID, uint8 enable ) { zclOptionRec_t *pOption; pOption = zclFindClusterOption( endpoint, clusterID ); if ( pOption != NULL ) { if ( enable ) { pOption->option |= AF_EN_SECURITY; } else { pOption->option &= (AF_EN_SECURITY ^ 0xFF); } } } #ifdef ZCL_DISCOVER /********************************************************************* * @fn zclFindNextAttrRec * * @brief Find the attribute (or next) record that matchs the parameters * * @param endpoint - Application's endpoint * @param clusterID - cluster ID * @param attr - attribute looking for * * @return pointer to attribute record, NULL if not found */ static uint8 zclFindNextAttrRec( uint8 endpoint, uint16 clusterID, uint16 *attrId, zclAttrRec_t *pAttr ) { zclAttrRecsList *pRec = zclFindAttrRecsList( endpoint ); if ( pRec != NULL ) { for ( uint16 x = 0; x < pRec->numAttributes; x++ ) { if ( ( pRec->attrs[x].clusterID == clusterID ) && ( pRec->attrs[x].attr.attrId >= *attrId ) ) { *pAttr = pRec->attrs[x]; // Update attribute ID *attrId = pAttr->attr.attrId; return ( TRUE ); // EMBEDDED RETURN } } } return ( FALSE ); } #endif // ZCL_DISCOVER /********************************************************************* * @fn zclSerializeData * * @brief Builds a buffer from the attribute data to sent out over * the air. * * @param dataType - data types defined in zcl.h * @param attrData - pointer to the attribute data * @param buf - where to put the serialized data * * @return pointer to end of destination buffer */ uint8 *zclSerializeData( uint8 dataType, void *attrData, uint8 *buf ) { uint8 *pStr; uint16 len; switch ( dataType ) { case ZCL_DATATYPE_DATA8: case ZCL_DATATYPE_BOOLEAN: case ZCL_DATATYPE_BITMAP8: case ZCL_DATATYPE_INT8: case ZCL_DATATYPE_UINT8: case ZCL_DATATYPE_ENUM8: *buf++ = *((uint8 *)attrData); break; case ZCL_DATATYPE_DATA16: case ZCL_DATATYPE_BITMAP16: case ZCL_DATATYPE_UINT16: case ZCL_DATATYPE_INT16: case ZCL_DATATYPE_ENUM16: case ZCL_DATATYPE_SEMI_PREC: case ZCL_DATATYPE_CLUSTER_ID: case ZCL_DATATYPE_ATTR_ID: *buf++ = LO_UINT16( *((uint16*)attrData) ); *buf++ = HI_UINT16( *((uint16*)attrData) ); break; case ZCL_DATATYPE_DATA24: case ZCL_DATATYPE_BITMAP24: case ZCL_DATATYPE_UINT24: case ZCL_DATATYPE_INT24: *buf++ = BREAK_UINT32( *((uint32*)attrData), 0 ); *buf++ = BREAK_UINT32( *((uint32*)attrData), 1 ); *buf++ = BREAK_UINT32( *((uint32*)attrData), 2 ); break; case ZCL_DATATYPE_DATA32: case ZCL_DATATYPE_BITMAP32: case ZCL_DATATYPE_UINT32: case ZCL_DATATYPE_INT32: case ZCL_DATATYPE_SINGLE_PREC: case ZCL_DATATYPE_TOD: case ZCL_DATATYPE_DATE: case ZCL_DATATYPE_UTC: case ZCL_DATATYPE_BAC_OID: buf = osal_buffer_uint32( buf, *((uint32*)attrData) ); break; case ZCL_DATATYPE_UINT40: pStr = (uint8*)attrData; buf = osal_memcpy( buf, pStr, 5 ); break; case ZCL_DATATYPE_UINT48: pStr = (uint8*)attrData; buf = osal_memcpy( buf, pStr, 6 ); break; case ZCL_DATATYPE_IEEE_ADDR: pStr = (uint8*)attrData; buf = osal_memcpy( buf, pStr, 8 ); break; case ZCL_DATATYPE_CHAR_STR: case ZCL_DATATYPE_OCTET_STR: pStr = (uint8*)attrData; len = *pStr; buf = osal_memcpy( buf, pStr, len+1 ); // Including length field break; case ZCL_DATATYPE_LONG_CHAR_STR: case ZCL_DATATYPE_LONG_OCTET_STR: pStr = (uint8*)attrData; len = BUILD_UINT16( pStr[0], pStr[1] ); buf = osal_memcpy( buf, pStr, len+2 ); // Including length field break; case ZCL_DATATYPE_128_BIT_SEC_KEY: pStr = (uint8*)attrData; buf = osal_memcpy( buf, pStr, SEC_KEY_LEN ); break; case ZCL_DATATYPE_NO_DATA: case ZCL_DATATYPE_UNKNOWN: // Fall through default: break; } return ( buf ); } #ifdef ZCL_REPORT /********************************************************************* * @fn zclAnalogDataType * * @brief Checks to see if Data Type is Analog * * @param dataType - data type * * @return TRUE if data type is analog */ uint8 zclAnalogDataType( uint8 dataType ) { uint8 analog; switch ( dataType ) { case ZCL_DATATYPE_UINT8: case ZCL_DATATYPE_UINT16: case ZCL_DATATYPE_UINT24: case ZCL_DATATYPE_UINT32: case ZCL_DATATYPE_UINT40: case ZCL_DATATYPE_UINT48: case ZCL_DATATYPE_UINT56: case ZCL_DATATYPE_UINT64: case ZCL_DATATYPE_INT8: case ZCL_DATATYPE_INT16: case ZCL_DATATYPE_INT24: case ZCL_DATATYPE_INT32: case ZCL_DATATYPE_INT40: case ZCL_DATATYPE_INT48: case ZCL_DATATYPE_INT56: case ZCL_DATATYPE_INT64: case ZCL_DATATYPE_SEMI_PREC: case ZCL_DATATYPE_SINGLE_PREC: case ZCL_DATATYPE_DOUBLE_PREC: case ZCL_DATATYPE_TOD: case ZCL_DATATYPE_DATE: case ZCL_DATATYPE_UTC: analog = TRUE; break; default: analog = FALSE; break; } return ( analog ); } /********************************************************************* * @fn zcl_BuildAnalogData * * @brief Build an analog arribute out of sequential bytes. * * @param dataType - type of data * @param pData - pointer to data * @param pBuf - where to put the data * * @return none */ static void zcl_BuildAnalogData( uint8 dataType, uint8 *pData, uint8 *pBuf) { switch ( dataType ) { case ZCL_DATATYPE_UINT8: case ZCL_DATATYPE_INT8: *pData = *pBuf; break; case ZCL_DATATYPE_UINT16: case ZCL_DATATYPE_INT16: case ZCL_DATATYPE_SEMI_PREC: *((uint16*)pData) = BUILD_UINT16( pBuf[0], pBuf[1] ); break; case ZCL_DATATYPE_UINT24: case ZCL_DATATYPE_INT24: *((uint32*)pData) = osal_build_uint32( pBuf, 3 ); break; case ZCL_DATATYPE_UINT32: case ZCL_DATATYPE_INT32: case ZCL_DATATYPE_SINGLE_PREC: case ZCL_DATATYPE_TOD: case ZCL_DATATYPE_DATE: case ZCL_DATATYPE_UTC: *((uint32*)pData) = osal_build_uint32( pBuf, 4 ); break; case ZCL_DATATYPE_UINT40: case ZCL_DATATYPE_UINT48: case ZCL_DATATYPE_UINT56: case ZCL_DATATYPE_UINT64: case ZCL_DATATYPE_INT40: case ZCL_DATATYPE_INT48: case ZCL_DATATYPE_INT56: case ZCL_DATATYPE_INT64: case ZCL_DATATYPE_DOUBLE_PREC: *pData = 0; break; default: *pData = 0; break; } } #endif // ZCL_REPORT /********************************************************************* * @fn zclGetDataTypeLength * * @brief Return the length of the datatype in octet. * * NOTE: Should not be called for ZCL_DATATYPE_OCTECT_STR or * ZCL_DATATYPE_CHAR_STR data types. * * @param dataType - data type * * @return length of data */ uint8 zclGetDataTypeLength( uint8 dataType ) { uint8 len; switch ( dataType ) { case ZCL_DATATYPE_DATA8: case ZCL_DATATYPE_BOOLEAN: case ZCL_DATATYPE_BITMAP8: case ZCL_DATATYPE_INT8: case ZCL_DATATYPE_UINT8: case ZCL_DATATYPE_ENUM8: len = 1; break; case ZCL_DATATYPE_DATA16: case ZCL_DATATYPE_BITMAP16: case ZCL_DATATYPE_UINT16: case ZCL_DATATYPE_INT16: case ZCL_DATATYPE_ENUM16: case ZCL_DATATYPE_SEMI_PREC: case ZCL_DATATYPE_CLUSTER_ID: case ZCL_DATATYPE_ATTR_ID: len = 2; break; case ZCL_DATATYPE_DATA24: case ZCL_DATATYPE_BITMAP24: case ZCL_DATATYPE_UINT24: case ZCL_DATATYPE_INT24: len = 3; break; case ZCL_DATATYPE_DATA32: case ZCL_DATATYPE_BITMAP32: case ZCL_DATATYPE_UINT32: case ZCL_DATATYPE_INT32: case ZCL_DATATYPE_SINGLE_PREC: case ZCL_DATATYPE_TOD: case ZCL_DATATYPE_DATE: case ZCL_DATATYPE_UTC: case ZCL_DATATYPE_BAC_OID: len = 4; break; case ZCL_DATATYPE_UINT40: case ZCL_DATATYPE_INT40: len = 5; break; case ZCL_DATATYPE_UINT48: case ZCL_DATATYPE_INT48: len = 6; break; case ZCL_DATATYPE_UINT56: case ZCL_DATATYPE_INT56: len = 7; break; case ZCL_DATATYPE_DOUBLE_PREC: case ZCL_DATATYPE_IEEE_ADDR: case ZCL_DATATYPE_UINT64: case ZCL_DATATYPE_INT64: len = 8; break; case ZCL_DATATYPE_128_BIT_SEC_KEY: len = SEC_KEY_LEN; break; case ZCL_DATATYPE_NO_DATA: case ZCL_DATATYPE_UNKNOWN: // Fall through default: len = 0; break; } return ( len ); } /********************************************************************* * @fn zclGetAttrDataLength * * @brief Return the length of the attribute. * * @param dataType - data type * @param pData - pointer to data * * @return returns atrribute length */ uint16 zclGetAttrDataLength( uint8 dataType, uint8 *pData ) { uint16 dataLen = 0; if ( dataType == ZCL_DATATYPE_LONG_CHAR_STR || dataType == ZCL_DATATYPE_LONG_OCTET_STR ) { dataLen = BUILD_UINT16( pData[0], pData[1] ) + 2; // long string length + 2 for length field } else if ( dataType == ZCL_DATATYPE_CHAR_STR || dataType == ZCL_DATATYPE_OCTET_STR ) { dataLen = *pData + 1; // string length + 1 for length field } else { dataLen = zclGetDataTypeLength( dataType ); } return ( dataLen ); } /********************************************************************* * @fn zclReadAttrData * * @brief Read the attribute's current value into pAttrData. * * @param pAttrData - where to put attribute data * @param pAttr - pointer to attribute * @param pDataLen - where to put attribute data length * * @return Success */ uint8 zclReadAttrData( uint8 *pAttrData, zclAttrRec_t *pAttr, uint16 *pDataLen ) { uint16 dataLen; dataLen = zclGetAttrDataLength( pAttr->attr.dataType, (uint8*)(pAttr->attr.dataPtr) ); osal_memcpy( pAttrData, pAttr->attr.dataPtr, dataLen ); if ( pDataLen != NULL ) { *pDataLen = dataLen; } return ( ZCL_STATUS_SUCCESS ); } #ifdef ZCL_READ /********************************************************************* * @fn zclGetAttrDataLengthUsingCB * * @brief Use application's callback to get the length of the attribute's * current value stored in the database. * * @param endpoint - application's endpoint * @param clusterId - cluster that attribute belongs to * @param attrId - attribute id * * @return returns attribute length */ static uint16 zclGetAttrDataLengthUsingCB( uint8 endpoint, uint16 clusterId, uint16 attrId ) { uint16 dataLen = 0; zclReadWriteCB_t pfnReadWriteCB = zclGetReadWriteCB( endpoint ); if ( pfnReadWriteCB != NULL ) { // Only get the attribute length (*pfnReadWriteCB)( clusterId, attrId, ZCL_OPER_LEN, NULL, &dataLen ); } return ( dataLen ); } /********************************************************************* * @fn zclReadAttrDataUsingCB * * @brief Use application's callback to read the attribute's current * value stored in the database. * * @param endpoint - application's endpoint * @param clusterId - cluster that attribute belongs to * @param attrId - attribute id * @param pAttrData - where to put attribute data * @param pDataLen - where to put attribute data length * * @return Successful if data was read */ static ZStatus_t zclReadAttrDataUsingCB( uint8 endpoint, uint16 clusterId, uint16 attrId, uint8 *pAttrData, uint16 *pDataLen ) { zclReadWriteCB_t pfnReadWriteCB = zclGetReadWriteCB( endpoint ); if ( pDataLen != NULL ) { *pDataLen = 0; // Always initialize it to 0 } if ( pfnReadWriteCB != NULL ) { // Read the attribute value and its length return ( (*pfnReadWriteCB)( clusterId, attrId, ZCL_OPER_READ, pAttrData, pDataLen ) ); } return ( ZCL_STATUS_SOFTWARE_FAILURE ); } /********************************************************************* * @fn zclAuthorizeRead * * @brief Use application's callback to authorize a Read operation * on a given attribute. * * @param endpoint - application's endpoint * @param srcAddr - source Address * @param pAttr - pointer to attribute * * @return ZCL_STATUS_SUCCESS: Operation authorized * ZCL_STATUS_NOT_AUTHORIZED: Operation not authorized */ static ZStatus_t zclAuthorizeRead( uint8 endpoint, afAddrType_t *srcAddr, zclAttrRec_t *pAttr ) { if ( zcl_AccessCtrlAuthRead( pAttr->attr.accessControl ) ) { zclAuthorizeCB_t pfnAuthorizeCB = zclGetAuthorizeCB( endpoint ); if ( pfnAuthorizeCB != NULL ) { return ( (*pfnAuthorizeCB)( srcAddr, pAttr, ZCL_OPER_READ ) ); } } return ( ZCL_STATUS_SUCCESS ); } #endif // ZCL_READ #ifdef ZCL_WRITE /********************************************************************* * @fn zclWriteAttrData * * @brief Write the received data. * * @param endpoint - application's endpoint * @param pAttr - where to write data to * @param pWriteRec - data to be written * * @return Successful if data was written */ static ZStatus_t zclWriteAttrData( uint8 endpoint, afAddrType_t *srcAddr, zclAttrRec_t *pAttr, zclWriteRec_t *pWriteRec ) { uint8 status; if ( zcl_AccessCtrlWrite( pAttr->attr.accessControl ) ) { status = zclAuthorizeWrite( endpoint, srcAddr, pAttr ); if ( status == ZCL_STATUS_SUCCESS ) { if ( ( zcl_ValidateAttrDataCB == NULL ) || zcl_ValidateAttrDataCB( pAttr, pWriteRec ) ) { // Write the attribute value uint16 len = zclGetAttrDataLength( pAttr->attr.dataType, pWriteRec->attrData ); osal_memcpy( pAttr->attr.dataPtr, pWriteRec->attrData, len ); status = ZCL_STATUS_SUCCESS; } else { status = ZCL_STATUS_INVALID_VALUE; } } } else { status = ZCL_STATUS_READ_ONLY; } return ( status ); } /********************************************************************* * @fn zclWriteAttrDataUsingCB * * @brief Use application's callback to write the attribute's current * value stored in the database. * * @param endpoint - application's endpoint * @param pAttr - where to write data to * @param pAttrData - data to be written * * @return Successful if data was written */ static ZStatus_t zclWriteAttrDataUsingCB( uint8 endpoint, afAddrType_t *srcAddr, zclAttrRec_t *pAttr, uint8 *pAttrData ) { uint8 status; if ( zcl_AccessCtrlWrite( pAttr->attr.accessControl ) ) { status = zclAuthorizeWrite( endpoint, srcAddr, pAttr ); if ( status == ZCL_STATUS_SUCCESS ) { zclReadWriteCB_t pfnReadWriteCB = zclGetReadWriteCB( endpoint ); if ( pfnReadWriteCB != NULL ) { // Write the attribute value status = (*pfnReadWriteCB)( pAttr->clusterID, pAttr->attr.attrId, ZCL_OPER_WRITE, pAttrData, NULL ); } else { status = ZCL_STATUS_SOFTWARE_FAILURE; } } } else { status = ZCL_STATUS_READ_ONLY; } return ( status ); } /********************************************************************* * @fn zclAuthorizeWrite * * @brief Use application's callback to authorize a Write operation * on a given attribute. * * @param endpoint - application's endpoint * @param srcAddr - source Address * @param pAttr - pointer to attribute * * @return ZCL_STATUS_SUCCESS: Operation authorized * ZCL_STATUS_NOT_AUTHORIZED: Operation not authorized */ static ZStatus_t zclAuthorizeWrite( uint8 endpoint, afAddrType_t *srcAddr, zclAttrRec_t *pAttr ) { if ( zcl_AccessCtrlAuthWrite( pAttr->attr.accessControl ) ) { zclAuthorizeCB_t pfnAuthorizeCB = zclGetAuthorizeCB( endpoint ); if ( pfnAuthorizeCB != NULL ) { return ( (*pfnAuthorizeCB)( srcAddr, pAttr, ZCL_OPER_WRITE ) ); } } return ( ZCL_STATUS_SUCCESS ); } #endif // ZCL_WRITE #ifdef ZCL_READ /********************************************************************* * @fn zclParseInReadCmd * * @brief Parse the "Profile" Read Commands * * NOTE: THIS FUNCTION ALLOCATES THE RETURN BUFFER, SO THE CALLING * FUNCTION IS RESPONSIBLE TO FREE THE MEMORY. * * @param pCmd - pointer to incoming data to parse * * @return pointer to the parsed command structure */ void *zclParseInReadCmd( zclParseCmd_t *pCmd ) { zclReadCmd_t *readCmd; uint8 *pBuf = pCmd->pData; readCmd = (zclReadCmd_t *)osal_mem_alloc( sizeof ( zclReadCmd_t ) + pCmd->dataLen ); if ( readCmd != NULL ) { readCmd->numAttr = pCmd->dataLen / 2; // Atrribute ID for ( uint8 i = 0; i < readCmd->numAttr; i++ ) { readCmd->attrID[i] = BUILD_UINT16( pBuf[0], pBuf[1] ); pBuf += 2; } } return ( (void *)readCmd ); } /********************************************************************* * @fn zclParseInReadRspCmd * * @brief Parse the "Profile" Read Response Commands * * NOTE: THIS FUNCTION ALLOCATES THE RETURN BUFFER, SO THE CALLING * FUNCTION IS RESPONSIBLE TO FREE THE MEMORY. * * @param pCmd - pointer to incoming data to parse * * @return pointer to the parsed command structure */ static void *zclParseInReadRspCmd( zclParseCmd_t *pCmd ) { zclReadRspCmd_t *readRspCmd; uint8 *pBuf = pCmd->pData; uint8 *dataPtr; uint8 numAttr = 0; uint8 hdrLen; uint16 dataLen = 0; uint16 attrDataLen; // find out the number of attributes and the length of attribute data while ( pBuf < ( pCmd->pData + pCmd->dataLen ) ) { uint8 status; numAttr++; pBuf += 2; // move pass attribute id status = *pBuf++; if ( status == ZCL_STATUS_SUCCESS ) { uint8 dataType = *pBuf++; attrDataLen = zclGetAttrDataLength( dataType, pBuf ); pBuf += attrDataLen; // move pass attribute data // add padding if needed if ( PADDING_NEEDED( attrDataLen ) ) { attrDataLen++; } dataLen += attrDataLen; } } // calculate the length of the response header hdrLen = sizeof( zclReadRspCmd_t ) + ( numAttr * sizeof( zclReadRspStatus_t ) ); readRspCmd = (zclReadRspCmd_t *)osal_mem_alloc( hdrLen + dataLen ); if ( readRspCmd != NULL ) { pBuf = pCmd->pData; dataPtr = (uint8 *)( (uint8 *)readRspCmd + hdrLen ); readRspCmd->numAttr = numAttr; for ( uint8 i = 0; i < numAttr; i++ ) { zclReadRspStatus_t *statusRec = &(readRspCmd->attrList[i]); statusRec->attrID = BUILD_UINT16( pBuf[0], pBuf[1] ); pBuf += 2; statusRec->status = *pBuf++; if ( statusRec->status == ZCL_STATUS_SUCCESS ) { statusRec->dataType = *pBuf++; attrDataLen = zclGetAttrDataLength( statusRec->dataType, pBuf ); osal_memcpy( dataPtr, pBuf, attrDataLen); statusRec->data = dataPtr; pBuf += attrDataLen; // move pass attribute data // advance attribute data pointer if ( PADDING_NEEDED( attrDataLen ) ) { attrDataLen++; } dataPtr += attrDataLen; } } } return ( (void *)readRspCmd ); } #endif // ZCL_READ #ifdef ZCL_WRITE /********************************************************************* * @fn zclParseInWriteCmd * * @brief Parse the "Profile" Write, Write Undivided and Write No * Response Commands * * NOTE: THIS FUNCTION ALLOCATES THE RETURN BUFFER, SO THE CALLING * FUNCTION IS RESPONSIBLE TO FREE THE MEMORY. * * @param pCmd - pointer to incoming data to parse * * @return pointer to the parsed command structure */ void *zclParseInWriteCmd( zclParseCmd_t *pCmd ) { zclWriteCmd_t *writeCmd; uint8 *pBuf = pCmd->pData; uint16 attrDataLen; uint8 *dataPtr; uint8 numAttr = 0; uint8 hdrLen; uint16 dataLen = 0; // find out the number of attributes and the length of attribute data while ( pBuf < ( pCmd->pData + pCmd->dataLen ) ) { uint8 dataType; numAttr++; pBuf += 2; // move pass attribute id dataType = *pBuf++; attrDataLen = zclGetAttrDataLength( dataType, pBuf ); pBuf += attrDataLen; // move pass attribute data // add padding if needed if ( PADDING_NEEDED( attrDataLen ) ) { attrDataLen++; } dataLen += attrDataLen; } // calculate the length of the response header hdrLen = sizeof( zclWriteCmd_t ) + ( numAttr * sizeof( zclWriteRec_t ) ); writeCmd = (zclWriteCmd_t *)osal_mem_alloc( hdrLen + dataLen ); if ( writeCmd != NULL ) { pBuf = pCmd->pData; dataPtr = (uint8 *)( (uint8 *)writeCmd + hdrLen ); writeCmd->numAttr = numAttr; for ( uint8 i = 0; i < numAttr; i++ ) { zclWriteRec_t *statusRec = &(writeCmd->attrList[i]); statusRec->attrID = BUILD_UINT16( pBuf[0], pBuf[1] ); pBuf += 2; statusRec->dataType = *pBuf++; attrDataLen = zclGetAttrDataLength( statusRec->dataType, pBuf ); osal_memcpy( dataPtr, pBuf, attrDataLen); statusRec->attrData = dataPtr; pBuf += attrDataLen; // move pass attribute data // advance attribute data pointer if ( PADDING_NEEDED( attrDataLen ) ) { attrDataLen++; } dataPtr += attrDataLen; } } return ( (void *)writeCmd ); } /********************************************************************* * @fn zclParseInWriteRspCmd * * @brief Parse the "Profile" Write Response Commands * * NOTE: THIS FUNCTION ALLOCATES THE RETURN BUFFER, SO THE CALLING * FUNCTION IS RESPONSIBLE TO FREE THE MEMORY. * * @param pCmd - pointer to incoming data to parse * * @return pointer to the parsed command structure */ static void *zclParseInWriteRspCmd( zclParseCmd_t *pCmd ) { zclWriteRspCmd_t *writeRspCmd; uint8 *pBuf = pCmd->pData; uint8 i = 0; writeRspCmd = (zclWriteRspCmd_t *)osal_mem_alloc( sizeof ( zclWriteRspCmd_t ) + pCmd->dataLen ); if ( writeRspCmd != NULL ) { if ( pCmd->dataLen == 1 ) { // special case when all writes were successfull writeRspCmd->attrList[i++].status = *pBuf; } else { while ( pBuf < ( pCmd->pData + pCmd->dataLen ) ) { writeRspCmd->attrList[i].status = *pBuf++; writeRspCmd->attrList[i++].attrID = BUILD_UINT16( pBuf[0], pBuf[1] ); pBuf += 2; } } writeRspCmd->numAttr = i; } return ( (void *)writeRspCmd ); } #endif // ZCL_WRITE #ifdef ZCL_REPORT /********************************************************************* * @fn zclParseInConfigReportCmd * * @brief Parse the "Profile" Configure Reporting Command * * NOTE: THIS FUNCTION ALLOCATES THE RETURN BUFFER, SO THE CALLING * FUNCTION IS RESPONSIBLE TO FREE THE MEMORY. * * @param pCmd - pointer to incoming data to parse * * @return pointer to the parsed command structure */ void *zclParseInConfigReportCmd( zclParseCmd_t *pCmd ) { zclCfgReportCmd_t *cfgReportCmd; uint8 *pBuf = pCmd->pData; uint8 *dataPtr; uint8 numAttr = 0; uint8 dataType; uint8 hdrLen; uint16 dataLen = 0; uint8 reportChangeLen; // length of Reportable Change field // Calculate the length of the Request command while ( pBuf < ( pCmd->pData + pCmd->dataLen ) ) { uint8 direction; numAttr++; direction = *pBuf++; pBuf += 2; // move pass the attribute ID // Is there a Reportable Change field? if ( direction == ZCL_SEND_ATTR_REPORTS ) { dataType = *pBuf++; pBuf += 4; // move pass the Min and Max Reporting Intervals // For attributes of 'discrete' data types this field is omitted if ( zclAnalogDataType( dataType ) ) { reportChangeLen = zclGetDataTypeLength( dataType ); pBuf += reportChangeLen; // add padding if needed if ( PADDING_NEEDED( reportChangeLen ) ) { reportChangeLen++; } dataLen += reportChangeLen; } } else { pBuf += 2; // move pass the Timeout Period } } // while loop hdrLen = sizeof( zclCfgReportCmd_t ) + ( numAttr * sizeof( zclCfgReportRec_t ) ); cfgReportCmd = (zclCfgReportCmd_t *)osal_mem_alloc( hdrLen + dataLen ); if ( cfgReportCmd != NULL ) { pBuf = pCmd->pData; dataPtr = (uint8 *)( (uint8 *)cfgReportCmd + hdrLen ); cfgReportCmd->numAttr = numAttr; for ( uint8 i = 0; i < numAttr; i++ ) { zclCfgReportRec_t *reportRec = &(cfgReportCmd->attrList[i]); osal_memset( reportRec, 0, sizeof( zclCfgReportRec_t ) ); reportRec->direction = *pBuf++; reportRec->attrID = BUILD_UINT16( pBuf[0], pBuf[1] ); pBuf += 2; if ( reportRec->direction == ZCL_SEND_ATTR_REPORTS ) { // Attribute to be reported reportRec->dataType = *pBuf++; reportRec->minReportInt = BUILD_UINT16( pBuf[0], pBuf[1] ); pBuf += 2; reportRec->maxReportInt = BUILD_UINT16( pBuf[0], pBuf[1] ); pBuf += 2; // For attributes of 'discrete' data types this field is omitted if ( zclAnalogDataType( reportRec->dataType ) ) { zcl_BuildAnalogData( reportRec->dataType, dataPtr, pBuf); reportRec->reportableChange = dataPtr; reportChangeLen = zclGetDataTypeLength( reportRec->dataType ); pBuf += reportChangeLen; // advance attribute data pointer if ( PADDING_NEEDED( reportChangeLen ) ) { reportChangeLen++; } dataPtr += reportChangeLen; } } else { // Attribute reports to be received reportRec->timeoutPeriod = BUILD_UINT16( pBuf[0], pBuf[1] ); pBuf += 2; } } // while loop } return ( (void *)cfgReportCmd ); } /********************************************************************* * @fn zclParseInConfigReportRspCmd * * @brief Parse the "Profile" Configure Reporting Response Command * * NOTE: THIS FUNCTION ALLOCATES THE RETURN BUFFER, SO THE CALLING * FUNCTION IS RESPONSIBLE TO FREE THE MEMORY. * * @param pCmd - pointer to incoming data to parse * * @return pointer to the parsed command structure */ static void *zclParseInConfigReportRspCmd( zclParseCmd_t *pCmd ) { zclCfgReportRspCmd_t *cfgReportRspCmd; uint8 *pBuf = pCmd->pData; uint8 numAttr; numAttr = pCmd->dataLen / ( 1 + 1 + 2 ); // Status + Direction + Attribute ID cfgReportRspCmd = (zclCfgReportRspCmd_t *)osal_mem_alloc( sizeof( zclCfgReportRspCmd_t ) + ( numAttr * sizeof( zclCfgReportStatus_t ) ) ); if ( cfgReportRspCmd != NULL ) { cfgReportRspCmd->numAttr = numAttr; for ( uint8 i = 0; i < cfgReportRspCmd->numAttr; i++ ) { cfgReportRspCmd->attrList[i].status = *pBuf++; cfgReportRspCmd->attrList[i].direction = *pBuf++; cfgReportRspCmd->attrList[i].attrID = BUILD_UINT16( pBuf[0], pBuf[1] ); pBuf += 2; } } return ( (void *)cfgReportRspCmd ); } /********************************************************************* * @fn zclParseInReadReportCfgCmd * * @brief Parse the "Profile" Read Reporting Configuration Command * * NOTE: THIS FUNCTION ALLOCATES THE RETURN BUFFER, SO THE CALLING * FUNCTION IS RESPONSIBLE TO FREE THE MEMORY. * * @param pCmd - pointer to incoming data to parse * * @return pointer to the parsed command structure */ void *zclParseInReadReportCfgCmd( zclParseCmd_t *pCmd ) { zclReadReportCfgCmd_t *readReportCfgCmd; uint8 *pBuf = pCmd->pData; uint8 numAttr; numAttr = pCmd->dataLen / ( 1 + 2 ); // Direction + Attribute ID readReportCfgCmd = (zclReadReportCfgCmd_t *)osal_mem_alloc( sizeof( zclReadReportCfgCmd_t ) + ( numAttr * sizeof( zclReadReportCfgRec_t ) ) ); if ( readReportCfgCmd != NULL ) { readReportCfgCmd->numAttr = numAttr; for ( uint8 i = 0; i < readReportCfgCmd->numAttr; i++) { readReportCfgCmd->attrList[i].direction = *pBuf++;; readReportCfgCmd->attrList[i].attrID = BUILD_UINT16( pBuf[0], pBuf[1] ); pBuf += 2; } } return ( (void *)readReportCfgCmd ); } /********************************************************************* * @fn zclParseInReadReportCfgRspCmd * * @brief Parse the "Profile" Read Reporting Configuration Response Command * * NOTE: THIS FUNCTION ALLOCATES THE RETURN BUFFER, SO THE CALLING * FUNCTION IS RESPONSIBLE TO FREE THE MEMORY. * * @param pCmd - pointer to incoming data to parse * * @return pointer to the parsed command structure */ static void *zclParseInReadReportCfgRspCmd( zclParseCmd_t *pCmd ) { zclReadReportCfgRspCmd_t *readReportCfgRspCmd; uint8 reportChangeLen; uint8 *pBuf = pCmd->pData; uint8 *dataPtr; uint8 numAttr = 0; uint8 hdrLen; uint16 dataLen = 0; // Calculate the length of the response command while ( pBuf < ( pCmd->pData + pCmd->dataLen ) ) { uint8 status; uint8 direction; numAttr++; status = *pBuf++; direction = *pBuf++; pBuf += 2; // move pass the attribute ID if ( status == ZCL_STATUS_SUCCESS ) { if ( direction == ZCL_SEND_ATTR_REPORTS ) { uint8 dataType = *pBuf++; pBuf += 4; // move pass the Min and Max Reporting Intervals // For attributes of 'discrete' data types this field is omitted if ( zclAnalogDataType( dataType ) ) { reportChangeLen = zclGetDataTypeLength( dataType ); pBuf += reportChangeLen; // add padding if needed if ( PADDING_NEEDED( reportChangeLen ) ) { reportChangeLen++; } dataLen += reportChangeLen; } } else { pBuf += 2; // move pass the Timeout field } } } // while loop hdrLen = sizeof( zclReadReportCfgRspCmd_t ) + ( numAttr * sizeof( zclReportCfgRspRec_t ) ); readReportCfgRspCmd = (zclReadReportCfgRspCmd_t *)osal_mem_alloc( hdrLen + dataLen ); if ( readReportCfgRspCmd != NULL ) { pBuf = pCmd->pData; dataPtr = (uint8 *)( (uint8 *)readReportCfgRspCmd + hdrLen ); readReportCfgRspCmd->numAttr = numAttr; for ( uint8 i = 0; i < numAttr; i++ ) { zclReportCfgRspRec_t *reportRspRec = &(readReportCfgRspCmd->attrList[i]); reportRspRec->status = *pBuf++; reportRspRec->direction = *pBuf++; reportRspRec->attrID = BUILD_UINT16( pBuf[0], pBuf[1] ); pBuf += 2; if ( reportRspRec->status == ZCL_STATUS_SUCCESS ) { if ( reportRspRec->direction == ZCL_SEND_ATTR_REPORTS ) { reportRspRec->dataType = *pBuf++; reportRspRec->minReportInt = BUILD_UINT16( pBuf[0], pBuf[1] ); pBuf += 2; reportRspRec->maxReportInt = BUILD_UINT16( pBuf[0], pBuf[1] ); pBuf += 2; if ( zclAnalogDataType( reportRspRec->dataType ) ) { zcl_BuildAnalogData( reportRspRec->dataType, dataPtr, pBuf); reportRspRec->reportableChange = dataPtr; reportChangeLen = zclGetDataTypeLength( reportRspRec->dataType ); pBuf += reportChangeLen; // advance attribute data pointer if ( PADDING_NEEDED( reportChangeLen ) ) { reportChangeLen++; } dataPtr += reportChangeLen; } } else { reportRspRec->timeoutPeriod = BUILD_UINT16( pBuf[0], pBuf[1] ); pBuf += 2; } } } } return ( (void *)readReportCfgRspCmd ); } /********************************************************************* * @fn zclParseInReportCmd * * @brief Parse the "Profile" Report Command * * NOTE: THIS FUNCTION ALLOCATES THE RETURN BUFFER, SO THE CALLING * FUNCTION IS RESPONSIBLE TO FREE THE MEMORY. * * @param pCmd - pointer to incoming data to parse * * @return pointer to the parsed command structure */ void *zclParseInReportCmd( zclParseCmd_t *pCmd ) { zclReportCmd_t *reportCmd; uint8 *pBuf = pCmd->pData; uint16 attrDataLen; uint8 *dataPtr; uint8 numAttr = 0; uint8 hdrLen; uint16 dataLen = 0; // find out the number of attributes and the length of attribute data while ( pBuf < ( pCmd->pData + pCmd->dataLen ) ) { uint8 dataType; numAttr++; pBuf += 2; // move pass attribute id dataType = *pBuf++; attrDataLen = zclGetAttrDataLength( dataType, pBuf ); pBuf += attrDataLen; // move pass attribute data // add padding if needed if ( PADDING_NEEDED( attrDataLen ) ) { attrDataLen++; } dataLen += attrDataLen; } hdrLen = sizeof( zclReportCmd_t ) + ( numAttr * sizeof( zclReport_t ) ); reportCmd = (zclReportCmd_t *)osal_mem_alloc( hdrLen + dataLen ); if (reportCmd != NULL ) { pBuf = pCmd->pData; dataPtr = (uint8 *)( (uint8 *)reportCmd + hdrLen ); reportCmd->numAttr = numAttr; for ( uint8 i = 0; i < numAttr; i++ ) { zclReport_t *reportRec = &(reportCmd->attrList[i]); reportRec->attrID = BUILD_UINT16( pBuf[0], pBuf[1] ); pBuf += 2; reportRec->dataType = *pBuf++; attrDataLen = zclGetAttrDataLength( reportRec->dataType, pBuf ); osal_memcpy( dataPtr, pBuf, attrDataLen ); reportRec->attrData = dataPtr; pBuf += attrDataLen; // move pass attribute data // advance attribute data pointer if ( PADDING_NEEDED( attrDataLen ) ) { attrDataLen++; } dataPtr += attrDataLen; } } return ( (void *)reportCmd ); } #endif // ZCL_REPORT /********************************************************************* * @fn zclParseInDefaultRspCmd * * @brief Parse the "Profile" Default Response Command * * NOTE: THIS FUNCTION ALLOCATES THE RETURN BUFFER, SO THE CALLING * FUNCTION IS RESPONSIBLE TO FREE THE MEMORY. * * @param pCmd - pointer to incoming data to parse * * @return pointer to the parsed command structure */ static void *zclParseInDefaultRspCmd( zclParseCmd_t *pCmd ) { zclDefaultRspCmd_t *defaultRspCmd; uint8 *pBuf = pCmd->pData; defaultRspCmd = (zclDefaultRspCmd_t *)osal_mem_alloc( sizeof ( zclDefaultRspCmd_t ) ); if ( defaultRspCmd != NULL ) { defaultRspCmd->commandID = *pBuf++; defaultRspCmd->statusCode = *pBuf; } return ( (void *)defaultRspCmd ); } #ifdef ZCL_DISCOVER /********************************************************************* * @fn zclParseInDiscCmd * * @brief Parse the "Profile" Discovery Commands * * NOTE: THIS FUNCTION ALLOCATES THE RETURN BUFFER, SO THE CALLING * FUNCTION IS RESPONSIBLE TO FREE THE MEMORY. * * @param pCmd - pointer to incoming data to parse * * @return pointer to the parsed command structure */ void *zclParseInDiscCmd( zclParseCmd_t *pCmd ) { zclDiscoverCmd_t *discoverCmd; uint8 *pBuf = pCmd->pData; discoverCmd = (zclDiscoverCmd_t *)osal_mem_alloc( sizeof ( zclDiscoverCmd_t ) ); if ( discoverCmd != NULL ) { discoverCmd->startAttr = BUILD_UINT16( pBuf[0], pBuf[1] ); pBuf += 2; discoverCmd->maxAttrIDs = *pBuf; } return ( (void *)discoverCmd ); } /********************************************************************* * @fn zclParseInDiscRspCmd * * @brief Parse the "Profile" Discovery Response Commands * * NOTE: THIS FUNCTION ALLOCATES THE RETURN BUFFER, SO THE CALLING * FUNCTION IS RESPONSIBLE TO FREE THE MEMORY. * * @param pCmd - pointer to incoming data to parse * * @return pointer to the parsed command structure */ #define ZCLDISCRSPCMD_DATALEN(a) ((a)-1) // data len - Discovery Complete static void *zclParseInDiscRspCmd( zclParseCmd_t *pCmd ) { zclDiscoverRspCmd_t *discoverRspCmd; uint8 *pBuf = pCmd->pData; uint8 numAttr = ZCLDISCRSPCMD_DATALEN(pCmd->dataLen) / ( 2 + 1 ); // Attr ID + Data Type discoverRspCmd = (zclDiscoverRspCmd_t *)osal_mem_alloc( sizeof ( zclDiscoverRspCmd_t ) + ( numAttr * sizeof(zclDiscoverInfo_t) ) ); if ( discoverRspCmd != NULL ) { discoverRspCmd->discComplete = *pBuf++; discoverRspCmd->numAttr = numAttr; for ( uint8 i = 0; i < numAttr; i++ ) { discoverRspCmd->attrList[i].attrID = BUILD_UINT16( pBuf[0], pBuf[1] ); pBuf += 2; discoverRspCmd->attrList[i].dataType = *pBuf++;; } } return ( (void *)discoverRspCmd ); } #endif // ZCL_DISCOVER #ifdef ZCL_READ /********************************************************************* * @fn zclProcessInReadCmd * * @brief Process the "Profile" Read Command * * @param pInMsg - incoming message to process * * @return TRUE if command processed. FALSE, otherwise. */ static uint8 zclProcessInReadCmd( zclIncoming_t *pInMsg ) { zclReadCmd_t *readCmd; zclReadRspCmd_t *readRspCmd; zclAttrRec_t attrRec; uint16 len; readCmd = (zclReadCmd_t *)pInMsg->attrCmd; // calculate the length of the response status record len = sizeof( zclReadRspCmd_t ) + (readCmd->numAttr * sizeof( zclReadRspStatus_t )); readRspCmd = osal_mem_alloc( len ); if ( readRspCmd == NULL ) { return FALSE; // EMBEDDED RETURN } readRspCmd->numAttr = readCmd->numAttr; for ( uint8 i = 0; i < readCmd->numAttr; i++ ) { zclReadRspStatus_t *statusRec = &(readRspCmd->attrList[i]); statusRec->attrID = readCmd->attrID[i]; if ( zclFindAttrRec( pInMsg->msg->endPoint, pInMsg->msg->clusterId, readCmd->attrID[i], &attrRec ) ) { if ( zcl_AccessCtrlRead( attrRec.attr.accessControl ) ) { statusRec->status = zclAuthorizeRead( pInMsg->msg->endPoint, &(pInMsg->msg->srcAddr), &attrRec ); if ( statusRec->status == ZCL_STATUS_SUCCESS ) { statusRec->data = attrRec.attr.dataPtr; statusRec->dataType = attrRec.attr.dataType; } } else { statusRec->status = ZCL_STATUS_WRITE_ONLY; } } else { statusRec->status = ZCL_STATUS_UNSUPPORTED_ATTRIBUTE; } } // Build and send Read Response command zcl_SendReadRsp( pInMsg->msg->endPoint, &(pInMsg->msg->srcAddr), pInMsg->msg->clusterId, readRspCmd, ZCL_FRAME_SERVER_CLIENT_DIR, true, pInMsg->hdr.transSeqNum ); osal_mem_free( readRspCmd ); return TRUE; } #endif // ZCL_READ #ifdef ZCL_WRITE /********************************************************************* * @fn processInWriteCmd * * @brief Process the "Profile" Write and Write No Response Commands * * @param pInMsg - incoming message to process * * @return TRUE if command processed. FALSE, otherwise. */ static uint8 zclProcessInWriteCmd( zclIncoming_t *pInMsg ) { zclWriteCmd_t *writeCmd; zclWriteRspCmd_t *writeRspCmd; uint8 sendRsp = FALSE; uint8 j = 0; writeCmd = (zclWriteCmd_t *)pInMsg->attrCmd; if ( pInMsg->hdr.commandID == ZCL_CMD_WRITE ) { // We need to send a response back - allocate space for it writeRspCmd = (zclWriteRspCmd_t *)osal_mem_alloc( sizeof( zclWriteRspCmd_t ) + sizeof( zclWriteRspStatus_t ) * writeCmd->numAttr ); if ( writeRspCmd == NULL ) { return FALSE; // EMBEDDED RETURN } sendRsp = TRUE; } for ( uint8 i = 0; i < writeCmd->numAttr; i++ ) { zclAttrRec_t attrRec; zclWriteRec_t *statusRec = &(writeCmd->attrList[i]); if ( zclFindAttrRec( pInMsg->msg->endPoint, pInMsg->msg->clusterId, statusRec->attrID, &attrRec ) ) { if ( statusRec->dataType == attrRec.attr.dataType ) { uint8 status; // Write the new attribute value if ( attrRec.attr.dataPtr != NULL ) { status = zclWriteAttrData( pInMsg->msg->endPoint, &(pInMsg->msg->srcAddr), &attrRec, statusRec ); } else // Use CB { status = zclWriteAttrDataUsingCB( pInMsg->msg->endPoint, &(pInMsg->msg->srcAddr), &attrRec, statusRec->attrData ); } // If successful, a write attribute status record shall NOT be generated if ( sendRsp && status != ZCL_STATUS_SUCCESS ) { // Attribute is read only - move on to the next write attribute record writeRspCmd->attrList[j].status = status; writeRspCmd->attrList[j++].attrID = statusRec->attrID; } } else { // Attribute data type is incorrect - move on to the next write attribute record if ( sendRsp ) { writeRspCmd->attrList[j].status = ZCL_STATUS_INVALID_DATA_TYPE; writeRspCmd->attrList[j++].attrID = statusRec->attrID; } } } else { // Attribute is not supported - move on to the next write attribute record if ( sendRsp ) { writeRspCmd->attrList[j].status = ZCL_STATUS_UNSUPPORTED_ATTRIBUTE; writeRspCmd->attrList[j++].attrID = statusRec->attrID; } } } // for loop if ( sendRsp ) { writeRspCmd->numAttr = j; if ( writeRspCmd->numAttr == 0 ) { // Since all records were written successful, include a single status record // in the resonse command with the status field set to SUCCESS and the // attribute ID field omitted. writeRspCmd->attrList[0].status = ZCL_STATUS_SUCCESS; writeRspCmd->numAttr = 1; } zcl_SendWriteRsp( pInMsg->msg->endPoint, &(pInMsg->msg->srcAddr), pInMsg->msg->clusterId, writeRspCmd, ZCL_FRAME_SERVER_CLIENT_DIR, true, pInMsg->hdr.transSeqNum ); osal_mem_free( writeRspCmd ); } return TRUE; } /********************************************************************* * @fn zclRevertWriteUndividedCmd * * @brief Revert the "Profile" Write Undevided Command * * @param pInMsg - incoming message to process * @param curWriteRec - old data * @param numAttr - number of attributes to be reverted * * @return none */ static void zclRevertWriteUndividedCmd( zclIncoming_t *pInMsg, zclWriteRec_t *curWriteRec, uint16 numAttr ) { for ( uint8 i = 0; i < numAttr; i++ ) { zclAttrRec_t attrRec; zclWriteRec_t *statusRec = &(curWriteRec[i]); if ( !zclFindAttrRec( pInMsg->msg->endPoint, pInMsg->msg->clusterId, statusRec->attrID, &attrRec ) ) { break; // should never happen } if ( attrRec.attr.dataPtr != NULL ) { // Just copy the old data back - no need to validate the data uint16 dataLen = zclGetAttrDataLength( attrRec.attr.dataType, statusRec->attrData ); osal_memcpy( attrRec.attr.dataPtr, statusRec->attrData, dataLen ); } else // Use CB { // Write the old data back zclWriteAttrDataUsingCB( pInMsg->msg->endPoint, &(pInMsg->msg->srcAddr), &attrRec, statusRec->attrData ); } } // for loop } /********************************************************************* * @fn zclProcessInWriteUndividedCmd * * @brief Process the "Profile" Write Undivided Command * * @param pInMsg - incoming message to process * * @return TRUE if command processed. FALSE, otherwise. */ static uint8 zclProcessInWriteUndividedCmd( zclIncoming_t *pInMsg ) { zclWriteCmd_t *writeCmd; zclWriteRspCmd_t *writeRspCmd; zclAttrRec_t attrRec; uint16 dataLen; uint16 curLen = 0; uint8 j = 0; writeCmd = (zclWriteCmd_t *)pInMsg->attrCmd; // Allocate space for Write Response Command writeRspCmd = (zclWriteRspCmd_t *)osal_mem_alloc( sizeof( zclWriteRspCmd_t ) + sizeof( zclWriteRspStatus_t )* writeCmd->numAttr ); if ( writeRspCmd == NULL ) { return FALSE; // EMBEDDED RETURN } // If any attribute cannot be written, no attribute values are changed. Hence, // make sure all the attributes are supported and writable for ( uint8 i = 0; i < writeCmd->numAttr; i++ ) { zclWriteRec_t *statusRec = &(writeCmd->attrList[i]); if ( !zclFindAttrRec( pInMsg->msg->endPoint, pInMsg->msg->clusterId, statusRec->attrID, &attrRec ) ) { // Attribute is not supported - stop here writeRspCmd->attrList[j].status = ZCL_STATUS_UNSUPPORTED_ATTRIBUTE; writeRspCmd->attrList[j++].attrID = statusRec->attrID; break; } if ( statusRec->dataType != attrRec.attr.dataType ) { // Attribute data type is incorrect - stope here writeRspCmd->attrList[j].status = ZCL_STATUS_INVALID_DATA_TYPE; writeRspCmd->attrList[j++].attrID = statusRec->attrID; break; } if ( !zcl_AccessCtrlWrite( attrRec.attr.accessControl ) ) { // Attribute is not writable - stop here writeRspCmd->attrList[j].status = ZCL_STATUS_READ_ONLY; writeRspCmd->attrList[j++].attrID = statusRec->attrID; break; } if ( zcl_AccessCtrlAuthWrite( attrRec.attr.accessControl ) ) { // Not authorized to write - stop here writeRspCmd->attrList[j].status = ZCL_STATUS_NOT_AUTHORIZED; writeRspCmd->attrList[j++].attrID = statusRec->attrID; break; } // Attribute Data length if ( attrRec.attr.dataPtr != NULL ) { dataLen = zclGetAttrDataLength( attrRec.attr.dataType, attrRec.attr.dataPtr ); } else // Use CB { dataLen = zclGetAttrDataLengthUsingCB( pInMsg->msg->endPoint, pInMsg->msg->clusterId, statusRec->attrID ); } // add padding if needed if ( PADDING_NEEDED( dataLen ) ) { dataLen++; } curLen += dataLen; } // for loop writeRspCmd->numAttr = j; if ( writeRspCmd->numAttr == 0 ) // All attributes can be written { uint8 *curDataPtr; zclWriteRec_t *curWriteRec; // calculate the length of the current data header uint8 hdrLen = j * sizeof( zclWriteRec_t ); // Allocate space to keep a copy of the current data curWriteRec = (zclWriteRec_t *) osal_mem_alloc( hdrLen + curLen ); if ( curWriteRec == NULL ) { osal_mem_free(writeRspCmd ); return FALSE; // EMBEDDED RETURN } curDataPtr = (uint8 *)((uint8 *)curWriteRec + hdrLen); // Write the new data over for ( uint8 i = 0; i < writeCmd->numAttr; i++ ) { uint8 status; zclWriteRec_t *statusRec = &(writeCmd->attrList[i]); zclWriteRec_t *curStatusRec = &(curWriteRec[i]); if ( !zclFindAttrRec( pInMsg->msg->endPoint, pInMsg->msg->clusterId, statusRec->attrID, &attrRec ) ) { break; // should never happen } // Keep a copy of the current data before before writing the new data over curStatusRec->attrID = statusRec->attrID; curStatusRec->attrData = curDataPtr; if ( attrRec.attr.dataPtr != NULL ) { // Read the current value zclReadAttrData( curDataPtr, &attrRec, &dataLen ); // Write the new attribute value status = zclWriteAttrData( pInMsg->msg->endPoint, &(pInMsg->msg->srcAddr), &attrRec, statusRec ); } else // Use CBs { // Read the current value zclReadAttrDataUsingCB( pInMsg->msg->endPoint, pInMsg->msg->clusterId, statusRec->attrID, curDataPtr, &dataLen ); // Write the new attribute value status = zclWriteAttrDataUsingCB( pInMsg->msg->endPoint, &(pInMsg->msg->srcAddr), &attrRec, statusRec->attrData ); } // If successful, a write attribute status record shall NOT be generated if ( status != ZCL_STATUS_SUCCESS ) { writeRspCmd->attrList[j].status = status; writeRspCmd->attrList[j++].attrID = statusRec->attrID; // Since this write failed, we need to revert all the pervious writes zclRevertWriteUndividedCmd( pInMsg, curWriteRec, i); break; } // add padding if needed if ( PADDING_NEEDED( dataLen ) ) { dataLen++; } curDataPtr += dataLen; } // for loop writeRspCmd->numAttr = j; if ( writeRspCmd->numAttr == 0 ) { // Since all records were written successful, include a single status record // in the resonse command with the status field set to SUCCESS and the // attribute ID field omitted. writeRspCmd->attrList[0].status = ZCL_STATUS_SUCCESS; writeRspCmd->numAttr = 1; } osal_mem_free( curWriteRec ); } zcl_SendWriteRsp( pInMsg->msg->endPoint, &(pInMsg->msg->srcAddr), pInMsg->msg->clusterId, writeRspCmd, ZCL_FRAME_SERVER_CLIENT_DIR, true, pInMsg->hdr.transSeqNum ); osal_mem_free( writeRspCmd ); return TRUE; } #endif // ZCL_WRITE #ifdef ZCL_DISCOVER /********************************************************************* * @fn zclProcessInDiscCmd * * @brief Process the "Profile" Discover Command * * @param pInMsg - incoming message to process * * @return TRUE if command processed. FALSE, otherwise. */ static uint8 zclProcessInDiscCmd( zclIncoming_t *pInMsg ) { zclDiscoverCmd_t *discoverCmd; zclDiscoverRspCmd_t *discoverRspCmd; uint8 discComplete = TRUE; zclAttrRec_t attrRec; uint16 attrID; uint8 i; discoverCmd = (zclDiscoverCmd_t *)pInMsg->attrCmd; // Find out the number of attributes supported within the specified range for ( i = 0, attrID = discoverCmd->startAttr; i < discoverCmd->maxAttrIDs; i++, attrID++ ) { if ( !zclFindNextAttrRec( pInMsg->msg->endPoint, pInMsg->msg->clusterId, &attrID, &attrRec ) ) { break; } } // Allocate space for the response command discoverRspCmd = (zclDiscoverRspCmd_t *)osal_mem_alloc( sizeof (zclDiscoverRspCmd_t) + sizeof ( zclDiscoverInfo_t ) * i ); if ( discoverRspCmd == NULL ) { return FALSE; // EMEDDED RETURN } discoverRspCmd->numAttr = i; if ( discoverRspCmd->numAttr != 0 ) { for ( i = 0, attrID = discoverCmd->startAttr; i < discoverRspCmd->numAttr; i++, attrID++ ) { if ( !zclFindNextAttrRec( pInMsg->msg->endPoint, pInMsg->msg->clusterId, &attrID, &attrRec ) ) { break; // Attribute not supported } discoverRspCmd->attrList[i].attrID = attrRec.attr.attrId; discoverRspCmd->attrList[i].dataType = attrRec.attr.dataType; } // Are there more attributes to be discovered? if ( zclFindNextAttrRec( pInMsg->msg->endPoint, pInMsg->msg->clusterId, &attrID, &attrRec ) ) { discComplete = FALSE; } } discoverRspCmd->discComplete = discComplete; zcl_SendDiscoverRspCmd( pInMsg->msg->endPoint, &pInMsg->msg->srcAddr, pInMsg->msg->clusterId, discoverRspCmd, ZCL_FRAME_SERVER_CLIENT_DIR, true, pInMsg->hdr.transSeqNum ); osal_mem_free( discoverRspCmd ); return TRUE; } #endif // ZCL_DISCOVER /********************************************************************* * @fn zclSendMsg * * @brief Send an incoming message to the Application * * @param pInMsg - incoming message to process * * @return TRUE */ static uint8 zclSendMsg( zclIncoming_t *pInMsg ) { zclIncomingMsg_t *pCmd; if ( zcl_RegisteredMsgTaskID == TASK_NO_TASK ) { return ( TRUE ); } pCmd = (zclIncomingMsg_t *)osal_msg_allocate( sizeof ( zclIncomingMsg_t ) ); if ( pCmd != NULL ) { // fill in the message pCmd->hdr.event = ZCL_INCOMING_MSG; pCmd->zclHdr = pInMsg->hdr; pCmd->clusterId = pInMsg->msg->clusterId; pCmd->srcAddr = pInMsg->msg->srcAddr; pCmd->endPoint = pInMsg->msg->endPoint; pCmd->attrCmd = pInMsg->attrCmd; // Application will free the attrCmd buffer pInMsg->attrCmd = NULL; /* send message through task message */ osal_msg_send( zcl_RegisteredMsgTaskID, (uint8 *)pCmd ); } return ( TRUE ); } /********************************************************************* *********************************************************************/