/****************************************************************************** Filename: zcl_ota.c Revised: $Date: 2011-07-18 05:10:28 -0700 (Mon, 18 Jul 2011) $ Revision: $Revision: 26827 $ Description: Zigbee Cluster Library - Over-the-Air Upgrade Cluster ( OTA ) Copyright 2010-2011 Texas Instruments Incorporated. All rights reserved. IMPORTANT: Your use of this Software is limited to those specific rights granted under the terms of a software license agreement between the user who downloaded the software, his/her employer (which must be your employer) and Texas Instruments Incorporated (the "License"). You may not use this Software unless you agree to abide by the terms of the License. The License limits your use, and you acknowledge, that the Software may not be modified, copied or distributed unless embedded on a Texas Instruments microcontroller or used solely and exclusively in conjunction with a Texas Instruments radio frequency transceiver, which is integrated into your product. Other than for the foregoing purpose, you may not use, reproduce, copy, prepare derivative works of, modify, distribute, perform, display or sell this Software and/or its documentation for any purpose. YOU FURTHER ACKNOWLEDGE AND AGREE THAT THE SOFTWARE AND DOCUMENTATION ARE PROVIDED “AS IS” WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION, ANY WARRANTY OF MERCHANTABILITY, TITLE, NON-INFRINGEMENT AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL TEXAS INSTRUMENTS OR ITS LICENSORS BE LIABLE OR OBLIGATED UNDER CONTRACT, NEGLIGENCE, STRICT LIABILITY, CONTRIBUTION, BREACH OF WARRANTY, OR OTHER LEGAL EQUITABLE THEORY ANY DIRECT OR INDIRECT DAMAGES OR EXPENSES INCLUDING BUT NOT LIMITED TO ANY INCIDENTAL, SPECIAL, INDIRECT, PUNITIVE OR CONSEQUENTIAL DAMAGES, LOST PROFITS OR LOST DATA, COST OF PROCUREMENT OF SUBSTITUTE GOODS, TECHNOLOGY, SERVICES, OR ANY CLAIMS BY THIRD PARTIES (INCLUDING BUT NOT LIMITED TO ANY DEFENSE THEREOF), OR OTHER SIMILAR COSTS. Should you have any questions regarding your right to use this Software, contact Texas Instruments Incorporated at www.TI.com. ******************************************************************************/ /****************************************************************************** * INCLUDES */ #include "ZComDef.h" #include "OSAL.h" #include "zcl.h" #include "zcl_general.h" #include "zcl_ota.h" #include "ota_common.h" #include "hal_lcd.h" #include "hal_led.h" #include "hal_ota.h" #include "MT_OTA.h" #include "ZDProfile.h" #include "ZDObject.h" #if defined ( INTER_PAN ) #include "stub_aps.h" #endif #if defined OTA_MMO_SIGN #include "ota_signature.h" #endif /****************************************************************************** * MACROS */ /****************************************************************************** * CONSTANTS */ #define OTA_MAX_TRANSACTIONS 4 #define OTA_TRANSACTION_EXPIRATION 1500 #define ZCL_OTA_HDR_LEN_OFFSET 6 // Header length location in OTA upgrade image #define ZCL_OTA_STK_VER_OFFSET 18 // Stack version location in OTA upgrade image /****************************************************************************** * GLOBAL VARIABLES */ // OTA attribute variables uint8 zclOTA_UpgradeServerID[Z_EXTADDR_LEN]; uint32 zclOTA_FileOffset = 0xFFFFFFFF; uint32 zclOTA_CurrentFileVersion = 0xFFFFFFFF; uint16 zclOTA_CurrentZigBeeStackVersion; uint32 zclOTA_DownloadedFileVersion = 0xFFFFFFFF; uint16 zclOTA_DownloadedZigBeeStackVersion = 0xFFFF; uint8 zclOTA_ImageUpgradeStatus; // Other OTA variables uint16 zclOTA_ManufacturerId; // Manufacturer ID uint16 zclOTA_ImageType; // Image type afAddrType_t zclOTA_serverAddr; // Server address uint8 zclOTA_AppTask = 0xFF; // Callback Task ID zclOTA_QueryImageRspParams_t queryResponse; // Global variable for sent query response /****************************************************************************** * LOCAL VARIABLES */ // Other OTA variables // Task ID static uint8 zclOTA_TaskID; // Sequence number static uint8 zclOTA_SeqNo = 0; static uint8 zclOTA_Permit = TRUE; #if defined OTA_MMO_SIGN static OTA_MmoCtrl_t zclOTA_MmoHash; static uint8 zclOTA_DataToHash[OTA_MMO_HASH_SIZE]; static uint8 zclOTA_HashPos; static uint8 zclOTA_SignerIEEE[Z_EXTADDR_LEN]; static uint8 zclOTA_SignatureData[OTA_SIGNATURE_LEN]; static uint8 zclOTA_Certificate[OTA_CERTIFICATE_LEN]; #endif // OTA_MMO_SIGN #if (defined OTA_CLIENT) && (OTA_CLIENT == TRUE) static uint32 zclOTA_DownloadedImageSize; // Downloaded image size static uint16 zclOTA_HeaderLen; // Image header length static uint16 zclOTA_UpdateDelay; static zclOTA_FileID_t zclOTA_CurrentDlFileId; static uint16 zclOTA_ElementTag; static uint32 zclOTA_ElementLen; static uint32 zclOTA_ElementPos; // Retry counters static uint8 zclOTA_BlockRetry; static uint8 zclOTA_UpgradeEndRetry; static uint8 zclOTA_ClientPdState; // OTA Header Magic Number Bytes static const uint8 zclOTA_HdrMagic[] = {0x1E, 0xF1, 0xEE, 0x0B}; #endif // OTA_CLIENT /****************************************************************************** * LOCAL FUNCTIONS */ static ZStatus_t zclOTA_HdlIncoming( zclIncoming_t *pInMsg ); #if (defined OTA_CLIENT) && (OTA_CLIENT == TRUE) static void zclOTA_StartTimer(uint16 eventId, uint32 minutes); static ZStatus_t sendImageBlockReq(afAddrType_t *dstAddr); static void zclOTA_ProcessZDOMsgs( zdoIncomingMsg_t *pMsg ); static void zclOTA_ImageBlockWaitExpired(void); static void zclOTA_UpgradeComplete(uint8 status); static uint8 zclOTA_CmpFileId(zclOTA_FileID_t *f1, zclOTA_FileID_t *f2); static uint8 zclOTA_ProcessImageData(uint8 *pData, uint8 len); static ZStatus_t zclOTA_SendQueryNextImageReq( afAddrType_t *dstAddr, zclOTA_QueryNextImageReqParams_t *pParams ); static ZStatus_t zclOTA_SendImageBlockReq( afAddrType_t *dstAddr, zclOTA_ImageBlockReqParams_t *pParams ); static ZStatus_t zclOTA_SendUpgradeEndReq( afAddrType_t *dstAddr, zclOTA_UpgradeEndReqParams_t *pParams ); static ZStatus_t zclOTA_ClientHdlIncoming( zclIncoming_t *pInMsg ); #endif // OTA_CLIENT #if (defined OTA_SERVER) && (OTA_SERVER == TRUE) static ZStatus_t zclOTA_SendQueryNextImageRsp( afAddrType_t *dstAddr, zclOTA_QueryImageRspParams_t *pParams ); static ZStatus_t zclOTA_SendImageBlockRsp( afAddrType_t *dstAddr, zclOTA_ImageBlockRspParams_t *pParams ); static ZStatus_t zclOTA_SendUpgradeEndRsp( afAddrType_t *dstAddr, zclOTA_UpgradeEndRspParams_t *pParams ); static ZStatus_t zclOTA_SendQuerySpecificFileRsp( afAddrType_t *dstAddr, zclOTA_QueryImageRspParams_t *pParams ); static ZStatus_t zclOTA_Srv_QueryNextImageReq(afAddrType_t *pSrcAddr, zclOTA_QueryNextImageReqParams_t *pParam); static ZStatus_t zclOTA_Srv_ImageBlockReq(afAddrType_t *pSrcAddr, zclOTA_ImageBlockReqParams_t *pParam); static ZStatus_t zclOTA_Srv_ImagePageReq(afAddrType_t *pSrcAddr, zclOTA_ImagePageReqParams_t *pParam); static ZStatus_t zclOTA_Srv_UpgradeEndReq(afAddrType_t *pSrcAddr, zclOTA_UpgradeEndReqParams_t *pParam); static ZStatus_t zclOTA_Srv_QuerySpecificFileReq(afAddrType_t *pSrcAddr, zclOTA_QuerySpecificFileReqParams_t *pParam); static void zclOTA_ProcessNextImgRsp(uint8* pMSGpkt, zclOTA_FileID_t *pFileId, afAddrType_t *pAddr); static void zclOTA_ProcessFileReadRsp(uint8* pMSGpkt, zclOTA_FileID_t *pFileId, afAddrType_t *pAddr); static void zclOTA_ServerHandleFileSysCb(OTA_MtMsg_t* pMSGpkt); static ZStatus_t zclOTA_ServerHdlIncoming( zclIncoming_t *pInMsg ); #endif // OTA_SERVER /****************************************************************************** * OTA ATTRIBUTE DEFINITIONS - Uses REAL cluster IDs */ #define ZCL_OTA_MAX_ATTRIBUTES 7 CONST zclAttrRec_t zclOTA_Attrs[ZCL_OTA_MAX_ATTRIBUTES] = { { ZCL_CLUSTER_ID_OTA, // Cluster IDs - defined in the foundation (ie. zcl.h) { // Attribute record ATTRID_UPGRADE_SERVER_ID, // Attribute ID - Found in Cluster Library header (ie. zcl_general.h) ZCL_DATATYPE_IEEE_ADDR, // Data Type - found in zcl.h ACCESS_CONTROL_READ, // Variable access control - found in zcl.h (void *)&zclOTA_UpgradeServerID // Pointer to attribute variable } }, { ZCL_CLUSTER_ID_OTA, { // Attribute record ATTRID_FILE_OFFSET, ZCL_DATATYPE_UINT32, ACCESS_CONTROL_READ, (void *)&zclOTA_FileOffset } }, { ZCL_CLUSTER_ID_OTA, { // Attribute record ATTRID_CURRENT_FILE_VERSION, ZCL_DATATYPE_UINT32, ACCESS_CONTROL_READ, (void *)&zclOTA_CurrentFileVersion } }, { ZCL_CLUSTER_ID_OTA, { // Attribute record ATTRID_CURRENT_ZIGBEE_STACK_VERSION, ZCL_DATATYPE_UINT16, ACCESS_CONTROL_READ, (void *)&zclOTA_CurrentZigBeeStackVersion } }, { ZCL_CLUSTER_ID_OTA, { // Attribute record ATTRID_DOWNLOADED_FILE_VERSION, ZCL_DATATYPE_UINT32, ACCESS_CONTROL_READ, (void *)&zclOTA_DownloadedFileVersion } }, { ZCL_CLUSTER_ID_OTA, { // Attribute record ATTRID_DOWNLOADED_ZIGBEE_STACK_VERSION, ZCL_DATATYPE_UINT16, ACCESS_CONTROL_READ, (void *)&zclOTA_DownloadedZigBeeStackVersion } }, { ZCL_CLUSTER_ID_OTA, { // Attribute record ATTRID_IMAGE_UPGRADE_STATUS, ZCL_DATATYPE_UINT8, ACCESS_CONTROL_READ, (void *)&zclOTA_ImageUpgradeStatus } } }; /****************************************************************************** * OTA SIMPLE DESCRIPTOR */ #ifndef OTA_HA #define ZCL_OTA_MAX_OPTIONS 1 zclOptionRec_t zclOta_Options[ZCL_OTA_MAX_OPTIONS] = { { ZCL_CLUSTER_ID_OTA, ( AF_EN_SECURITY | AF_ACK_REQUEST ), }, }; #endif // OTA_HA #if defined OTA_SERVER && (OTA_SERVER == TRUE) // Cluster ID list for match descriptor of the OTA Server. #define ZCL_OTA_MAX_INCLUSTERS 1 const cId_t zclOTA_InClusterList[ZCL_OTA_MAX_INCLUSTERS] = { ZCL_CLUSTER_ID_OTA }; #else #define ZCL_OTA_MAX_INCLUSTERS 0 #define zclOTA_InClusterList NULL #endif // OTA_SERVER #if defined OTA_CLIENT && (OTA_CLIENT == TRUE) // Cluster ID list for match descriptor of the OTA Client. #define ZCL_OTA_MAX_OUTCLUSTERS 1 const cId_t zclOTA_OutClusterList[ZCL_OTA_MAX_OUTCLUSTERS] = { ZCL_CLUSTER_ID_OTA }; #else #define ZCL_OTA_MAX_OUTCLUSTERS 0 #define zclOTA_OutClusterList NULL #endif // OTA_CLIENT SimpleDescriptionFormat_t zclOTA_SimpleDesc = { ZCL_OTA_ENDPOINT, // int Endpoint; ZCL_OTA_SAMPLE_PROFILE_ID, // uint16 AppProfId[2]; ZCL_OTA_SAMPLE_DEVICEID, // uint16 AppDeviceId[2]; ZCL_OTA_DEVICE_VERSION, // int AppDevVer:4; ZCL_OTA_FLAGS, // int AppFlags:4; ZCL_OTA_MAX_INCLUSTERS, // byte AppNumInClusters; (cId_t *)zclOTA_InClusterList, // byte *pAppInClusterList; ZCL_OTA_MAX_OUTCLUSTERS, // byte AppNumInClusters; (cId_t *)zclOTA_OutClusterList // byte *pAppInClusterList; }; // Endpoint for OTA Cluster static endPointDesc_t zclOTA_Ep = { ZCL_OTA_ENDPOINT, &zcl_TaskID, (SimpleDescriptionFormat_t *)&zclOTA_SimpleDesc, (afNetworkLatencyReq_t) 0 }; /****************************************************************************** * @fn zclOTA_PermitOta * * @brief Called to enable/disable OTA operation. * * @param permit - TRUE to enable OTA, FALSE to diable OTA * * @return none */ void zclOTA_PermitOta(uint8 permit) { zclOTA_Permit = permit; } /****************************************************************************** * @fn zclOTA_Register * * @brief Called by an application to register for callback messages * from the OTA. * * @param applicationTaskId - Application Task ID * * @return none */ void zclOTA_Register(uint8 applicationTaskId) { zclOTA_AppTask = applicationTaskId; } /****************************************************************************** * @fn zclOTA_RequestNextUpdate * * @brief Called by an application after discovery of the OTA server * to initiate the query next image of the OTA server. * * @param srvAddr - Short address of the server * @param srvEndPoint - Endpoint on the server * * @return ZStatus_t */ void zclOTA_RequestNextUpdate(uint16 srvAddr, uint8 srvEndPoint) { // Record the server address zclOTA_serverAddr.addrMode = afAddr16Bit; zclOTA_serverAddr.endPoint = srvEndPoint; zclOTA_serverAddr.addr.shortAddr = srvAddr; // Set an event to query the server osal_set_event(zclOTA_TaskID, ZCL_OTA_QUERY_SERVER_EVT); } /****************************************************************************** * @fn zclOTA_Init * * @brief Call to initialize the OTA Client Task * * @param task_id * * @return none */ void zclOTA_Init( uint8 task_id ) { zclOTA_TaskID = task_id; // Register for the cluster endpoint afRegister( &zclOTA_Ep ); // Register as a ZCL Plugin zcl_registerPlugin( ZCL_CLUSTER_ID_OTA, ZCL_CLUSTER_ID_OTA, zclOTA_HdlIncoming ); // Initialize attribute variables zclOTA_CurrentZigBeeStackVersion = OTA_STACK_VER_PRO; zclOTA_ImageUpgradeStatus = OTA_STATUS_NORMAL; // Register attribute list zcl_registerAttrList( ZCL_OTA_ENDPOINT, ZCL_OTA_MAX_ATTRIBUTES, zclOTA_Attrs ); #ifndef OTA_HA // Register the application's cluster option list zcl_registerClusterOptionList( ZCL_OTA_ENDPOINT, ZCL_OTA_MAX_OPTIONS, zclOta_Options ); #endif // OTA_HA // Register with the ZDO to receive Network Address Responses ZDO_RegisterForZDOMsg(task_id, IEEE_addr_rsp); // The default upgradeServerID is FF:FF:FF:FF:FF:FF:FF:FF osal_memset(zclOTA_UpgradeServerID, 0xFF, sizeof(zclOTA_UpgradeServerID)); #if defined (OTA_SERVER) && (OTA_SERVER == TRUE) // Register with the files system MT_OtaRegister(task_id); #endif // OTA_SERVER } /****************************************************************************** * @fn zclOTA_event_loop * * @brief Event Loop Processor for OTA Client task. * * @param task_id - TaskId * events - events * * @return Unprocessed event bits */ uint16 zclOTA_event_loop( uint8 task_id, uint16 events ) { afIncomingMSGPacket_t *MSGpkt; if ( events & SYS_EVENT_MSG ) { while ( (MSGpkt = (afIncomingMSGPacket_t *)osal_msg_receive( task_id )) ) { switch ( MSGpkt->hdr.event ) { #if (defined OTA_CLIENT) && (OTA_CLIENT == TRUE) case ZDO_CB_MSG: zclOTA_ProcessZDOMsgs( (zdoIncomingMsg_t *)MSGpkt ); break; #endif // OTA_CLIENT #if (defined OTA_SERVER) && (OTA_SERVER == TRUE) case MT_SYS_OTA_MSG: zclOTA_ServerHandleFileSysCb((OTA_MtMsg_t*)MSGpkt); break; #endif default: break; } // Release the memory osal_msg_deallocate( (uint8 *)MSGpkt ); } // return unprocessed events return (events ^ SYS_EVENT_MSG); } #if (defined OTA_CLIENT) && (OTA_CLIENT == TRUE) if ( events & ZCL_OTA_IMAGE_BLOCK_WAIT_EVT ) { // If the time has expired, perform the required action if (zclOTA_UpdateDelay == 0) { zclOTA_ImageBlockWaitExpired(); } else { // Decrement the number of minutes to wait and reset the timer zclOTA_UpdateDelay--; osal_start_timerEx(zclOTA_TaskID, ZCL_OTA_IMAGE_BLOCK_WAIT_EVT, 60000); } return ( events ^ ZCL_OTA_IMAGE_BLOCK_WAIT_EVT ); } if ( events & ZCL_OTA_UPGRADE_WAIT_EVT ) { // If the time has expired, perform the required action if (zclOTA_UpdateDelay == 0) { if (zclOTA_ImageUpgradeStatus == OTA_STATUS_COUNTDOWN) { zclOTA_UpgradeComplete(ZSuccess); } else if (zclOTA_ImageUpgradeStatus == OTA_STATUS_UPGRADE_WAIT) { if (++zclOTA_UpgradeEndRetry > OTA_MAX_END_REQ_RETRIES) { // If we have not heard from the server for N retries, perform the upgrade zclOTA_UpgradeComplete(ZSuccess); } else { // Send another update end request zclOTA_UpgradeEndReqParams_t req; req.status = ZSuccess; osal_memcpy(&req.fileId, &zclOTA_CurrentDlFileId, sizeof(zclOTA_FileID_t)); zclOTA_SendUpgradeEndReq(&zclOTA_serverAddr, &req); // Restart the timer for another hour zclOTA_StartTimer(ZCL_OTA_UPGRADE_WAIT_EVT, 3600); } } } else { // Decrement the number of minutes to wait and reset the timer zclOTA_UpdateDelay--; osal_start_timerEx(zclOTA_TaskID, ZCL_OTA_UPGRADE_WAIT_EVT, 60000); } return ( events ^ ZCL_OTA_UPGRADE_WAIT_EVT ); } if (events & ZCL_OTA_IMAGE_QUERY_TO_EVT ) { if (zclOTA_ImageUpgradeStatus == OTA_STATUS_NORMAL) { // Notify the application task of the timeout waiting for the download to start zclOTA_CallbackMsg_t *pMsg; pMsg = (zclOTA_CallbackMsg_t*) osal_msg_allocate(sizeof(zclOTA_CallbackMsg_t)); if (pMsg) { pMsg->hdr.event = ZCL_OTA_CALLBACK_IND; pMsg->hdr.status = ZFailure; pMsg->ota_event = ZCL_OTA_START_CALLBACK; osal_msg_send(zclOTA_AppTask, (uint8*) pMsg); } } return ( events ^ ZCL_OTA_IMAGE_QUERY_TO_EVT ); } if (events & ZCL_OTA_BLOCK_RSP_TO_EVT ) { // We timed out waiting for a Block Response if (++zclOTA_BlockRetry > OTA_MAX_BLOCK_RETRIES) { // Send a failed update end request zclOTA_UpgradeEndReqParams_t req; req.status = ZOtaAbort; osal_memcpy(&req.fileId, &zclOTA_CurrentDlFileId, sizeof(zclOTA_FileID_t)); zclOTA_SendUpgradeEndReq(&zclOTA_serverAddr, &req); zclOTA_UpgradeComplete(ZOtaAbort); } else { // Send another block request sendImageBlockReq(&zclOTA_serverAddr); } return ( events ^ ZCL_OTA_BLOCK_RSP_TO_EVT ); } if (events & ZCL_OTA_QUERY_SERVER_EVT ) { zclOTA_QueryNextImageReqParams_t queryParams; queryParams.fieldControl = 0; queryParams.fileId.manufacturer = OTA_MANUFACTURER_ID; queryParams.fileId.type = OTA_TYPE_ID; queryParams.fileId.version = zclOTA_CurrentFileVersion; zclOTA_SendQueryNextImageReq(&zclOTA_serverAddr, &queryParams); // Set a timer to wait for the response osal_start_timerEx(zclOTA_TaskID, ZCL_OTA_IMAGE_QUERY_TO_EVT, 10000); return ( events ^ ZCL_OTA_QUERY_SERVER_EVT ); } #endif // OTA_CLIENT // Discard unknown events return 0; } /****************************************************************************** * @fn zclOTA_HdlIncoming * * @brief Callback from ZCL to process incoming Commands specific * to this cluster library or Profile commands for attributes * that aren't in the attribute list * * @param pInMsg - pointer to the incoming message * * @return ZStatus_t */ static ZStatus_t zclOTA_HdlIncoming( zclIncoming_t *pInMsg ) { ZStatus_t stat = ZSuccess; if ( zcl_ClusterCmd( pInMsg->hdr.fc.type ) ) { // Is this a manufacturer specific command? if ( pInMsg->hdr.fc.manuSpecific == 0 ) { // Is command for server? if ( zcl_ServerCmd( pInMsg->hdr.fc.direction ) ) { #if (defined OTA_SERVER) && (OTA_SERVER == TRUE) stat = zclOTA_ServerHdlIncoming( pInMsg ); #else stat = ZCL_STATUS_UNSUP_CLUSTER_COMMAND; #endif // OTA_SERVER } else // Else command is for client { #if (defined OTA_CLIENT) && (OTA_CLIENT == TRUE) stat = zclOTA_ClientHdlIncoming( pInMsg ); #else stat = ZCL_STATUS_UNSUP_CLUSTER_COMMAND; #endif // OTA_CLIENT } } else { // We don't support any manufacturer specific command. stat = ZCL_STATUS_UNSUP_MANU_CLUSTER_COMMAND; } } else { // Handle all the normal (Read, Write...) commands -- should never get here stat = ZFailure; } return ( stat ); } #if (defined OTA_SERVER) && (OTA_SERVER == TRUE) /****************************************************************************** * @fn zclOTA_SendImageNotify * * @brief Send an OTA Image Notify message. * * @param dstAddr - where you want the message to go * @param pParams - message parameters * * @return ZStatus_t */ ZStatus_t zclOTA_SendImageNotify( afAddrType_t *dstAddr, zclOTA_ImageNotifyParams_t *pParams ) { ZStatus_t status; uint8 buf[PAYLOAD_MAX_LEN_IMAGE_NOTIFY]; uint8 *pBuf = buf; *pBuf++ = pParams->payloadType; *pBuf++ = pParams->queryJitter; if (pParams->payloadType >= NOTIFY_PAYLOAD_JITTER_MFG) { *pBuf++ = LO_UINT16(pParams->fileId.manufacturer); *pBuf++ = HI_UINT16(pParams->fileId.manufacturer); } if (pParams->payloadType >= NOTIFY_PAYLOAD_JITTER_MFG_TYPE) { *pBuf++ = LO_UINT16(pParams->fileId.type); *pBuf++ = HI_UINT16(pParams->fileId.type); } if (pParams->payloadType == NOTIFY_PAYLOAD_JITTER_MFG_TYPE_VERS) { pBuf = osal_buffer_uint32(pBuf, pParams->fileId.version); } status = zcl_SendCommand( ZCL_OTA_ENDPOINT, dstAddr, ZCL_CLUSTER_ID_OTA, COMMAND_IMAGE_NOTIFY, TRUE, ZCL_FRAME_SERVER_CLIENT_DIR, TRUE, 0, zclOTA_SeqNo++, (uint16) (pBuf - buf), buf ); return status; } /****************************************************************************** * @fn zclOTA_SendQueryNextImageRsp * * @brief Send an OTA Query Next Image Response message. * * @param dstAddr - where you want the message to go * @param pParams - message parameters * * @return ZStatus_t */ ZStatus_t zclOTA_SendQueryNextImageRsp( afAddrType_t *dstAddr, zclOTA_QueryImageRspParams_t *pParams ) { ZStatus_t status; uint8 buf[PAYLOAD_MAX_LEN_QUERY_NEXT_IMAGE_RSP]; uint8 *pBuf = buf; *pBuf++ = pParams->status; if (pParams->status == ZCL_STATUS_SUCCESS) { *pBuf++ = LO_UINT16(pParams->fileId.manufacturer); *pBuf++ = HI_UINT16(pParams->fileId.manufacturer); *pBuf++ = LO_UINT16(pParams->fileId.type); *pBuf++ = HI_UINT16(pParams->fileId.type); pBuf = osal_buffer_uint32(pBuf, pParams->fileId.version); pBuf = osal_buffer_uint32(pBuf, pParams->imageSize); } status = zcl_SendCommand( ZCL_OTA_ENDPOINT, dstAddr, ZCL_CLUSTER_ID_OTA, COMMAND_QUERY_NEXT_IMAGE_RSP, TRUE, ZCL_FRAME_SERVER_CLIENT_DIR, TRUE, 0, zclOTA_SeqNo++, (uint16) (pBuf - buf), buf ); return status; } /****************************************************************************** * @fn zclOTA_SendImageBlockRsp * * @brief Send an OTA Image Block Response mesage. * * @param dstAddr - where you want the message to go * @param pParams - message parameters * * @return ZStatus_t */ ZStatus_t zclOTA_SendImageBlockRsp( afAddrType_t *dstAddr, zclOTA_ImageBlockRspParams_t *pParams ) { uint8 *buf; uint8 *pBuf; ZStatus_t status; uint8 len; if (pParams->status == ZCL_STATUS_SUCCESS) { len = PAYLOAD_MAX_LEN_IMAGE_BLOCK_RSP + pParams->rsp.success.dataSize; } else if (pParams->status == ZCL_STATUS_WAIT_FOR_DATA) { len = PAYLOAD_MIN_LEN_IMAGE_BLOCK_WAIT; } else { len = 1; } buf = osal_mem_alloc( len ); if ( buf == NULL ) { return (ZMemError); } pBuf = buf; *pBuf++ = pParams->status; if (pParams->status == ZCL_STATUS_SUCCESS) { *pBuf++ = LO_UINT16(pParams->rsp.success.fileId.manufacturer); *pBuf++ = HI_UINT16(pParams->rsp.success.fileId.manufacturer); *pBuf++ = LO_UINT16(pParams->rsp.success.fileId.type); *pBuf++ = HI_UINT16(pParams->rsp.success.fileId.type); pBuf = osal_buffer_uint32(pBuf, pParams->rsp.success.fileId.version); pBuf = osal_buffer_uint32(pBuf, pParams->rsp.success.fileOffset); *pBuf++ = pParams->rsp.success.dataSize; osal_memcpy(pBuf, pParams->rsp.success.pData, pParams->rsp.success.dataSize); } else if (pParams->status == ZCL_STATUS_WAIT_FOR_DATA) { pBuf = osal_buffer_uint32(pBuf, pParams->rsp.wait.currentTime); pBuf = osal_buffer_uint32(pBuf, pParams->rsp.wait.requestTime); } status = zcl_SendCommand( ZCL_OTA_ENDPOINT, dstAddr, ZCL_CLUSTER_ID_OTA, COMMAND_IMAGE_BLOCK_RSP, TRUE, ZCL_FRAME_SERVER_CLIENT_DIR, TRUE, 0, zclOTA_SeqNo++, len, buf ); osal_mem_free(buf); return status; } /****************************************************************************** * @fn zclOTA_SendUpgradeEndRsp * * @brief Send an OTA Upgrade End Response mesage. * * @param dstAddr - where you want the message to go * @param pParams - message parameters * * @return ZStatus_t */ ZStatus_t zclOTA_SendUpgradeEndRsp( afAddrType_t *dstAddr, zclOTA_UpgradeEndRspParams_t *pParams ) { ZStatus_t status; uint8 buf[PAYLOAD_MAX_LEN_UPGRADE_END_RSP]; uint8 *pBuf = buf; *pBuf++ = LO_UINT16(pParams->fileId.manufacturer); *pBuf++ = HI_UINT16(pParams->fileId.manufacturer); *pBuf++ = LO_UINT16(pParams->fileId.type); *pBuf++ = HI_UINT16(pParams->fileId.type); pBuf = osal_buffer_uint32(pBuf, pParams->fileId.version); pBuf = osal_buffer_uint32(pBuf, pParams->currentTime); pBuf = osal_buffer_uint32(pBuf, pParams->upgradeTime); status = zcl_SendCommand( ZCL_OTA_ENDPOINT, dstAddr, ZCL_CLUSTER_ID_OTA, COMMAND_UPGRADE_END_RSP, TRUE, ZCL_FRAME_SERVER_CLIENT_DIR, TRUE, 0, zclOTA_SeqNo++, PAYLOAD_MAX_LEN_UPGRADE_END_RSP, buf ); return status; } /****************************************************************************** * @fn zclOTA_SendQuerySpecificFileRsp * * @brief Send an OTA Query Specific File Response mesage. * * @param dstAddr - where you want the message to go * @param pParams - message parameters * * @return ZStatus_t */ ZStatus_t zclOTA_SendQuerySpecificFileRsp( afAddrType_t *dstAddr, zclOTA_QueryImageRspParams_t *pParams ) { ZStatus_t status; uint8 buf[PAYLOAD_MAX_LEN_QUERY_SPECIFIC_FILE_RSP]; uint8 *pBuf = buf; *pBuf++ = pParams->status; if (pParams->status == ZCL_STATUS_SUCCESS) { *pBuf++ = LO_UINT16(pParams->fileId.manufacturer); *pBuf++ = HI_UINT16(pParams->fileId.manufacturer); *pBuf++ = LO_UINT16(pParams->fileId.type); *pBuf++ = HI_UINT16(pParams->fileId.type); pBuf = osal_buffer_uint32(pBuf, pParams->fileId.version); pBuf = osal_buffer_uint32(pBuf, pParams->imageSize); } status = zcl_SendCommand( ZCL_OTA_ENDPOINT, dstAddr, ZCL_CLUSTER_ID_OTA, COMMAND_QUERY_SPECIFIC_FILE_RSP, TRUE, ZCL_FRAME_SERVER_CLIENT_DIR, TRUE, 0, zclOTA_SeqNo++, (uint16) (pBuf - buf), buf ); return status; } #endif // OTA_SERVER #if (defined OTA_CLIENT) && (OTA_CLIENT == TRUE) /****************************************************************************** * @fn zclOTA_SendQueryNextImageReq * * @brief Send an OTA Query Next Image Request mesage. * * @param dstAddr - where you want the message to go * @param pParams - message parameters * * @return ZStatus_t */ ZStatus_t zclOTA_SendQueryNextImageReq( afAddrType_t *dstAddr, zclOTA_QueryNextImageReqParams_t *pParams ) { ZStatus_t status; uint8 buf[PAYLOAD_MAX_LEN_QUERY_NEXT_IMAGE_REQ]; uint8 *pBuf = buf; *pBuf++ = pParams->fieldControl; *pBuf++ = LO_UINT16(pParams->fileId.manufacturer); *pBuf++ = HI_UINT16(pParams->fileId.manufacturer); *pBuf++ = LO_UINT16(pParams->fileId.type); *pBuf++ = HI_UINT16(pParams->fileId.type); pBuf = osal_buffer_uint32(pBuf, pParams->fileId.version); if (pParams->fieldControl == 1) { *pBuf++ = LO_UINT16(pParams->hardwareVersion); *pBuf++ = HI_UINT16(pParams->hardwareVersion); } status = zcl_SendCommand( ZCL_OTA_ENDPOINT, dstAddr, ZCL_CLUSTER_ID_OTA, COMMAND_QUERY_NEXT_IMAGE_REQ, TRUE, ZCL_FRAME_CLIENT_SERVER_DIR, FALSE, 0, zclOTA_SeqNo++, (uint16) (pBuf - buf), buf ); return status; } /****************************************************************************** * @fn zclOTA_SendImageBlockReq * * @brief Send an OTA Image Block Request mesage. * * @param dstAddr - where you want the message to go * @param pParams - message parameters * * @return ZStatus_t */ ZStatus_t zclOTA_SendImageBlockReq( afAddrType_t *dstAddr, zclOTA_ImageBlockReqParams_t *pParams ) { ZStatus_t status; uint8 buf[PAYLOAD_MAX_LEN_IMAGE_BLOCK_REQ]; uint8 *pBuf = buf; *pBuf++ = pParams->fieldControl; *pBuf++ = LO_UINT16(pParams->fileId.manufacturer); *pBuf++ = HI_UINT16(pParams->fileId.manufacturer); *pBuf++ = LO_UINT16(pParams->fileId.type); *pBuf++ = HI_UINT16(pParams->fileId.type); pBuf = osal_buffer_uint32(pBuf, pParams->fileId.version); pBuf = osal_buffer_uint32(pBuf, pParams->fileOffset); *pBuf++ = pParams->maxDataSize; if (pParams->fieldControl == 1) { osal_cpyExtAddr(pBuf, pParams->nodeAddr); pBuf += Z_EXTADDR_LEN; } status = zcl_SendCommand( ZCL_OTA_ENDPOINT, dstAddr, ZCL_CLUSTER_ID_OTA, COMMAND_IMAGE_BLOCK_REQ, TRUE, ZCL_FRAME_CLIENT_SERVER_DIR, FALSE, 0, zclOTA_SeqNo++, (uint16) (pBuf - buf), buf ); return status; } /****************************************************************************** * @fn zclOTA_SendUpgradeEndReq * * @brief Send an OTA Upgrade End Request mesage. * * @param dstAddr - where you want the message to go * @param pParams - message parameters * * @return ZStatus_t */ ZStatus_t zclOTA_SendUpgradeEndReq( afAddrType_t *dstAddr, zclOTA_UpgradeEndReqParams_t *pParams ) { ZStatus_t status; uint8 buf[PAYLOAD_MAX_LEN_UPGRADE_END_REQ]; uint8 *pBuf = buf; *pBuf++ = pParams->status; *pBuf++ = LO_UINT16(pParams->fileId.manufacturer); *pBuf++ = HI_UINT16(pParams->fileId.manufacturer); *pBuf++ = LO_UINT16(pParams->fileId.type); *pBuf++ = HI_UINT16(pParams->fileId.type); pBuf = osal_buffer_uint32(pBuf, pParams->fileId.version); status = zcl_SendCommand( ZCL_OTA_ENDPOINT, dstAddr, ZCL_CLUSTER_ID_OTA, COMMAND_UPGRADE_END_REQ, TRUE, ZCL_FRAME_CLIENT_SERVER_DIR, FALSE, 0, zclOTA_SeqNo++, (uint16) (pBuf - buf), buf ); return status; } /****************************************************************************** * @fn zclOTA_StartTimer * * @brief Start a ZCL OTA timer. * * @param eventId - OSAL event set on timer expiration * @param seconds - timeout in seconds * * @return None */ static void zclOTA_StartTimer(uint16 eventId, uint32 seconds) { // Record the number of whole minutes to wait zclOTA_UpdateDelay = (seconds / 60); // Set a timer for the remaining seconds to wait. osal_start_timerEx(zclOTA_TaskID, eventId, (seconds % 60) * 1000); } /****************************************************************************** * @fn sendImageBlockReq * * @brief Send an Image Block Request. * * @param dstAddr - where you want the message to go * * @return ZStatus_t */ static ZStatus_t sendImageBlockReq(afAddrType_t *dstAddr) { zclOTA_ImageBlockReqParams_t req; req.fieldControl = 0; req.fileId.manufacturer = zclOTA_ManufacturerId; req.fileId.type = zclOTA_ImageType; req.fileId.version = zclOTA_DownloadedFileVersion; req.fileOffset = zclOTA_FileOffset; if (zclOTA_DownloadedImageSize - zclOTA_FileOffset < OTA_MAX_MTU) { req.maxDataSize = zclOTA_DownloadedImageSize - zclOTA_FileOffset; } else { req.maxDataSize = OTA_MAX_MTU; } // Start a timer waiting for a response osal_start_timerEx(zclOTA_TaskID, ZCL_OTA_BLOCK_RSP_TO_EVT, OTA_MAX_BLOCK_RSP_WAIT_TIME); return zclOTA_SendImageBlockReq( dstAddr, &req); } /****************************************************************************** * @fn zclOTA_ProcessImageData * * @brief Process image data as it is received from the host. * * @param pData - pointer to the data * @param len - length of the data * * @return status of the operation */ uint8 zclOTA_ProcessImageData(uint8 *pData, uint8 len) { int8 i; #if defined OTA_MMO_SIGN uint8 skipHash = FALSE; #endif if (zclOTA_ImageUpgradeStatus != OTA_STATUS_IN_PROGRESS) { return ZCL_STATUS_ABORT; } #if (defined HAL_LED) && (HAL_LED == TRUE) // Toggle an LED to indicate we received a new block HalLedSet(HAL_LED_2, HAL_LED_MODE_TOGGLE); #endif // write data to secondary storage HalOTAWrite(zclOTA_FileOffset, pData, len, HAL_OTA_DL); for (i=0; i= ZCL_OTA_PD_ELEM_LEN1_STATE) && (zclOTA_ClientPdState <= ZCL_OTA_PD_ELEMENT_STATE)) { if (zclOTA_ElementTag == OTA_ECDSA_SIGNATURE_TAG_ID) { skipHash = TRUE; } } #endif switch (zclOTA_ClientPdState) { // verify header magic number case ZCL_OTA_PD_MAGIC_0_STATE: // Initialize control variables #if defined OTA_MMO_SIGN osal_memset(&zclOTA_MmoHash, 0, sizeof(zclOTA_MmoHash)); zclOTA_HashPos = 0; #endif // Missing break intended case ZCL_OTA_PD_MAGIC_1_STATE: case ZCL_OTA_PD_MAGIC_2_STATE: case ZCL_OTA_PD_MAGIC_3_STATE: if (pData[i] != zclOTA_HdrMagic[zclOTA_ClientPdState]) { return ZCL_STATUS_INVALID_IMAGE; } zclOTA_ClientPdState++; break; case ZCL_OTA_PD_HDR_LEN1_STATE: // get header length if (zclOTA_FileOffset == ZCL_OTA_HDR_LEN_OFFSET) { zclOTA_HeaderLen = pData[i]; zclOTA_ClientPdState = ZCL_OTA_PD_HDR_LEN2_STATE; } break; case ZCL_OTA_PD_HDR_LEN2_STATE: zclOTA_HeaderLen |= (((uint16)pData[i]) << 8) & 0xFF00; zclOTA_ClientPdState = ZCL_OTA_PD_STK_VER1_STATE; break; case ZCL_OTA_PD_STK_VER1_STATE: // get stack version if (zclOTA_FileOffset == ZCL_OTA_STK_VER_OFFSET) { zclOTA_DownloadedZigBeeStackVersion = pData[i]; zclOTA_ClientPdState = ZCL_OTA_PD_STK_VER2_STATE; } break; case ZCL_OTA_PD_STK_VER2_STATE: zclOTA_DownloadedZigBeeStackVersion |= (((uint16)pData[i]) << 8) & 0xFF00; zclOTA_ClientPdState = ZCL_OTA_PD_CONT_HDR_STATE; if (zclOTA_DownloadedZigBeeStackVersion != OTA_HDR_STACK_VERSION) { return ZCL_STATUS_INVALID_IMAGE; } break; case ZCL_OTA_PD_CONT_HDR_STATE: // Complete the header if (zclOTA_FileOffset == zclOTA_HeaderLen-1) { zclOTA_ClientPdState = ZCL_OTA_PD_ELEM_TAG1_STATE; } break; case ZCL_OTA_PD_ELEM_TAG1_STATE: zclOTA_ElementTag = pData[i]; zclOTA_ClientPdState = ZCL_OTA_PD_ELEM_TAG2_STATE; break; case ZCL_OTA_PD_ELEM_TAG2_STATE: zclOTA_ElementTag |= (((uint16)pData[i]) << 8) & 0xFF00; zclOTA_ElementPos = 0; zclOTA_ClientPdState = ZCL_OTA_PD_ELEM_LEN1_STATE; break; case ZCL_OTA_PD_ELEM_LEN1_STATE: zclOTA_ElementLen = pData[i]; zclOTA_ClientPdState = ZCL_OTA_PD_ELEM_LEN2_STATE; break; case ZCL_OTA_PD_ELEM_LEN2_STATE: zclOTA_ElementLen |= ((uint32)pData[i] << 8) & 0x0000FF00; zclOTA_ClientPdState = ZCL_OTA_PD_ELEM_LEN3_STATE; break; case ZCL_OTA_PD_ELEM_LEN3_STATE: zclOTA_ElementLen |= ((uint32)pData[i] << 16) & 0x00FF0000; zclOTA_ClientPdState = ZCL_OTA_PD_ELEM_LEN4_STATE; break; case ZCL_OTA_PD_ELEM_LEN4_STATE: zclOTA_ElementLen |= ((uint32)pData[i] << 24) & 0xFF000000; zclOTA_ClientPdState = ZCL_OTA_PD_ELEMENT_STATE; // Make sure the length of the element isn't bigger than the image if (zclOTA_ElementLen > (zclOTA_DownloadedImageSize - zclOTA_FileOffset)) { return ZCL_STATUS_INVALID_IMAGE; } #if defined OTA_MMO_SIGN if (zclOTA_ElementTag == OTA_ECDSA_SIGNATURE_TAG_ID) { if (zclOTA_ElementLen != OTA_SIGNATURE_LEN + Z_EXTADDR_LEN) { return ZCL_STATUS_INVALID_IMAGE; } } else if (zclOTA_ElementTag == OTA_ECDSA_CERT_TAG_ID) { if (zclOTA_ElementLen != OTA_CERTIFICATE_LEN) { return ZCL_STATUS_INVALID_IMAGE; } } #endif break; case ZCL_OTA_PD_ELEMENT_STATE: #if defined OTA_MMO_SIGN if (zclOTA_ElementTag == OTA_ECDSA_SIGNATURE_TAG_ID) { if (zclOTA_ElementPos < Z_EXTADDR_LEN) zclOTA_SignerIEEE[zclOTA_ElementPos] = pData[i]; else zclOTA_SignatureData[zclOTA_ElementPos - Z_EXTADDR_LEN] = pData[i]; } else if (zclOTA_ElementTag == OTA_ECDSA_CERT_TAG_ID) { zclOTA_Certificate[zclOTA_ElementPos] = pData[i]; } #endif if (++zclOTA_ElementPos == zclOTA_ElementLen) { // Element is complete if (zclOTA_ElementTag == OTA_UPGRADE_IMAGE_TAG_ID) { // The serial flash can take up to 25 ms before it is ready for a read uint32 k; for (k=0; k<0xffff; k++) { asm("NOP"); } // When the image is complete, verify CRC if (HalOTAChkDL(HAL_OTA_CRC_OSET) != SUCCESS) { #if (defined HAL_LCD) && (HAL_LCD == TRUE) HalLcdWriteString("OTA CRC Fail", HAL_LCD_LINE_3); #endif return ZCL_STATUS_INVALID_IMAGE; } } zclOTA_ClientPdState = ZCL_OTA_PD_ELEM_TAG1_STATE; } break; default: break; } #if defined OTA_MMO_SIGN // We need to skip the hash calculation on the signature element. // When receiving a tag, we wait to receive the entire tag before // adding the byte to the hash buffer because it could be the tag // for the signature if (zclOTA_ClientPdState == ZCL_OTA_PD_ELEM_TAG2_STATE) { skipHash = TRUE; } else if (zclOTA_ClientPdState == ZCL_OTA_PD_ELEM_LEN1_STATE) { if (zclOTA_ElementTag != OTA_ECDSA_SIGNATURE_TAG_ID) { // This tag is not for the signature. // Put the Lower byte of the tag into the hash buffer now // The high byte will be processed as usual below zclOTA_DataToHash[zclOTA_HashPos++] = LO_UINT16(zclOTA_ElementTag); // When the buffer reaches OTA_MMO_HASH_SIZE, update the Hash if (zclOTA_HashPos == OTA_MMO_HASH_SIZE) { OTA_CalculateMmoR3(&zclOTA_MmoHash, zclOTA_DataToHash, OTA_MMO_HASH_SIZE, FALSE); zclOTA_HashPos = 0; } skipHash = FALSE; } } if (!skipHash) { // Maintain a buffer of data to hash zclOTA_DataToHash[zclOTA_HashPos++] = pData[i]; // When the buffer reaches OTA_MMO_HASH_SIZE, update the Hash if (zclOTA_HashPos == OTA_MMO_HASH_SIZE) { OTA_CalculateMmoR3(&zclOTA_MmoHash, zclOTA_DataToHash, OTA_MMO_HASH_SIZE, FALSE); zclOTA_HashPos = 0; } } #endif // Check if the download is complete if (++zclOTA_FileOffset >= zclOTA_DownloadedImageSize) { zclOTA_ImageUpgradeStatus = OTA_STATUS_COMPLETE; #if defined OTA_MMO_SIGN // Complete the hash calcualtion OTA_CalculateMmoR3(&zclOTA_MmoHash, zclOTA_DataToHash, zclOTA_HashPos, TRUE); // Validate the hash if (OTA_ValidateSignature(zclOTA_MmoHash.hash, zclOTA_Certificate, zclOTA_SignatureData, zclOTA_SignerIEEE) != ZSuccess) return ZCL_STATUS_INVALID_IMAGE; #endif return ZSuccess; } } return ZSuccess; } /****************************************************************************** * @fn zclOTA_ProcessImageNotify * * @brief Process received Image Notify command. * * @param pInMsg - pointer to the incoming message * * @return ZStatus_t */ static ZStatus_t zclOTA_ProcessImageNotify( zclIncoming_t *pInMsg ) { zclOTA_ImageNotifyParams_t param; zclOTA_QueryNextImageReqParams_t req; uint8 *pData; // verify message length if ((pInMsg->pDataLen > PAYLOAD_MAX_LEN_IMAGE_NOTIFY) || (pInMsg->pDataLen < PAYLOAD_MIN_LEN_IMAGE_NOTIFY)) { // no further processing if invalid return ZCL_STATUS_MALFORMED_COMMAND; } // verify in 'normal' state if ((zclOTA_Permit == FALSE) || (zclOTA_ImageUpgradeStatus != OTA_STATUS_NORMAL)) { return ZFailure; } // parse message pData = pInMsg->pData; param.payloadType = *pData++; param.queryJitter = *pData++; param.fileId.manufacturer = BUILD_UINT16(pData[0], pData[1]); pData += 2; param.fileId.type = BUILD_UINT16(pData[0], pData[1]); pData += 2; param.fileId.version = osal_build_uint32( pData, 4 ); // if message is broadcast if (pInMsg->msg->wasBroadcast) { // verify manufacturer if ((param.payloadType >= NOTIFY_PAYLOAD_JITTER_MFG) && (param.fileId.manufacturer != zclOTA_ManufacturerId)) { return ZSuccess; } // verify image type if ((param.payloadType >= NOTIFY_PAYLOAD_JITTER_MFG_TYPE) && (param.fileId.type != zclOTA_ImageType)) { return ZSuccess; } // verify version; if version matches ignore if ((param.payloadType >= NOTIFY_PAYLOAD_JITTER_MFG_TYPE_VERS) && (param.fileId.version == zclOTA_CurrentFileVersion)) { return ZSuccess; } // get random value and compare to query jitter if (((uint8) osal_rand() % 100) > param.queryJitter) { // if greater than query jitter ignore; return ZSuccess; } } // if unicast message, or broadcast and still made it here, send query next image req.fieldControl = 0; req.fileId.manufacturer = zclOTA_ManufacturerId; req.fileId.type = zclOTA_ImageType; req.fileId.version = zclOTA_CurrentFileVersion; zclOTA_SendQueryNextImageReq( &(pInMsg->msg->srcAddr), &req); return ZSuccess; } /****************************************************************************** * @fn zclOTA_ProcessQueryNextImageRsp * * @brief Process received Query Next Image Response. * * @param pInMsg - pointer to the incoming message * * @return ZStatus_t */ static ZStatus_t zclOTA_ProcessQueryNextImageRsp( zclIncoming_t *pInMsg ) { zclOTA_QueryImageRspParams_t param; uint8 *pData; uint8 status = ZFailure; // verify message length if ((pInMsg->pDataLen != PAYLOAD_MAX_LEN_QUERY_NEXT_IMAGE_RSP) && (pInMsg->pDataLen != PAYLOAD_MIN_LEN_QUERY_NEXT_IMAGE_RSP)) { // no further processing if invalid return ZCL_STATUS_MALFORMED_COMMAND; } // ignore message if in 'download in progress' state if (zclOTA_ImageUpgradeStatus == OTA_STATUS_IN_PROGRESS) { return ZSuccess; } // get status pData = pInMsg->pData; param.status = *pData++; // if status is success if (param.status == ZCL_STATUS_SUCCESS) { // parse message param.fileId.manufacturer = BUILD_UINT16(pData[0], pData[1]); pData += 2; param.fileId.type = BUILD_UINT16(pData[0], pData[1]); pData += 2; param.fileId.version = osal_build_uint32( pData, 4 ); pData += 4; param.imageSize = osal_build_uint32( pData, 4 ); // verify manufacturer id and image type if ((param.fileId.type == zclOTA_ImageType) && (param.fileId.manufacturer == zclOTA_ManufacturerId)) { // store file version and image size zclOTA_DownloadedFileVersion = param.fileId.version; zclOTA_DownloadedImageSize = param.imageSize; // initialize other variables zclOTA_FileOffset = 0; zclOTA_ClientPdState = ZCL_OTA_PD_MAGIC_0_STATE; // set state to 'in progress' zclOTA_ImageUpgradeStatus = OTA_STATUS_IN_PROGRESS; // store server address zclOTA_serverAddr = pInMsg->msg->srcAddr; // Store the file ID osal_memcpy(&zclOTA_CurrentDlFileId, ¶m.fileId, sizeof(zclOTA_FileID_t)); // send image block request sendImageBlockReq(&(pInMsg->msg->srcAddr)); status = ZCL_STATUS_CMD_HAS_RSP; // Request the IEEE address of the server to put into the // ATTRID_UPGRADE_SERVER_ID attribute ZDP_IEEEAddrReq(pInMsg->msg->srcAddr.addr.shortAddr, ZDP_ADDR_REQTYPE_SINGLE, 0, 0); osal_stop_timerEx(zclOTA_TaskID, ZCL_OTA_IMAGE_QUERY_TO_EVT); } } if (zclOTA_AppTask != 0xFF) { // Notify the application task of the failure zclOTA_CallbackMsg_t *pMsg; pMsg = (zclOTA_CallbackMsg_t*) osal_msg_allocate(sizeof(zclOTA_CallbackMsg_t)); if (pMsg) { pMsg->hdr.event = ZCL_OTA_CALLBACK_IND; pMsg->hdr.status = param.status; pMsg->ota_event = ZCL_OTA_START_CALLBACK; osal_msg_send(zclOTA_AppTask, (uint8*) pMsg); } } return status; } /****************************************************************************** * @fn zclOTA_ProcessImageBlockRsp * * @brief Process received Image Block Response. * * @param pInMsg - pointer to the incoming message * * @return ZStatus_t */ static ZStatus_t zclOTA_ProcessImageBlockRsp( zclIncoming_t *pInMsg ) { zclOTA_ImageBlockRspParams_t param; zclOTA_UpgradeEndReqParams_t req; uint8 *pData; uint8 status = ZSuccess; // verify in 'in progress' state if (zclOTA_ImageUpgradeStatus != OTA_STATUS_IN_PROGRESS) { return ZSuccess; } // get status pData = pInMsg->pData; param.status = *pData++; // if status is success if (param.status == ZCL_STATUS_SUCCESS) { // verify message length if (pInMsg->pDataLen < PAYLOAD_MAX_LEN_IMAGE_BLOCK_RSP) { // no further processing if invalid return ZCL_STATUS_MALFORMED_COMMAND; } // parse message param.rsp.success.fileId.manufacturer = BUILD_UINT16(pData[0], pData[1]); pData += 2; param.rsp.success.fileId.type = BUILD_UINT16(pData[0], pData[1]); pData += 2; param.rsp.success.fileId.version = osal_build_uint32( pData, 4 ); pData += 4; param.rsp.success.fileOffset = osal_build_uint32( pData, 4 ); pData += 4; param.rsp.success.dataSize = *pData++; param.rsp.success.pData = pData; // verify manufacturer, image type, file version, file offset if ((param.rsp.success.fileId.type != zclOTA_ImageType) || (param.rsp.success.fileId.manufacturer != zclOTA_ManufacturerId) || (param.rsp.success.fileId.version != zclOTA_DownloadedFileVersion)) { status = ZCL_STATUS_INVALID_IMAGE; } else { // Drop duplicate packets (retries) if (param.rsp.success.fileOffset != zclOTA_FileOffset) { return ZSuccess; } status = zclOTA_ProcessImageData(param.rsp.success.pData, param.rsp.success.dataSize); // Stop the timer and clear the retry count zclOTA_BlockRetry = 0; osal_stop_timerEx(zclOTA_TaskID, ZCL_OTA_BLOCK_RSP_TO_EVT); if (status == ZSuccess) { if (zclOTA_ImageUpgradeStatus == OTA_STATUS_COMPLETE) { // send upgrade end req with success status osal_memcpy(&req.fileId, ¶m.rsp.success.fileId, sizeof(zclOTA_FileID_t)); req.status = ZSuccess; zclOTA_SendUpgradeEndReq( &(pInMsg->msg->srcAddr), &req ); } else { sendImageBlockReq(&(pInMsg->msg->srcAddr)); } } } } // else if status is 'wait for data' else if (param.status == ZCL_STATUS_WAIT_FOR_DATA) { // verify message length if (pInMsg->pDataLen != PAYLOAD_MIN_LEN_IMAGE_BLOCK_WAIT) { // no further processing if invalid return ZCL_STATUS_MALFORMED_COMMAND; } // parse message param.rsp.wait.currentTime = osal_build_uint32( pData, 4 ); pData += 4; param.rsp.wait.requestTime = osal_build_uint32( pData, 4 ); // Stop the timer and clear the retry count zclOTA_BlockRetry = 0; osal_stop_timerEx(zclOTA_TaskID, ZCL_OTA_BLOCK_RSP_TO_EVT); // set timer for next image block req zclOTA_StartTimer(ZCL_OTA_IMAGE_BLOCK_WAIT_EVT, (param.rsp.wait.requestTime - param.rsp.wait.currentTime)); } else if (param.status == ZCL_STATUS_ABORT) { // download aborted; set state to 'normal' state zclOTA_ImageUpgradeStatus = OTA_STATUS_NORMAL; // Stop the timer and clear the retry count zclOTA_BlockRetry = 0; osal_stop_timerEx(zclOTA_TaskID, ZCL_OTA_BLOCK_RSP_TO_EVT); zclOTA_UpgradeComplete(ZOtaAbort); return ZSuccess; } else { return ZCL_STATUS_MALFORMED_COMMAND; } if (status != ZSuccess) { // download failed; set state to 'normal' zclOTA_ImageUpgradeStatus = OTA_STATUS_NORMAL; // send upgrade end req with failure status osal_memcpy(&req.fileId, ¶m.rsp.success.fileId, sizeof(zclOTA_FileID_t)); req.status = status; zclOTA_SendUpgradeEndReq( &(pInMsg->msg->srcAddr), &req ); } return ZSuccess; } /****************************************************************************** * @fn zclOTA_ProcessUpgradeEndRsp * * @brief Process received Upgrade End Response. * * @param pInMsg - pointer to the incoming message * * @return ZStatus_t */ static ZStatus_t zclOTA_ProcessUpgradeEndRsp( zclIncoming_t *pInMsg ) { zclOTA_UpgradeEndRspParams_t param; zclOTA_FileID_t currentFileId = {zclOTA_ManufacturerId, zclOTA_ImageType, zclOTA_DownloadedFileVersion}; uint8 *pData; // verify message length if (pInMsg->pDataLen != PAYLOAD_MAX_LEN_UPGRADE_END_RSP) { // no further processing if invalid return ZCL_STATUS_MALFORMED_COMMAND; } // parse message pData = pInMsg->pData; param.fileId.manufacturer = BUILD_UINT16(pData[0], pData[1]); pData += 2; param.fileId.type = BUILD_UINT16(pData[0], pData[1]); pData += 2; param.fileId.version = osal_build_uint32( pData, 4 ); pData += 4; param.currentTime = osal_build_uint32( pData, 4 ); pData += 4; param.upgradeTime = osal_build_uint32( pData, 4 ); // verify in 'download complete' or 'waiting for upgrade' state if ((zclOTA_ImageUpgradeStatus == OTA_STATUS_COMPLETE) || ((zclOTA_ImageUpgradeStatus == OTA_STATUS_UPGRADE_WAIT) && (param.upgradeTime!=OTA_UPGRADE_TIME_WAIT))) { // verify manufacturer, image type if (zclOTA_CmpFileId(¶m.fileId, ¤tFileId) == FALSE) { return ZSuccess; } // check upgrade time if (param.upgradeTime != OTA_UPGRADE_TIME_WAIT) { uint32 notifyDelay = 0; if (param.upgradeTime > param.currentTime) { // time to wait before notification notifyDelay = param.upgradeTime - param.currentTime; } // set state to 'countdown' zclOTA_ImageUpgradeStatus = OTA_STATUS_COUNTDOWN; // set timer for upgrade complete notification zclOTA_StartTimer(ZCL_OTA_UPGRADE_WAIT_EVT, notifyDelay); } else { // Wait for another upgrade end response zclOTA_ImageUpgradeStatus = OTA_STATUS_UPGRADE_WAIT; // Set a timer for 60 minutes to send another Upgrade End Rsp zclOTA_StartTimer(ZCL_OTA_UPGRADE_WAIT_EVT, 3600); zclOTA_UpgradeEndRetry = 0; } } return ZSuccess; } /****************************************************************************** * @fn zclOTA_ProcessQuerySpecificFileRsp * * @brief Process received Query Specific File Response. * * @param pInMsg - pointer to the incoming message * * @return ZStatus_t */ static ZStatus_t zclOTA_ProcessQuerySpecificFileRsp( zclIncoming_t *pInMsg ) { zclOTA_QueryImageRspParams_t param; uint8 *pData; // verify message length if ((pInMsg->pDataLen != PAYLOAD_MAX_LEN_QUERY_SPECIFIC_FILE_RSP) && (pInMsg->pDataLen != PAYLOAD_MIN_LEN_QUERY_SPECIFIC_FILE_RSP)) { // no further processing if invalid return ZCL_STATUS_MALFORMED_COMMAND; } // ignore message if in 'download in progress' state if (zclOTA_ImageUpgradeStatus == OTA_STATUS_IN_PROGRESS) { return ZSuccess; } // get status pData = pInMsg->pData; param.status = *pData++; // if status is success if (param.status == ZCL_STATUS_SUCCESS) { // parse message param.fileId.manufacturer = BUILD_UINT16(pData[0], pData[1]); pData += 2; param.fileId.type = BUILD_UINT16(pData[0], pData[1]); pData += 2; param.fileId.version = osal_build_uint32( pData, 4 ); pData += 4; param.imageSize = osal_build_uint32( pData, 4 ); // verify manufacturer id and image type if ((param.fileId.type == zclOTA_ImageType) && (param.fileId.manufacturer == zclOTA_ManufacturerId)) { // store file version and image size zclOTA_DownloadedFileVersion = param.fileId.version; zclOTA_DownloadedImageSize = param.imageSize; // initialize other variables zclOTA_FileOffset = 0; // set state to 'in progress' zclOTA_ImageUpgradeStatus = OTA_STATUS_IN_PROGRESS; // send image block request sendImageBlockReq(&(pInMsg->msg->srcAddr)); } } return ZSuccess; } /****************************************************************************** * @fn zclOTA_ClientHdlIncoming * * @brief Handle incoming client commands. * * @param pInMsg - pointer to the incoming message * * @return ZStatus_t */ static ZStatus_t zclOTA_ClientHdlIncoming( zclIncoming_t *pInMsg ) { switch (pInMsg->hdr.commandID) { case COMMAND_IMAGE_NOTIFY: return zclOTA_ProcessImageNotify( pInMsg ); case COMMAND_QUERY_NEXT_IMAGE_RSP: return zclOTA_ProcessQueryNextImageRsp( pInMsg ); case COMMAND_IMAGE_BLOCK_RSP: return zclOTA_ProcessImageBlockRsp( pInMsg ); case COMMAND_UPGRADE_END_RSP: return zclOTA_ProcessUpgradeEndRsp( pInMsg ); case COMMAND_QUERY_SPECIFIC_FILE_RSP: return zclOTA_ProcessQuerySpecificFileRsp( pInMsg ); default: return ZFailure; } } /****************************************************************************** * @fn zclOTA_CmpFileId * * @brief Called to compare two file IDs * * @param f1, f2 - Pointers to the two file IDs to compare * * @return TRUE if the file IDs are the same, else FALSE */ static uint8 zclOTA_CmpFileId(zclOTA_FileID_t *f1, zclOTA_FileID_t *f2) { if ((f1->manufacturer == 0xFFFF) || (f2->manufacturer == 0xFFFF) || (f1->manufacturer == f2->manufacturer)) { if ((f1->type == 0xFFFF) || (f2->type == 0xFFFF) || (f1->type == f2->type)) { if ((f1->version == 0xFFFFFFFF) || (f2->version == 0xFFFFFFFF) || (f1->version == f2->version)) { return TRUE; } } } return FALSE; } /****************************************************************************** * @fn zclOTA_ImageBlockWaitExpired * * @brief Perform action on image block wait timer expiration. * * @param none * * @return none */ static void zclOTA_ImageBlockWaitExpired(void) { // verify in 'in progress' state if (zclOTA_ImageUpgradeStatus == OTA_STATUS_IN_PROGRESS) { // request next block sendImageBlockReq(&zclOTA_serverAddr); } } /****************************************************************************** * @fn zclOTA_UpgradeComplete * * @brief Notify the application task that an upgrade has completed. * * @param status - The status of the upgrade * * @return none */ static void zclOTA_UpgradeComplete(uint8 status) { // Go back to the normal state zclOTA_ImageUpgradeStatus = OTA_STATUS_NORMAL; if ((zclOTA_DownloadedImageSize == OTA_HEADER_LEN_MIN_ECDSA) || (zclOTA_DownloadedImageSize == OTA_HEADER_LEN_MIN)) { status = ZFailure; } if (zclOTA_AppTask != 0xFF) { // Notify the application task the upgrade stopped zclOTA_CallbackMsg_t *pMsg; pMsg = (zclOTA_CallbackMsg_t*) osal_msg_allocate(sizeof(zclOTA_CallbackMsg_t)); if (pMsg) { pMsg->hdr.event = ZCL_OTA_CALLBACK_IND; pMsg->hdr.status = status; pMsg->ota_event = ZCL_OTA_DL_COMPLETE_CALLBACK; osal_msg_send(zclOTA_AppTask, (uint8*) pMsg); } } } /****************************************************************************** * @fn zclOTA_ProcessZDOMsgs * * @brief Process callbacks from the ZDO. * * @param pMsg - a Pointer to the message from the ZDO * * @return none */ static void zclOTA_ProcessZDOMsgs( zdoIncomingMsg_t *pMsg ) { if (pMsg->clusterID == IEEE_addr_rsp) { ZDO_NwkIEEEAddrResp_t *pNwkAddrRsp = ZDO_ParseAddrRsp( pMsg ); // If this is from the OTA server, record the server's IEEE address if (pNwkAddrRsp != NULL) { if (pNwkAddrRsp->nwkAddr == zclOTA_serverAddr.addr.shortAddr) { osal_memcpy(&zclOTA_UpgradeServerID, pNwkAddrRsp->extAddr, Z_EXTADDR_LEN); } osal_mem_free( pNwkAddrRsp ); } } } #endif // OTA_CLIENT #if defined (OTA_SERVER) && (OTA_SERVER == TRUE) /****************************************************************************** * @fn zclOTA_ProcessNextImgRsp * * @brief Handles a response to a MT_OTA_NEXT_IMG_RSP. * * @param pMsg - The data from the server. * pFileId - The ID of the OTA File. * pAddr - The source of the message. * * @return none */ void zclOTA_ProcessNextImgRsp(uint8* pMsg, zclOTA_FileID_t *pFileId, afAddrType_t *pAddr) { zclOTA_QueryImageRspParams_t queryRsp; uint8 options; uint8 status; // Get the status of the operation status = *pMsg++; // Get the options options = *pMsg++; // Copy the file ID osal_memcpy(&queryRsp.fileId, pFileId, sizeof(zclOTA_FileID_t)); // Set the image size if (status == ZSuccess) { queryRsp.status = ZSuccess; queryRsp.imageSize = BUILD_UINT32(pMsg[0], pMsg[1], pMsg[2], pMsg[3]); } else { queryRsp.status = ZOtaNoImageAvailable; queryRsp.imageSize = 0; } queryResponse = queryRsp; // save global variable for query image response. Used later in image block request check // Send a response to the client if (options & MT_OTA_QUERY_SPECIFIC_OPTION) { zclOTA_SendQuerySpecificFileRsp(pAddr, &queryRsp); } else { zclOTA_SendQueryNextImageRsp(pAddr, &queryRsp); } } /****************************************************************************** * @fn zclOTA_ProcessFileReadRsp * * @brief Handles a response to a MT_OTA_FILE_READ_RSP. * * @param pMsg - The data from the server. * pFileId - The ID of the OTA File. * pAddr - The source of the message. * * @return none */ void zclOTA_ProcessFileReadRsp(uint8* pMsg, zclOTA_FileID_t *pFileId, afAddrType_t *pAddr) { zclOTA_ImageBlockRspParams_t blockRsp; // Set the status blockRsp.status = *pMsg++; // Check the status of the file read if (blockRsp.status == ZSuccess) { // Fill in the response parameters osal_memcpy(&blockRsp.rsp.success.fileId, pFileId, sizeof(zclOTA_FileID_t)); blockRsp.rsp.success.fileOffset = BUILD_UINT32(pMsg[0], pMsg[1], pMsg[2], pMsg[3]); pMsg += 4; blockRsp.rsp.success.dataSize = *pMsg++; blockRsp.rsp.success.pData = pMsg; } else { blockRsp.status = ZOtaAbort; } // Send the block response to the peer zclOTA_SendImageBlockRsp(pAddr, &blockRsp); } /****************************************************************************** * @fn OTA_HandleFileSysCb * * @brief Handles File Server Callbacks. * * @param pMSGpkt - The data from the server. * * @return none */ void zclOTA_ServerHandleFileSysCb(OTA_MtMsg_t* pMSGpkt) { zclOTA_FileID_t pFileId; afAddrType_t pAddr; uint8 *pMsg; if (pMSGpkt != NULL) { // Get the File ID and AF Address pMsg = pMSGpkt->data; pMsg = OTA_StreamToFileId(&pFileId, pMsg); pMsg = OTA_StreamToAfAddr(&pAddr, pMsg); switch(pMSGpkt->cmd) { case MT_OTA_NEXT_IMG_RSP: zclOTA_ProcessNextImgRsp(pMsg, &pFileId, &pAddr); break; case MT_OTA_FILE_READ_RSP: zclOTA_ProcessFileReadRsp(pMsg, &pFileId, &pAddr); break; default: break; } } } /****************************************************************************** * @fn zclOTA_Srv_QueryNextImageReq * * @brief Handle a Query Next Image Request. * * @param pSrcAddr - The source of the message * pParam - message parameters * * @return ZStatus_t * * @note On a query next image, we must request a file listing * from the File Server. Then open a file if */ ZStatus_t zclOTA_Srv_QueryNextImageReq(afAddrType_t *pSrcAddr, zclOTA_QueryNextImageReqParams_t *pParam) { uint8 options = 0; uint8 status; if (zclOTA_Permit) { if (pParam->fieldControl) { options |= MT_OTA_HW_VER_PRESENT_OPTION; } // Request the next image for this device from the console via the MT File System status = MT_OtaGetImage(pSrcAddr, &pParam->fileId, pParam->hardwareVersion, NULL, options); } else { status = ZOtaNoImageAvailable; } if (status != ZSuccess) { zclOTA_QueryImageRspParams_t queryRsp; // Fill in the response parameters osal_memcpy(&queryRsp.fileId, &pParam->fileId, sizeof(zclOTA_FileID_t)); queryRsp.status = ZOtaNoImageAvailable; queryRsp.imageSize = 0; // Send a failure response to the client zclOTA_SendQueryNextImageRsp(pSrcAddr, &queryRsp); } return ZCL_STATUS_CMD_HAS_RSP; } /****************************************************************************** * @fn zclOTA_Srv_ImageBlockReq * * @brief Handle an Image Block Request. * * @param pSrcAddr - The source of the message * pParam - message parameters * * @return ZStatus_t */ ZStatus_t zclOTA_Srv_ImageBlockReq(afAddrType_t *pSrcAddr, zclOTA_ImageBlockReqParams_t *pParam) { uint8 status = ZFailure; if (pParam->fileId.version != queryResponse.fileId.version) { status = ZCL_STATUS_NO_IMAGE_AVAILABLE; } else { if (zclOTA_Permit && (pParam != NULL)) { uint8 len = pParam->maxDataSize; if (len > OTA_MAX_MTU) { len = OTA_MAX_MTU; } // Read the data from the OTA Console status = MT_OtaFileReadReq(pSrcAddr, &pParam->fileId, len, pParam->fileOffset); // Send a wait response to the client if (status != ZSuccess) { zclOTA_ImageBlockRspParams_t blockRsp; // Fill in the response parameters blockRsp.status = ZOtaWaitForData; osal_memcpy(&blockRsp.rsp.success.fileId, &pParam->fileId, sizeof(zclOTA_FileID_t)); blockRsp.rsp.wait.currentTime = 0; blockRsp.rsp.wait.requestTime = OTA_SEND_BLOCK_WAIT; // Send the block to the peer zclOTA_SendImageBlockRsp(pSrcAddr, &blockRsp); } status = ZCL_STATUS_CMD_HAS_RSP; } } return status; } /****************************************************************************** * @fn zclOTA_Srv_ImagePageReq * * @brief Handle an Image Page Request. Note: Not currently supported. * * @param pSrcAddr - The source of the message * pParam - message parameters * * @return ZStatus_t */ ZStatus_t zclOTA_Srv_ImagePageReq(afAddrType_t *pSrcAddr, zclOTA_ImagePageReqParams_t *pParam) { // Send not supported resposne return ZUnsupClusterCmd; } /****************************************************************************** * @fn zclOTA_Srv_UpgradeEndReq * * @brief Handle an Upgrade End Request. * * @param pSrcAddr - The source of the message * pParam - message parameters * * @return ZStatus_t */ ZStatus_t zclOTA_Srv_UpgradeEndReq(afAddrType_t *pSrcAddr, zclOTA_UpgradeEndReqParams_t *pParam) { uint8 status = ZFailure; if (zclOTA_Permit && (pParam != NULL)) { zclOTA_UpgradeEndRspParams_t rspParms; if (pParam->status == ZSuccess) { osal_memcpy(&rspParms.fileId, &pParam->fileId, sizeof(zclOTA_FileID_t)); rspParms.currentTime = osal_GetSystemClock(); rspParms.upgradeTime = rspParms.currentTime + OTA_UPGRADE_DELAY; // Send the response to the peer zclOTA_SendUpgradeEndRsp(pSrcAddr, &rspParms); } // Notify the Console Tool MT_OtaSendStatus(pSrcAddr->addr.shortAddr, MT_OTA_DL_COMPLETE, pParam->status, 0); status = ZCL_STATUS_CMD_HAS_RSP; } return status; } /****************************************************************************** * @fn zclOTA_Srv_QuerySpecificFileReq * * @brief Handles a Query Specific File Request. * * @param pSrcAddr - The source of the message * pParam - message parameters * * @return ZStatus_t */ ZStatus_t zclOTA_Srv_QuerySpecificFileReq(afAddrType_t *pSrcAddr, zclOTA_QuerySpecificFileReqParams_t *pParam) { uint8 status; // Request the image from the console if (zclOTA_Permit) { status = MT_OtaGetImage(pSrcAddr, &pParam->fileId, 0, pParam->nodeAddr, MT_OTA_QUERY_SPECIFIC_OPTION); } else { status = ZOtaNoImageAvailable; } if (status != ZSuccess) { zclOTA_QueryImageRspParams_t queryRsp; // Fill in the response parameters osal_memcpy(&queryRsp.fileId, &pParam->fileId, sizeof(zclOTA_FileID_t)); queryRsp.status = ZOtaNoImageAvailable; queryRsp.imageSize = 0; // Send a failure response to the client zclOTA_SendQuerySpecificFileRsp(pSrcAddr, &queryRsp); } return ZCL_STATUS_CMD_HAS_RSP; } /****************************************************************************** * @fn zclOTA_ProcessQueryNextImageReq * * @brief Process received Query Next Image Request. * * @param pInMsg - pointer to the incoming message * * @return ZStatus_t */ static ZStatus_t zclOTA_ProcessQueryNextImageReq( zclIncoming_t *pInMsg ) { zclOTA_QueryNextImageReqParams_t param; uint8 *pData; /* verify message length */ if ((pInMsg->pDataLen != PAYLOAD_MAX_LEN_QUERY_NEXT_IMAGE_REQ) && (pInMsg->pDataLen != PAYLOAD_MIN_LEN_QUERY_NEXT_IMAGE_REQ)) { /* no further processing if invalid */ return ZCL_STATUS_MALFORMED_COMMAND; } /* parse message parameters */ pData = pInMsg->pData; param.fieldControl = *pData++; param.fileId.manufacturer = BUILD_UINT16(pData[0], pData[1]); pData += 2; param.fileId.type = BUILD_UINT16(pData[0], pData[1]); pData += 2; param.fileId.version = osal_build_uint32( pData, 4 ); pData += 4; if ((param.fieldControl & 0x01) != 0) { param.hardwareVersion = BUILD_UINT16(pData[0], pData[1]); } /* call callback */ return zclOTA_Srv_QueryNextImageReq(&pInMsg->msg->srcAddr, ¶m); } /****************************************************************************** * @fn zclOTA_ProcessImageBlockReq * * @brief Process received Image Block Request. * * @param pInMsg - pointer to the incoming message * * @return ZStatus_t */ static ZStatus_t zclOTA_ProcessImageBlockReq( zclIncoming_t *pInMsg ) { zclOTA_ImageBlockReqParams_t param; uint8 *pData; /* verify message length */ if ((pInMsg->pDataLen != PAYLOAD_MAX_LEN_IMAGE_BLOCK_REQ) && (pInMsg->pDataLen != PAYLOAD_MIN_LEN_IMAGE_BLOCK_REQ)) { /* no further processing if invalid */ return ZCL_STATUS_MALFORMED_COMMAND; } /* parse message parameters */ pData = pInMsg->pData; param.fieldControl = *pData++; param.fileId.manufacturer = BUILD_UINT16(pData[0], pData[1]); pData += 2; param.fileId.type = BUILD_UINT16(pData[0], pData[1]); pData += 2; param.fileId.version = osal_build_uint32( pData, 4 ); pData += 4; param.fileOffset = osal_build_uint32( pData, 4 ); pData += 4; param.maxDataSize = *pData++; if ((param.fieldControl & 0x01) != 0) { osal_cpyExtAddr(param.nodeAddr, pData); } /* call callback */ return zclOTA_Srv_ImageBlockReq(&pInMsg->msg->srcAddr, ¶m); } /****************************************************************************** * @fn zclOTA_ProcessImagePageReq * * @brief Process received Image Page Request. * * @param pInMsg - pointer to the incoming message * * @return ZStatus_t */ static ZStatus_t zclOTA_ProcessImagePageReq( zclIncoming_t *pInMsg ) { zclOTA_ImagePageReqParams_t param; uint8 *pData; /* verify message length */ if ((pInMsg->pDataLen != PAYLOAD_MAX_LEN_IMAGE_PAGE_REQ) && (pInMsg->pDataLen != PAYLOAD_MIN_LEN_IMAGE_PAGE_REQ)) { /* no further processing if invalid */ return ZCL_STATUS_MALFORMED_COMMAND; } /* parse message parameters */ pData = pInMsg->pData; param.fieldControl = *pData++; param.fileId.manufacturer = BUILD_UINT16(pData[0], pData[1]); pData += 2; param.fileId.type = BUILD_UINT16(pData[0], pData[1]); pData += 2; param.fileId.version = osal_build_uint32( pData, 4 ); pData += 4; param.fileOffset = osal_build_uint32( pData, 4 ); pData += 4; param.maxDataSize = *pData++; param.pageSize = BUILD_UINT16(pData[0], pData[1]); pData += 2; param.responseSpacing = BUILD_UINT16(pData[0], pData[1]); pData += 2; if ((param.fieldControl & 0x01) != 0) { osal_cpyExtAddr(param.nodeAddr, pData); } /* call callback */ return zclOTA_Srv_ImagePageReq(&pInMsg->msg->srcAddr, ¶m); } /****************************************************************************** * @fn zclOTA_ProcessUpgradeEndReq * * @brief Process received Upgrade End Request. * * @param pInMsg - pointer to the incoming message * * @return ZStatus_t */ static ZStatus_t zclOTA_ProcessUpgradeEndReq( zclIncoming_t *pInMsg ) { zclOTA_UpgradeEndReqParams_t param; uint8 *pData; /* verify message length */ if ((pInMsg->pDataLen != PAYLOAD_MAX_LEN_UPGRADE_END_REQ) && (pInMsg->pDataLen != PAYLOAD_MIN_LEN_UPGRADE_END_REQ)) { /* no further processing if invalid */ return ZCL_STATUS_MALFORMED_COMMAND; } /* parse message parameters */ pData = pInMsg->pData; param.status = *pData++; if (param.status == ZCL_STATUS_SUCCESS) { param.fileId.manufacturer = BUILD_UINT16(pData[0], pData[1]); pData += 2; param.fileId.type = BUILD_UINT16(pData[0], pData[1]); pData += 2; param.fileId.version = osal_build_uint32( pData, 4 ); } /* call callback */ return zclOTA_Srv_UpgradeEndReq(&pInMsg->msg->srcAddr, ¶m); } /****************************************************************************** * @fn zclOTA_ProcessQuerySpecificFileReq * * @brief Process received Image Page Request. * * @param pInMsg - pointer to the incoming message * * @return ZStatus_t */ static ZStatus_t zclOTA_ProcessQuerySpecificFileReq( zclIncoming_t *pInMsg ) { zclOTA_QuerySpecificFileReqParams_t param; uint8 *pData; /* verify message length */ if (pInMsg->pDataLen != PAYLOAD_MAX_LEN_QUERY_SPECIFIC_FILE_REQ) { /* no further processing if invalid */ return ZCL_STATUS_MALFORMED_COMMAND; } /* parse message parameters */ pData = pInMsg->pData; osal_cpyExtAddr(param.nodeAddr, pData); pData += Z_EXTADDR_LEN; param.fileId.manufacturer = BUILD_UINT16(pData[0], pData[1]); pData += 2; param.fileId.type = BUILD_UINT16(pData[0], pData[1]); pData += 2; param.fileId.version = osal_build_uint32( pData, 4 ); pData += 4; param.stackVersion = BUILD_UINT16(pData[0], pData[1]); /* call callback */ return zclOTA_Srv_QuerySpecificFileReq(&pInMsg->msg->srcAddr, ¶m); } /****************************************************************************** * @fn zclOTA_ServerHdlIncoming * * @brief Handle incoming server commands. * * @param pInMsg - pointer to the incoming message * * @return ZStatus_t */ static ZStatus_t zclOTA_ServerHdlIncoming( zclIncoming_t *pInMsg ) { switch (pInMsg->hdr.commandID) { case COMMAND_QUERY_NEXT_IMAGE_REQ: return zclOTA_ProcessQueryNextImageReq( pInMsg ); case COMMAND_IMAGE_BLOCK_REQ: return zclOTA_ProcessImageBlockReq( pInMsg ); case COMMAND_IMAGE_PAGE_REQ: return zclOTA_ProcessImagePageReq( pInMsg ); case COMMAND_UPGRADE_END_REQ: return zclOTA_ProcessUpgradeEndReq( pInMsg ); case COMMAND_QUERY_SPECIFIC_FILE_REQ: return zclOTA_ProcessQuerySpecificFileReq( pInMsg ); default: return ZFailure; } } #endif // OTA_SERVER