/************************************************************************************************** Filename: oad_app.c Revised: $Date: 2011-05-16 10:25:15 -0700 (Mon, 16 May 2011) $ Revision: $Revision: 25990 $ Description: This file contains the implementation of an Over Air Download application. Copyright 2008-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 #include "AF.h" #include "hal_adc.h" #include "hal_board_cfg.h" #include "hal_flash.h" #include "hal_oad.h" #include "oad_app.h" #include "oad_preamble.h" #include "OnBoard.h" #include "OSAL_Nv.h" #if defined ZPORT #include "hal_key.h" #include "MT.h" #include "MT_APP.h" #include "MT_X.h" #include "ZDApp.h" #else #if (HAL_OAD_XNV_IS_INT && ((HAL_OAD_DL_OSET % HAL_FLASH_PAGE_SIZE) != 0)) #include "hal_xnv.h" #endif #endif /* ------------------------------------------------------------------------------------------------ * Macros * ------------------------------------------------------------------------------------------------ */ #define DO_EVENT_CALLBACK(e) st ( if (s_pCallback && ((e) & s_eventMask)) s_pCallback((e)); ) /* ------------------------------------------------------------------------------------------------ * Constants * ------------------------------------------------------------------------------------------------ */ #if !defined OAD_NV_ID // Arbitrarily pick the last Id available to the user Application. #define OAD_NV_ID 0x0FFF #endif #define PREAMBLE_NV_ID OAD_NV_ID // support to select callback event for which subscription is desired // this information is transmitted as a bit map. #define ZLCB_EVENT_OADBEGIN_CLIENT ((uint16)0x0001) #define ZLCB_EVENT_OADEND_CLIENT ((uint16)0x0002) #define ZLCB_EVENT_OADBEGIN_SERVER ((uint16)0x0004) #define ZLCB_EVENT_OADEND_SERVER ((uint16)0x0008) #define ZLCB_EVENT_CODE_ENABLE_RESET ((uint16)0x0010) #define ZLCB_EVENT_ALL (ZLCB_EVENT_OADBEGIN_CLIENT | \ ZLCB_EVENT_OADEND_CLIENT | \ ZLCB_EVENT_OADBEGIN_SERVER | \ ZLCB_EVENT_OADEND_SERVER | \ ZLCB_EVENT_CODE_ENABLE_RESET \ ) #define SDC_RETRY_COUNT (10) #define SDR_WAIT_TO (1000) // Some reasonable time to get the code enable response out. #define SDC_WAIT_TO_ENABLE (10000) /* ------------------------------------------------------------------------------------------------ * Typedefs * ------------------------------------------------------------------------------------------------ */ #if defined ZPORT #define SIZEOF_ZAIN_HDR (sizeof(uint16) + sizeof(uint8) + sizeof(uint16) + sizeof(uint8)) // Z-Architect headers // inbound to host (from dongle directly or external platform) PACK_1 typedef struct { uint16 zaproxy_nwkAddr; uint8 zaproxy_endp; uint16 zaproxy_ClusterID; uint8 zaproxy_msglen; uint8 zaproxy_payload[1]; } zahdrin_t; // outbound from host (to dongle directly or external platform) PACK_1 typedef struct { uint16 zaproxy_nwkAddr; uint8 zaproxy_endp; uint16 zaproxy_ClusterID; uint8 zaproxy_msglen; uint8 zaproxy_payload[1]; } zahdrout_t; #endif /* ------------------------------------------------------------------------------------------------ * Global Variables * ------------------------------------------------------------------------------------------------ */ uint8 oad_app_taskId; /* ------------------------------------------------------------------------------------------------ * Global Functions * ------------------------------------------------------------------------------------------------ */ #if defined ZPORT #define HalOADChkDL(V) 1 #define HalOADInvRC(V) #define HalOADAvail(V) HAL_OAD_DL_SIZE #define HalOADRead(A, B, C, D) #define HalOADWrite(A, B, C, D) #endif /* ------------------------------------------------------------------------------------------------ * Local Variables * ------------------------------------------------------------------------------------------------ */ static uint8 transId; static afAddrType_t dstAddr; static zlclientC_t *s_clientInfo; static uint8 s_State, s_SessionID, s_blkSize; static uint16 s_NextPacket, s_NumPktGet, s_SDCRetryCount; static zlmhdr_t *s_sdcmd; static zlsdC_t *s_sdcpayload; static zlmhdr_t *s_sdreply; #if !defined ZPORT static zlsdR_t *s_sdrpayload; static image_t s_itype; static uint8 s_lastSeen, s_firstTx = 1; #endif static void (*s_pCallback)(uint16); static uint16 s_eventMask; static uint8 s_lastTxSeqNum; #if defined ZPORT static uint8 s_serialMsg; static uint16 s_lastSeqNum; static uint16 s_myNwkAddr = 0xFFFE; // pass through stuff static uint8 s_PTSeqNum; static afIncomingMSGPacket_t s_PTClientInfo; #else #pragma location="PREAMBLE" const CODE preamble_t _preamble = { {PREAMBLE_MAGIC1, PREAMBLE_MAGIC2}, HAL_OAD_DL_SIZE, HAL_OAD_VERS, HAL_OAD_MANU, HAL_OAD_PROD }; #pragma required=_preamble #endif // This list should be filled with Application specific Cluster IDs. static const cId_t OAD_ClusterList[OAD_CLUSTER_CNT] = { OAD_CLUSTERID_CS //,OAD_CLUSTERID_EM }; static const SimpleDescriptionFormat_t OAD_SimpleDesc = { OAD_ENDPOINT, OAD_PROFILE_ID, OAD_DEVICEID, OAD_DEVICE_VERSION, OAD_FLAGS, OAD_CLUSTER_CNT, (cId_t *)OAD_ClusterList, 0, NULL }; static const endPointDesc_t OAD_epDesc= { OAD_ENDPOINT, &oad_app_taskId, (SimpleDescriptionFormat_t *)&OAD_SimpleDesc, noLatencyReqs, }; #if defined HAL_OAD_BL21 // this is the mailbox value that tells the boot code to flash the downloaded image // even though the operational image may be sane. #define MBOX_OAD_ENABLE 0x454E424C // 'ENBL' enable downloaded code PACK_1 typedef struct mbox_s { volatile unsigned long BootRead; volatile unsigned long AppRead; } mboxMsg_t; #pragma location="MBOXMSG_ADDR" __no_init mboxMsg_t mboxMsg; PACK_1 typedef struct { uint8 (*ReadFlash)(image_t, uint32 addr, uint8 *pBuf, uint16 len); uint8 (*WriteFlash)(image_t, uint32 addr, uint8 *pBuf, uint16 len); uint8 (*CheckCodeSanity)(image_t, uint32 addr1, uint32 addr2); uint8 (*GetPreamble)(image_t, uint32 addr, preamble_t *pBuf); } mbox_t; #pragma location="MBOX_ADDR" __no_init mbox_t mbox; #endif /* ------------------------------------------------------------------------------------------------ * Local Functions * ------------------------------------------------------------------------------------------------ */ static void procSysEvtMsg(void); static void zlResetState(void); static void ZLOADApp_MessageMSGCB( afIncomingMSGPacket_t * ); static void ZLOADApp_handleCommand(afIncomingMSGPacket_t *, zlmhdr_t *); static void ZLOADApp_handleReply(afIncomingMSGPacket_t *, zlmhdr_t *); static void zlCleanupOnReset(void); static void zlStartClientSession(void); static void zlProcessSDR(zlsdR_t *); static void zlRequestNextDataPacket(void); static void zlCleanupOnXferDone(void); static void zlResendSDC(void); #if defined ZPORT static void zlSendSerial(uint8 *, uint8); static void ZLOADApp_SerialMessageMSGCB(zahdrout_t *); static void zlZArchitectProxyMsg(zahdrout_t *); static uint8 zlPassOnStartSessionOK(uint8 *); static zahdrin_t *zlBuildExternalInboundSerialMSG(afIncomingMSGPacket_t *, uint8 *, uint8); static zahdrin_t *zlBuildInternalInboundSerialMSG(uint8 *, uint8); static void zlHandleKeys(uint8 shift, uint8 keys); #else static uint8 zlSendCommand(uint8, uint8 *); static void zlProcessSDC(zlsdC_t *); static uint8 zlIsReqPacketNumOK(uint16); #if (HAL_OAD_XNV_IS_INT && ((HAL_OAD_DL_OSET % HAL_FLASH_PAGE_SIZE) != 0)) static Status_t zlEraseHalfPage(void); #endif #endif /************************************************************************************************** * @fn oadAppInit * * @brief This function is called by OSAL system startup. * * input parameters * * @param id - The Task ID assigned by OSAL. * * output parameters * * None. * * @return None. ************************************************************************************************** */ void oadAppInit(uint8 id) { oad_app_taskId = id; afRegister((endPointDesc_t *)&OAD_epDesc); s_State = ZLSTATE_IDLE; id = PREAMBLE_OFFSET; osal_nv_item_init(PREAMBLE_NV_ID, 1, &id); #if defined HAL_OAD_BL21 mboxMsg.BootRead = 0; #endif #if defined ZPORT // Register for all key events - This app will handle all key events RegisterForKeys(oad_app_taskId); mtxMode = TRUE; #endif } /************************************************************************************************** * @fn oadAppEvt * * @brief This function is called to process the OSAL events for the task. * * input parameters * * @param id - The Task ID assigned by OSAL. * @param evts - A bit mask representing the OSAL events that are pending for this task. * * output parameters * * None. * * @return None. ************************************************************************************************** */ uint16 oadAppEvt(uint8 id, uint16 evts) { uint16 mask = 0; (void)id; if (evts & SYS_EVENT_MSG) { mask = SYS_EVENT_MSG; procSysEvtMsg(); } else if (evts & ZLOAD_CODE_ENABLE_EVT) { // let the user shut down the environment if requested DO_EVENT_CALLBACK(ZLCB_EVENT_CODE_ENABLE_RESET); #if defined HAL_OAD_BL21 // Set up mail box to tell the pre-2.2 boot code to flash the downloaded image. mboxMsg.BootRead = MBOX_OAD_ENABLE; #else HalOADInvRC(); #endif SystemReset(); } else if (evts & ZLOAD_IS_CLIENT_EVT) { mask = ZLOAD_IS_CLIENT_EVT; zlStartClientSession(); } else if (evts & ZLOAD_XFER_DONE_EVT) { mask = ZLOAD_XFER_DONE_EVT; zlCleanupOnXferDone(); } else if (evts & ZLOAD_RESET_EVT) { mask = ZLOAD_RESET_EVT; zlCleanupOnReset(); } else if (evts & ZLOAD_SDRTIMER_EVT) { mask = ZLOAD_SDRTIMER_EVT; zlResendSDC(); } else if (evts & ZLOAD_RESET_BOARD_EVT) { SystemResetSoft(); } else { mask = evts; // Discard unknown events - should never happen. } return (evts ^ mask); // Return unprocessed events. } /************************************************************************************************** * @fn procSysEvtMsg * * @brief This function is called by oadAppEvt() to process all of the pending OSAL messages. * * input parameters * * None. * * output parameters * * None. * * @return None. ************************************************************************************************** */ static void procSysEvtMsg(void) { uint8 *msgPtr; while ((msgPtr = osal_msg_receive(oad_app_taskId))) { #if defined ZPORT s_serialMsg = 0; #endif switch ( *msgPtr ) { case MT_SYS_APP_MSG: case MT_SYS_APP_RSP_MSG: #if defined ZPORT // This is how we get messages from a Host application (ZOAD.exe). s_serialMsg = 1; ZLOADApp_SerialMessageMSGCB((zahdrout_t *)(((mtSysAppMsg_t *)msgPtr)->appData)); #endif break; case AF_DATA_CONFIRM_CMD: break; case AF_INCOMING_MSG_CMD: ZLOADApp_MessageMSGCB((afIncomingMSGPacket_t *)msgPtr); break; #if defined ZPORT case KEY_CHANGE: zlHandleKeys(((keyChange_t *)msgPtr)->state, ((keyChange_t *)msgPtr)->keys); break; #endif case ZDO_NEW_DSTADDR: /* dstEP = msgPtr[1]; dstAddr = (zAddrType_t *)&msgPtr[2]; dstAddr.addrMode = dstAddr->addrMode; dstAddr.endPoint = dstEP; if (dstAddr->addrMode == afAddr16Bit) dstAddr.addr.shortAddr = dstAddr->addr.shortAddr; else osal_memcpy(dstAddr.addr.extAddr, dstAddr->addr.extAddr, Z_EXTADDR_LEN); */ break; case ZDO_STATE_CHANGE: #if defined ZPORT if (((devStates_t)(((osal_event_hdr_t *)msgPtr)->status) == DEV_END_DEVICE_UNAUTH) || ((devStates_t)(((osal_event_hdr_t *)msgPtr)->status) == DEV_END_DEVICE)) { MT_X_FakeNwkJoinCnf(); } #endif break; default: break; } osal_msg_deallocate(msgPtr); // Receiving task is responsible for releasing the memory. } } /********************************************************************************** * @fn ZLOADApp_MessageMSGCB * * @brief Handle a normal ZLOAD command or reply. Forward it to proper handler * * @param MSGpkt - pointer to incoming AF packet * * @return none */ static void ZLOADApp_MessageMSGCB(afIncomingMSGPacket_t *MSGpkt) { zlmhdr_t *inMsg = (zlmhdr_t *)MSGpkt->cmd.Data; if (inMsg->zlhdr_msgid & ZLMSGID_REPLY_BIT) { ZLOADApp_handleReply(MSGpkt, inMsg); } else { ZLOADApp_handleCommand(MSGpkt, inMsg); } return; } /********************************************************************************** * @fn ZLOADApp_handleCommand * * @brief Handle a normal ZLOAD command * * @param input * MSGpkt pointer to incoming AF packet. needed to get * reply address info * msg pointer to ZLOAD message * * @return none */ static void ZLOADApp_handleCommand(afIncomingMSGPacket_t *MSGpkt, zlmhdr_t *msg) { uint8 *cpc, *cpr, paylSize; preamble_t preamble; #if defined ZPORT static uint8 *buf; if (!buf) { if (!(buf = osal_mem_alloc(sizeof(zlmhdr_t) + sizeof(zlreply_t)))) { return; } } #else uint8 *buf = NULL; // allocate the reply buffer. use a union of the replies -- they're all pretty // small -- except for Send Data. that one is preallocated because we want // to really have it and not spend time doing malloc/free for so many transactions // besides, we don't want a malloc() to fail during the transfer. if (msg->zlhdr_msgid != ZLMSGID_SEND_DATA) { if (!(buf = osal_mem_alloc(sizeof(zlmhdr_t) + sizeof(zlreply_t)))) { return; } } else { // make sure we don't have a premature send-data command. this is awkward because // in the normal case we've done a "static" allocation (for the durtion of the session) // to prevent thousands of alloc/free calls or a failed alloc. but now, we dont' have // that "static" memory yet. if (s_State != ZLSTATE_SERVER) { // uh oh. we never got a start session. now we need memory to // send the reply if (!(buf = osal_mem_alloc(sizeof(zlmhdr_t) + sizeof(zlsdR_t)))) { return; } // make the rest of the code work s_sdreply = (zlmhdr_t *)buf; s_sdreply->zlhdr_msgid = ZLMSGID_SEND_DATA | ZLMSGID_REPLY_BIT; s_sdreply->zlhdr_msglen = sizeof(zlsdR_t); s_sdrpayload = (zlsdR_t *)(buf+sizeof(zlmhdr_t)); s_sdrpayload->zlsdR_state = s_State; s_sdrpayload->zlsdR_errorCode = EC_SD_NOT_SERVER; } } #endif cpr = buf + sizeof(zlmhdr_t); // offset of reply payload cpc = (uint8 *)msg + sizeof(zlmhdr_t); // offset of command payload switch (msg->zlhdr_msgid) { case ZLMSGID_STATUSQ: { uint8 dlImagePreambleOffset; zlstatusR_t *reply = (zlstatusR_t *)cpr; paylSize = sizeof(zlstatusR_t); reply->zlsqR_state = s_State; reply->zlsqR_errorCode = EC_NO_ERROR; // report capabiltities and version reply->zlsqR_ProtocolVersion = ZLOAD_PROTOCOL_VERSION; reply->zlsqR_capabilties = ZLOAD_CAPABILTIES; cpr +=4; // populate the operational version values HalOADRead(PREAMBLE_OFFSET, (uint8 *)&preamble, sizeof(preamble_t), HAL_OAD_RC); cpr = osal_memcpy(cpr, &preamble.vers, ZL_IMAGE_ID_LENGTH); // do downloaded image if it's there osal_nv_read(PREAMBLE_NV_ID, 0, 1, &dlImagePreambleOffset); HalOADRead(dlImagePreambleOffset, (uint8 *)&preamble, sizeof(preamble_t), HAL_OAD_DL); if (preamble.vers != 0xFF) { cpr = osal_memcpy(cpr, &preamble.vers, ZL_IMAGE_ID_LENGTH); } else { osal_memset(cpr, 0, ZL_IMAGE_ID_LENGTH); cpr += ZL_IMAGE_ID_LENGTH; } cpr = osal_memcpy(cpr, &s_NextPacket, sizeof(s_NextPacket)); osal_memcpy(cpr, &s_NumPktGet, sizeof(s_NumPktGet)); } break; case ZLMSGID_SESSION_START: #if defined ZPORT // the pass through condition starts here, when a client tries // to begin a session with the dongle. if (s_State != ZLSTATE_IDLE) { ((zlbegsessR_t *)cpr)->zlbsR_errorCode = EC_BS_NOT_IDLE; } else if (s_serialMsg) { // somehow this came over the serial port and it isn't legal ((zlbegsessR_t *)cpr)->zlbsR_errorCode = EC_BS_NOT_SERVER; } else { // set mode. delay reply until we hear back from the Host s_PTSeqNum = msg->zlhdr_seqnum; s_PTClientInfo = *MSGpkt; s_SessionID = ((zlbegsessC_t *)cpc)->zlbsC_sessionID; if (zlPassOnStartSessionOK((uint8 *)msg)) { s_State = ZLSTATE_PASS_THROUGH; return; } else { ((zlbegsessR_t *)cpr)->zlbsR_errorCode = EC_BS_NO_MEM; } } #else if (!HalAdcCheckVdd(VDD_MIN_OAD)) { ((zlbegsessR_t *)cpr)->zlbsR_errorCode = EC_BAD_VDD; } else do { uint8 dlImagePreambleOffset; zlbegsessR_t *reply = (zlbegsessR_t *)cpr; // assume we're OK and set up fixed part of reply here reply->zlbsR_blkSize = ZL_DATA_BLK_SIZE; reply->zlbsR_numBlks = ZL_NUM_DATA_BLKS; reply->zlbsR_state = s_State; reply->zlbsR_errorCode = EC_NO_ERROR; paylSize = sizeof(zlbegsessR_t); if (s_State != ZLSTATE_IDLE) { if (s_SessionID == ((zlbegsessC_t *)cpc)->zlbsC_sessionID) { // Ok - this must be a no ack retransmit or a retry. } else { reply->zlbsR_errorCode = EC_BS_NOT_IDLE; continue; // done } } // see if there is a DL image to send osal_nv_read(PREAMBLE_NV_ID, 0, 1, &dlImagePreambleOffset); HalOADRead(dlImagePreambleOffset, (uint8 *)&preamble, sizeof(preamble_t), HAL_OAD_DL); if (preamble.vers != 0xFFFF) { if (!memcmp(cpc, (uint8 *)&preamble.vers, ZL_IMAGE_ID_LENGTH)) { // image matches request. osal_memcpy(&reply->zlbsR_imgLen, &preamble.len, sizeof(uint32)); s_State = ZLSTATE_SERVER; s_itype = HAL_OAD_DL; reply->zlbsR_preambleOffset = (uint8)PREAMBLE_OFFSET; continue; // done } } // no DL image. we know there's an operational image HalOADRead(PREAMBLE_OFFSET, (uint8 *)&preamble, sizeof(preamble_t), HAL_OAD_RC); if (!memcmp(cpc, (uint8 *)&preamble.vers, ZL_IMAGE_ID_LENGTH)) { // image matches request. osal_memcpy(&reply->zlbsR_imgLen, &preamble.len, sizeof(uint32)); s_State = ZLSTATE_SERVER; s_itype = HAL_OAD_RC; reply->zlbsR_preambleOffset = (uint8)PREAMBLE_OFFSET; } else { reply->zlbsR_errorCode = EC_BS_NO_MATCHES; } } while (0); // if we're good to go, allocate the memory for the SD replies // this will make things more efficient during the transfer if (!((zlbegsessR_t *)cpr)->zlbsR_errorCode) { uint8 *cp; zlbegsessR_t *reply = (zlbegsessR_t *)cpr; // need memory for sending replies and for holding flash pages if (!(cp=osal_mem_alloc(sizeof(zlmhdr_t)+sizeof(zlsdR_t)))) { // oops -- none available. let Client decide what to do. s_State = ZLSTATE_IDLE; reply->zlbsR_errorCode = EC_BS_NO_MEM; } else { s_NumPktGet = (preamble.len + (ZL_DATA_BLK_SIZE*ZL_NUM_DATA_BLKS-1)) / (ZL_DATA_BLK_SIZE*ZL_NUM_DATA_BLKS); s_blkSize = ZL_DATA_BLK_SIZE * ZL_NUM_DATA_BLKS; s_NextPacket = 0; s_SessionID = ((zlbegsessC_t *)cpc)->zlbsC_sessionID; // set up other pointers s_sdreply = (zlmhdr_t *)cp; s_sdreply->zlhdr_msgid = ZLMSGID_SEND_DATA | ZLMSGID_REPLY_BIT; s_sdreply->zlhdr_msglen = sizeof(zlsdR_t); s_sdrpayload = (zlsdR_t *)(cp+sizeof(zlmhdr_t)); DO_EVENT_CALLBACK(ZLCB_EVENT_OADBEGIN_SERVER); } } #endif break; case ZLMSGID_SESSION_TERM: { zlendsessR_t *reply = (zlendsessR_t *)cpr; reply->zlesR_state = s_State; reply->zlesR_errorCode = EC_NO_ERROR; // guard against terminating the wrong session if (((zlendsessC_t *)cpc)->zlesC_sessionID != s_SessionID) { // wrong session ID reply->zlesR_errorCode = EC_ES_BAD_SESS_ID; } #if defined ZPORT else if (s_serialMsg) { // somehow this came over the serial port and it isn't legal ((zlbegsessR_t *)cpr)->zlbsR_errorCode = EC_ES_NOT_SERVER; } #endif else { #if defined ZPORT if (ZLSTATE_PASS_THROUGH == s_State) { // everything is OK. pass this up to host if we're in pass-through mode // forward message to host zahdrin_t *zain = zlBuildExternalInboundSerialMSG(&s_PTClientInfo, (uint8 *)msg, sizeof(zlmhdr_t) + sizeof(zlendsessC_t)); if (zain) { if (SUCCESS != osal_start_timerEx(oad_app_taskId, ZLOAD_XFER_DONE_EVT, SDR_WAIT_TO)) { osal_set_event(oad_app_taskId, ZLOAD_XFER_DONE_EVT); } s_PTClientInfo = *MSGpkt; zlSendSerial((uint8 *)zain, SIZEOF_ZAIN_HDR + sizeof(zlmhdr_t) + sizeof(zlendsessC_t)); osal_mem_free(zain); return; } else { reply->zlesR_errorCode = EC_ES_NO_MEM; } } #else if (s_State == ZLSTATE_SERVER) { // everything is OK. osal_set_event(oad_app_taskId, ZLOAD_XFER_DONE_EVT); } #endif else { // right session ID but I'm not the server reply->zlesR_errorCode = EC_ES_NOT_SERVER; } } } paylSize = sizeof(zlendsessR_t); break; case ZLMSGID_CLIENT_CMD: { zlclientR_t *reply = (zlclientR_t *)cpr; reply->zlclR_errorCode = EC_NO_ERROR; paylSize = sizeof(zlclientR_t); reply->zlclR_state = s_State; #if defined ZPORT // we can be the client only if the dongle code itself is being updated // and then only if the Host is the server if (!s_serialMsg) { reply->zlclR_errorCode = EC_CL_NOT_CLIENT; } else #endif if (!HalAdcCheckVdd(VDD_MIN_OAD)) { reply->zlclR_errorCode = EC_BAD_VDD; } else if (s_State != ZLSTATE_IDLE) { if (!osal_memcmp(s_clientInfo, cpc, sizeof(zlclientC_t))) { reply->zlclR_errorCode = EC_CL_NOT_IDLE; } } #if (HAL_OAD_XNV_IS_INT && ((HAL_OAD_DL_OSET % HAL_FLASH_PAGE_SIZE) != 0)) // Bug 2946 - HalXNVWrite() only triggers a page erase when the first byte of a // page boundary is written, so when dividing the available internal flash in half // results in a page being split in half, this remedial measure is necessary. else if (zlEraseHalfPage() != SUCCESS) { reply->zlclR_errorCode = EC_CL_NO_MEM; } #endif else { zlclientC_t *cmd = (zlclientC_t *)cpc; uint8 *cp; // need memory for holding client info, and current send-data cmd // do one alloc and set up the pointers cp = osal_mem_alloc(sizeof(zlclientC_t)+sizeof(zlmhdr_t)+sizeof(zlsdC_t)); if (!cp) { // no more memory. let Commissioner decide what to do. maybe retry later. reply->zlclR_errorCode = EC_CL_NO_MEM; break; } else { // set up other pointers s_clientInfo = (zlclientC_t *)cp; s_sdcmd = (zlmhdr_t *)(cp+sizeof(zlclientC_t)); s_sdcpayload = (zlsdC_t *)(cp+sizeof(zlclientC_t)+sizeof(zlmhdr_t)); } // the info on the image to be downloaded and // the address of the Server is now saved both here // and in persistent memory osal_memcpy(s_clientInfo, cpc, sizeof(zlclientC_t)); // populate destination address structure for convenience dstAddr.addrMode = afAddr16Bit; dstAddr.endPoint = cmd->zlclC_endp; osal_memcpy(&dstAddr.addr.shortAddr, &cmd->zlclC_nwk, sizeof(uint16)); // set event to cause the session to begin osal_set_event(oad_app_taskId, ZLOAD_IS_CLIENT_EVT); } } break; case ZLMSGID_CODE_ENABLE: { zlceR_t *reply = (zlceR_t *)cpr; paylSize = sizeof(zlceR_t); reply->zlceR_state = s_State; #if defined ZPORT // we can enable code if the dongle code itself is being updated. if (!s_serialMsg) { reply->zlceR_errorCode = EC_CE_NOT_CLIENT; } else #endif if (s_State != ZLSTATE_IDLE) { reply->zlceR_errorCode = EC_CE_NOT_IDLE; } // see if we're supposed to enable the DL image. spec is in command payload // see if there is one... else { uint8 dlImagePreambleOset; osal_nv_read(PREAMBLE_NV_ID, 0, 1, &dlImagePreambleOset); HalOADRead(dlImagePreambleOset,(uint8 *)&preamble,sizeof(preamble_t),HAL_OAD_DL); if (preamble.vers != 0xFFFF) { // see if they match if (!memcmp(cpc, (uint8 *)&preamble.vers, ZL_IMAGE_ID_LENGTH)) { if (!HalAdcCheckVdd(VDD_MIN_OAD)) { reply->zlceR_errorCode = EC_BAD_VDD; } else // DL image there and matches request, see if image is sane. if (SUCCESS == HalOADChkDL(dlImagePreambleOset)) { //set event to cause reset if (SUCCESS != osal_start_timerEx(oad_app_taskId, ZLOAD_CODE_ENABLE_EVT, SDC_WAIT_TO_ENABLE)) { osal_set_event(oad_app_taskId, ZLOAD_CODE_ENABLE_EVT); } reply->zlceR_errorCode = EC_NO_ERROR; } else { reply->zlceR_errorCode = EC_CE_IMAGE_INSANE; } } else { // DL image there but doesn't match request reply->zlceR_errorCode = EC_CE_NO_MATCH; } } else { // no DL image reply->zlceR_errorCode = EC_CE_NO_IMAGE; } } } break; case ZLMSGID_SEND_DATA: #if defined ZPORT { zlsdR_t *reply = (zlsdR_t *)cpr; paylSize = sizeof(zlsdR_t); // do a sanity check. if (s_serialMsg) { reply->zlsdR_errorCode = EC_SD_NOT_SERVER; } else if (ZLSTATE_PASS_THROUGH == s_State) { // this should be good enough... if (s_PTClientInfo.srcAddr.addr.shortAddr != MSGpkt->srcAddr.addr.shortAddr) { // wrong client -- not the current client reply->zlsdR_errorCode = EC_SD_NOT_CLIENT; } else if (((zlsdC_t *)cpc)->zlsdC_sessionID != s_SessionID) { reply->zlsdR_errorCode = EC_SD_BAD_SESS_ID; } else { zahdrin_t *zain = zlBuildExternalInboundSerialMSG(&s_PTClientInfo, (uint8 *)msg, sizeof(zlmhdr_t) + sizeof(zlsdC_t)); // forward message to host if (zain) { zlSendSerial((uint8 *)zain, SIZEOF_ZAIN_HDR + sizeof(zlmhdr_t) + sizeof(zlsdC_t)); osal_mem_free(zain); // save the AF data. there may be AF parameters we need for the reply s_PTClientInfo = *MSGpkt; return; } else { reply->zlsdR_errorCode = EC_SD_NO_MEM; } } } else { reply->zlsdR_errorCode = EC_SD_NOT_SERVER; } } #else paylSize = sizeof(zlsdR_t); if (s_State != ZLSTATE_SERVER) { // this is the error case when we get a SD before we've started a session break; } s_sdrpayload->zlsdR_state = s_State; buf = (uint8 *)s_sdreply; if (((zlsdC_t *)cpc)->zlsdC_sessionID != s_SessionID) { s_sdrpayload->zlsdR_errorCode = EC_SD_BAD_SESS_ID; } else { // set error code first. it might get reset in the processing routine s_sdrpayload->zlsdR_errorCode = EC_NO_ERROR; // process the command zlProcessSDC((zlsdC_t *)cpc); } #endif break; case ZLMSGID_RESET: #if defined ZPORT // always legal if over serial port if (!s_serialMsg) { return; } else #endif { zlrstR_t *reply = (zlrstR_t *)cpr; reply->zlrstR_state = s_State; reply->zlrstR_errorCode = EC_NO_ERROR; } paylSize = sizeof(zlrstR_t); osal_set_event(oad_app_taskId, ZLOAD_RESET_EVT); break; default: return; } // the case above has filled in the payload. the header is the // same for all. go ahead and fill in header and send reply { zlmhdr_t *hdr = (zlmhdr_t *)buf; hdr->zlhdr_msgid = msg->zlhdr_msgid | ZLMSGID_REPLY_BIT; hdr->zlhdr_seqnum = msg->zlhdr_seqnum; hdr->zlhdr_msglen = paylSize; #if defined ZPORT if (!s_serialMsg) { #endif AF_DataRequest( &MSGpkt->srcAddr, afFindEndPointDesc( MSGpkt->endPoint), MSGpkt->clusterId, sizeof(zlmhdr_t) + paylSize, buf, &MSGpkt->cmd.TransSeqNumber, AF_DISCV_ROUTE, AF_DEFAULT_RADIUS ); #if defined ZPORT } else { zahdrin_t *zain = zlBuildInternalInboundSerialMSG((uint8 *)hdr, sizeof(zlmhdr_t) + paylSize); if (zain) { zlSendSerial((uint8 *)zain, SIZEOF_ZAIN_HDR + sizeof(zlmhdr_t) + paylSize); osal_mem_free(zain); } } #else if ((buf != NULL) && ((msg->zlhdr_msgid != ZLMSGID_SEND_DATA) || (s_State != ZLSTATE_SERVER))) { osal_mem_free(buf); } #endif } return; } #if !defined ZPORT /********************************************************************************** * @fn zlProcessSDC * * @brief Process the SDC command. * * @param cmd - A valid alsdC_t structure with valid data. * * @return none */ static void zlProcessSDC(zlsdC_t *cmd) { uint16 reqPktNum; osal_memcpy(&reqPktNum, &cmd->zlsdC_pktNum, sizeof(uint16)); // if the requested packet is the previous one the Client didn't get the // transmission for some reason. just resend the previous one. the data // are already in there. if we never got the command this test will fail. if (s_NextPacket && (reqPktNum == (s_NextPacket-1))) { return; } // make sure the request is otherwise valid if (!zlIsReqPacketNumOK(reqPktNum)) { s_sdrpayload->zlsdR_errorCode = EC_SD_BAD_PKT_NUM; return; } // Read data into reply buffer. HalOADRead((uint32)s_NextPacket * s_blkSize, s_sdrpayload->zlsdR_data, ZL_DATA_BLK_SIZE*ZL_NUM_DATA_BLKS, s_itype); // set packet number in reply osal_memcpy(&s_sdrpayload->zlsdR_pktNum, &reqPktNum, sizeof(uint16)); s_NextPacket++; } /********************************************************************************** * @fn zlIsReqPacketNumOK * * @brief Check validity of resquested packet number as Server * * @param input * reqNum requested packet number * * @return 0: packet number request illegal * 1: packet number request is valid */ static uint8 zlIsReqPacketNumOK(uint16 reqNum) { if (reqNum >= s_NumPktGet) { return 0; } return 1; } #endif /********************************************************************************** * @fn ZLOADApp_handleReply * * @brief Handle a normal ZLOAD reply * * @param input * msg pointer to ZLOAD message * * @return none. */ static void ZLOADApp_handleReply(afIncomingMSGPacket_t *MSGpkt, zlmhdr_t *msg) { uint8 *cpr; #if defined ZPORT uint8 msgSize = 0; #endif #if !defined ZPORT // right reply? if (s_firstTx) { s_firstTx = 0; } else { if (msg->zlhdr_seqnum != s_lastTxSeqNum) { return; } if (msg->zlhdr_seqnum == s_lastSeen) { // duplicate reply to a resend return; } } s_lastSeen = msg->zlhdr_seqnum; #endif cpr = (uint8 *)msg + sizeof(zlmhdr_t); // offset of reply payload // generate replies to commands here. reply may generate another command out, for // example, if data are being transfered and the dvice is acting as client. // Commands handled here. replies in next routine // ignore reply bit on switch() switch (msg->zlhdr_msgid & (0xFF ^ ZLMSGID_REPLY_BIT)) { case ZLMSGID_STATUSQ: #if defined ZPORT msgSize = 20; // * * * NO BREAK * * * case ZLMSGID_CLIENT_CMD: case ZLMSGID_RESET: case ZLMSGID_CODE_ENABLE: if (!msgSize) { msgSize = 2; } // right now, only the host could have sent these -- the dongle never sends these // commands on its own. it's a proxy reply. forward it on... if (!s_serialMsg) { zahdrin_t *zain = zlBuildExternalInboundSerialMSG(MSGpkt, (uint8 *)msg, sizeof(zlmhdr_t) + msgSize); if (zain) { zlSendSerial((uint8 *)zain, SIZEOF_ZAIN_HDR + sizeof(zlmhdr_t) + msgSize); osal_mem_free(zain); } } #endif break; case ZLMSGID_SESSION_START: #if defined ZPORT // this can happen both in pass-through and client mode -- the dongle could be // getting updated firmware and is the client receiving the start session reply. // in any case we cannot get this reply over air. it has to have come from the Host if (!s_serialMsg) { break; } else if (ZLSTATE_PASS_THROUGH == s_State) { // pass reply back to original client msg->zlhdr_seqnum = s_PTSeqNum; AF_DataRequest( &s_PTClientInfo.srcAddr, afFindEndPointDesc( s_PTClientInfo.endPoint ), s_PTClientInfo.clusterId, sizeof(zlmhdr_t) + sizeof(zlbegsessR_t), (uint8 *)msg, &s_PTClientInfo.cmd.TransSeqNumber, AF_TX_OPTIONS_NONE, AF_DEFAULT_RADIUS ); } else if (((zlbegsessR_t *)cpr)->zlbsR_errorCode) { // Server rejected the real client session. back to Idle state. osal_set_event(oad_app_taskId, ZLOAD_RESET_EVT); } // reply from Server to Begin Session command from Client else if (ZLSTATE_CLIENT != s_State) { // I didn't send it...ignore. break; } #else // reply from Server to Begin Session command from Client if (s_State != ZLSTATE_CLIENT) { // I didn't send it...ignore. break; } else if (((zlbegsessR_t *)cpr)->zlbsR_errorCode) { // Server rejected the session. back to Idle state. osal_set_event(oad_app_taskId, ZLOAD_RESET_EVT); } #endif else { // get length and make sure we have room uint32 imglen = ((zlbegsessR_t *)cpr)->zlbsR_imgLen; if (imglen > HalOADAvail()) { // no room. terminate session osal_set_event(oad_app_taskId, ZLOAD_XFER_DONE_EVT); } else { // TODO: The OAD Dongle Tool is broken and passes the wrong offset. //uint8 dlImagePreambleOffset = ((zlbegsessR_t *)cpr)->zlbsR_preambleOffset; //osal_nv_write(PREAMBLE_NV_ID, 0, 1, &dlImagePreambleOffset); s_blkSize = ((zlbegsessR_t *)cpr)->zlbsR_blkSize; s_blkSize *= ((zlbegsessR_t *)cpr)->zlbsR_numBlks; // figure out how many data packets to request. s_NumPktGet = (imglen+(s_blkSize-1)) / s_blkSize; s_NextPacket = 0; // request "next" (i.e., first) packet zlRequestNextDataPacket(); } } break; case ZLMSGID_SESSION_TERM: #if defined ZPORT // pass it back if we're in pass-through mode if (s_serialMsg && (ZLSTATE_PASS_THROUGH == s_State)) { AF_DataRequest( &s_PTClientInfo.srcAddr, afFindEndPointDesc( s_PTClientInfo.endPoint), s_PTClientInfo.clusterId, sizeof(zlmhdr_t) + sizeof(zlendsessR_t), (uint8 *)msg, &s_PTClientInfo.cmd.TransSeqNumber, AF_TX_OPTIONS_NONE, AF_DEFAULT_RADIUS ); // in any case, clean up osal_set_event(oad_app_taskId, ZLOAD_XFER_DONE_EVT); } #else // don't care. right now the Commisioner can't be // the client so it will never get a reply to the // terminate session command because it can't send // that command. #endif break; case ZLMSGID_SEND_DATA: #if defined ZPORT if (!s_serialMsg) { // reply should not be over air break; } else if (ZLSTATE_CLIENT == s_State) { // not a don't care if we're in client state zlProcessSDR((zlsdR_t *)cpr); } else if (ZLSTATE_PASS_THROUGH == s_State) { // forward to client AF_DataRequest( &s_PTClientInfo.srcAddr, afFindEndPointDesc( s_PTClientInfo.endPoint), s_PTClientInfo.clusterId, sizeof(zlmhdr_t) + sizeof(zlsdR_t), (uint8 *)msg, &s_PTClientInfo.cmd.TransSeqNumber, AF_TX_OPTIONS_NONE, AF_DEFAULT_RADIUS ); } #else // not a don't care if we're in client state if (s_State == ZLSTATE_CLIENT) { zlProcessSDR((zlsdR_t *)cpr); } #endif break; default: // don't care break; } } /********************************************************************************** * @fn zlProcessSDR * * @brief Process the Send Data Reply as Client * * @param input * sdr pointer to received Send Data Reply * * @return none */ static void zlProcessSDR(zlsdR_t *sdr) { uint16 tmp; osal_memcpy(&tmp, &sdr->zlsdR_pktNum, sizeof(uint16)); // is this the right packet? if (s_NextPacket > tmp) { return; // we've already seen this one. ignore it } else if (s_NextPacket < tmp) { // uh oh. we've missed one. kill session... osal_set_event(oad_app_taskId, ZLOAD_XFER_DONE_EVT); return; } else if (sdr->zlsdR_errorCode) { // there are no data here. just ignaore the packet. if things // are relly screwed up the timer will expire and we'll quit. return; } else if (!HalAdcCheckVdd(VDD_MIN_OAD)) { osal_set_event(oad_app_taskId, ZLOAD_XFER_DONE_EVT); return; } // Store the data in Xtra-NV. HalOADWrite((uint32)s_NextPacket * s_blkSize, sdr->zlsdR_data, s_blkSize, HAL_OAD_DL); s_NextPacket++; // OK to increment next packet number now. zlRequestNextDataPacket(); // ask for next packet } /********************************************************************************** * @fn zlRequestNextDataPacket * * @brief Set up the next data packet request. Takes care of knowing when * transfer is done. Takes care of retry timer. * * @param none * * @return none */ static void zlRequestNextDataPacket() { if (s_NextPacket >= s_NumPktGet) { osal_set_event(oad_app_taskId, ZLOAD_XFER_DONE_EVT); // kill response timeout check osal_stop_timerEx(oad_app_taskId, ZLOAD_SDRTIMER_EVT); return; } #if defined ZPORT s_sdcpayload->zlsdC_pktNum = s_NextPacket; s_sdcmd->zlhdr_seqnum = ++s_lastSeqNum; { zahdrin_t *zain = zlBuildInternalInboundSerialMSG((uint8 *)s_sdcmd, sizeof(zlmhdr_t) + sizeof(zlsdC_t)); if (zain) { zlSendSerial((uint8 *)zain, SIZEOF_ZAIN_HDR + sizeof(zlmhdr_t) + sizeof(zlsdC_t)); osal_mem_free(zain); } } #else osal_memcpy(&s_sdcpayload->zlsdC_pktNum, &s_NextPacket, sizeof(uint16)); s_sdcmd->zlhdr_seqnum = ++s_lastTxSeqNum; zlSendCommand(sizeof(zlmhdr_t) + sizeof(zlsdC_t), (uint8 *)s_sdcmd); #endif if (SUCCESS != osal_start_timerEx(oad_app_taskId, ZLOAD_SDRTIMER_EVT, SDR_WAIT_TO)) { osal_set_event(oad_app_taskId, ZLOAD_SDRTIMER_EVT); } s_SDCRetryCount = SDC_RETRY_COUNT; // set timeout timer // need osal timer resource and set event flag and need a method to process timeout event return; } /********************************************************************************** * @fn zlCleanupOnReset * * @brief ZLOAD reset occurred. Free resources and reset state machine and other * supporting constructs * * @param none * * @return none */ static void zlCleanupOnReset() { #if !defined ZPORT if (ZLSTATE_CLIENT == s_State) { DO_EVENT_CALLBACK(ZLCB_EVENT_OADEND_CLIENT); } else if (ZLSTATE_SERVER == s_State) { DO_EVENT_CALLBACK(ZLCB_EVENT_OADEND_SERVER); } #endif zlResetState(); } /************************************************************************************** * @fn zlCleanupOnXferDone * * @brief Data transfer session complete (not necessarily correctly). Free resources * and reset state machine and other supporting constructs. Send Terminate * Session command to Server. * * @param none * * @return none */ static void zlCleanupOnXferDone() { #if defined ZPORT if (ZLSTATE_PASS_THROUGH == s_State) { s_State = ZLSTATE_IDLE; return; } #endif // check for flashing last block and send Session Terminate command if (ZLSTATE_CLIENT == s_State) { zlmhdr_t *hdr = osal_mem_alloc(sizeof(zlmhdr_t) + sizeof(zlendsessC_t)); if (hdr) { zlendsessC_t *cmd = (zlendsessC_t *)((uint8 *)hdr + sizeof(zlmhdr_t)); hdr->zlhdr_msgid = ZLMSGID_SESSION_TERM; hdr->zlhdr_msglen = sizeof(zlendsessC_t); cmd->zlesC_sessionID = s_SessionID; hdr->zlhdr_seqnum = ++s_lastTxSeqNum; #if defined ZPORT { zahdrin_t *zain = zlBuildInternalInboundSerialMSG((uint8 *)hdr, sizeof(zlmhdr_t) + sizeof(zlendsessC_t)); if (zain) { zlSendSerial((uint8 *)zain, SIZEOF_ZAIN_HDR + sizeof(zlmhdr_t) + sizeof(zlendsessC_t)); osal_mem_free(zain); } } #else zlSendCommand(sizeof(zlmhdr_t) + sizeof(zlendsessC_t), (uint8 *)hdr); #endif osal_mem_free(hdr); } #if !defined ZPORT DO_EVENT_CALLBACK(ZLCB_EVENT_OADEND_CLIENT); #endif } #if !defined ZPORT else if (ZLSTATE_SERVER == s_State) { DO_EVENT_CALLBACK(ZLCB_EVENT_OADEND_SERVER); } #endif zlResetState(); } /************************************************************************************** * @fn zlStartClientSession * * @brief We have been commanded to be in the Client role. Set up to start the * session with the Server by sending the Begin Session command * * @param none * * @return none */ static void zlStartClientSession() { uint8 *buf; // we already have Server info. Contact Server to set up session if (buf=osal_mem_alloc(sizeof(zlmhdr_t) + sizeof(zlbegsessC_t))) { zlmhdr_t *hdr = (zlmhdr_t *)buf; zlbegsessC_t *cmd = (zlbegsessC_t *)((uint8 *)buf+sizeof(zlmhdr_t)); // initialize session id as lsb of IEEE address if (!s_SessionID) { uint8 addr[Z_EXTADDR_LEN]; ZMacGetReq(ZMacExtAddr, addr); s_SessionID = addr[0]; } s_State = ZLSTATE_CLIENT; s_SessionID++; hdr->zlhdr_msgid = ZLMSGID_SESSION_START; hdr->zlhdr_seqnum = ++s_lastTxSeqNum; hdr->zlhdr_msglen = sizeof(zlbegsessC_t); osal_memcpy(&cmd->zlbsC_ver, &s_clientInfo->zlclC_ver, sizeof(uint16)); osal_memcpy(&cmd->zlbsC_manu, &s_clientInfo->zlclC_manu, sizeof(uint16)); osal_memcpy(&cmd->zlbsC_prod, &s_clientInfo->zlclC_prod, sizeof(uint16)); cmd->zlbsC_sessionID = s_SessionID; s_NextPacket = 0; // populate session ID element in the Send data payload s_sdcpayload->zlsdC_sessionID = s_SessionID; // set up SD command header s_sdcmd->zlhdr_msgid = ZLMSGID_SEND_DATA; s_sdcmd->zlhdr_msglen = sizeof(zlsdC_t); #if defined ZPORT { zahdrin_t *zain = zlBuildInternalInboundSerialMSG((uint8 *)hdr, sizeof(zlmhdr_t) + sizeof(zlbegsessC_t)); if (zain) { zlSendSerial((uint8 *)zain, SIZEOF_ZAIN_HDR + sizeof(zlmhdr_t) + sizeof(zlbegsessC_t)); osal_mem_free(zain); } } #else if (zlSendCommand(sizeof(zlmhdr_t) + sizeof(zlbegsessC_t), buf)) { } #endif osal_mem_free(buf); DO_EVENT_CALLBACK(ZLCB_EVENT_OADBEGIN_CLIENT); } else { // we couldn't send the Begin Session command. go back to Idle state. osal_set_event(oad_app_taskId, ZLOAD_RESET_EVT); } return; } #if !defined ZPORT /************************************************************************************** * @fn zlSendCommand * * @brief Send command to destination. Can be either over air or to the Host. This * routine takes care of the context. * * @param input * length length of message being sent * buf pointer to message * * @return 0: successful send * 1: unseccessful send (return code for AF layer) */ static uint8 zlSendCommand(uint8 length, uint8 *buf) { uint8 rc; rc = AF_DataRequest( &dstAddr, afFindEndPointDesc( OAD_epDesc.endPoint ), OAD_CLUSTERID_CS, length, buf, &transId, AF_DISCV_ROUTE, DEF_NWK_RADIUS ); return rc; } #endif /************************************************************************************** * @fn zlResendSDC * * @brief Resend the previous Send Data Command because a repply was not received * before a timeout. * * @param none * * @return none. */ static void zlResendSDC() { if (s_State != ZLSTATE_CLIENT) { // late expiration -- don't care return; } if (--s_SDCRetryCount) { #if defined ZPORT s_sdcmd->zlhdr_seqnum = ++s_lastSeqNum; { zahdrin_t *zain = zlBuildInternalInboundSerialMSG((uint8 *)s_sdcmd, sizeof(zlmhdr_t) + sizeof(zlsdC_t)); if (zain) { zlSendSerial((uint8 *)zain, SIZEOF_ZAIN_HDR + sizeof(zlmhdr_t) + sizeof(zlsdC_t)); osal_mem_free(zain); } } #else // the static structure still contains the last packet number to be requested. // the only way the packet number gets bumped is if a reply is received. zlSendCommand(sizeof(zlmhdr_t) + sizeof(zlsdC_t), (uint8 *)s_sdcmd); #endif if (SUCCESS != osal_start_timerEx(oad_app_taskId, ZLOAD_SDRTIMER_EVT, SDR_WAIT_TO)) { osal_set_event(oad_app_taskId, ZLOAD_SDRTIMER_EVT); } } else { // too many retries. abandon session. osal_set_event(oad_app_taskId, ZLOAD_XFER_DONE_EVT); } return; } #if !defined ZPORT /************************************************************************************** * @fn oadAppRegisterCB * * @brief Register a callback to be referenced when OAD events occur. * * @param input * pCBFunction pointer to void function with an unsigned short argument. * eventMask bit mask of events for which the callback should be invoked. * * When the callback is invoked its argument will indicate via the bit mask argument * which event occurred. A null pointer or a null bit mask will have the effect of * deregistration. * * @return none. */ void oadAppRegisterCB(void (*pCBFunction)(uint16), uint16 eventMask) { s_pCallback = pCBFunction; s_eventMask = eventMask; return; } #endif /************************************************************************************************** * @fn zlResetState * * @brief This function frees any memory in use and resets the zl state info. * * input parameters * * None. * * output parameters * * None. * * @return None. ************************************************************************************************** */ static void zlResetState(void) { osal_stop_timerEx(oad_app_taskId, ZLOAD_SDRTIMER_EVT); // kill response timeout check if (s_sdreply) { osal_mem_free(s_sdreply); s_sdreply = NULL; } if (s_clientInfo) { osal_mem_free(s_clientInfo); s_clientInfo = NULL; } s_State = ZLSTATE_IDLE; s_NextPacket = 0; s_NumPktGet = 0; } #if defined ZPORT /********************************************************************* * @fn ZLOADApp_SerialMSGCB * * @brief Message from Host application. It's a command, a reply, * or a proxy command. Send it on appropriately * * @param input * inMsg pointer to incoming message * * @return none */ static void ZLOADApp_SerialMessageMSGCB(zahdrout_t *zaout) { zlmhdr_t *zlhdr = (zlmhdr_t *)zaout->zaproxy_payload; // is this for me? if (zaout->zaproxy_nwkAddr == s_myNwkAddr) { // is this a reply? if (zlhdr->zlhdr_msgid & ZLMSGID_REPLY_BIT) { ZLOADApp_handleReply(NULL, zlhdr); } else { ZLOADApp_handleCommand(NULL, zlhdr); } } else { // it's a proxy command zlZArchitectProxyMsg(zaout); } return; } /********************************************************************* * @fn zlSendSerial * * @brief Prepare message outgoing over serial port. * malloc and copy to add serial encapsulation. * * @param input * buf pointer to ougoing message buffer * len length of message * * @return none */ static void zlSendSerial(uint8 *buf, uint8 len) { uint8 *nbuf; if ((nbuf=osal_mem_alloc(len+1))) { osal_memcpy(nbuf+1, buf, len); *nbuf = OAD_ENDPOINT; #if !defined(ZTOOL_SUPPORT) MTProcessAppRspMsg(nbuf, len + 1); #else // ZTool expects an 81 byte message MTProcessAppRspMsg(nbuf, 81); #endif osal_mem_free(nbuf); } return; } /**************************************************************************** * @fn zlZArchitectProxyCommand * * @brief Send command to target device on behalf of ZArchitect Host App * * @param input * info pointer to ZArchitect proxy message * * @return none */ static void zlZArchitectProxyMsg(zahdrout_t *info) { afAddrType_t taddr; zlmhdr_t *zlhdr = (zlmhdr_t *)info->zaproxy_payload; // only certain outgoing messages are valid proxy commands switch (zlhdr->zlhdr_msgid) { case ZLMSGID_SESSION_START: case ZLMSGID_SESSION_TERM: case ZLMSGID_SEND_DATA: return; } // fill in address info taddr.addrMode = afAddr16Bit; taddr.endPoint = info->zaproxy_endp; taddr.addr.shortAddr = info->zaproxy_nwkAddr; AF_DataRequest( &taddr, afFindEndPointDesc( info->zaproxy_endp ), info->zaproxy_ClusterID, info->zaproxy_msglen, info->zaproxy_payload, &transId, AF_TX_OPTIONS_NONE, DEF_NWK_RADIUS ); } // start a session with the host on behalf of the client from which // the start session was received. do this by sending self a client // command using the start session parameters. ZLOAD will think it // is a client message coming from the host asking it to come get // the image being requested by the real client. static uint8 zlPassOnStartSessionOK(uint8 *msg) { zahdrin_t *zamsgin; // forward the Begin Session command zamsgin = zlBuildExternalInboundSerialMSG(&s_PTClientInfo, msg, sizeof(zlmhdr_t) + sizeof(zlbegsessC_t)); if (!zamsgin) { return 0; } zlSendSerial((uint8 *)zamsgin, SIZEOF_ZAIN_HDR + sizeof(zlmhdr_t) + sizeof(zlbegsessC_t)); osal_mem_free(zamsgin); return 1; } // inbound messages can be built from address information gleaned from the client // during a pass-through session setup. but there can also be inbound messages // such as status replies that are not part of any ongoing client session. in this // case the address information comes from the AF message. in either case this // information is requried to populate the header for for the host. static zahdrin_t *zlBuildExternalInboundSerialMSG(afIncomingMSGPacket_t *addressInfo, uint8 *zlmsg, uint8 len) { zahdrin_t *zamsgin; // build message zamsgin = (zahdrin_t *)osal_mem_alloc(SIZEOF_ZAIN_HDR + len); if (!zamsgin) { return 0; } zamsgin->zaproxy_nwkAddr = addressInfo->srcAddr.addr.shortAddr; zamsgin->zaproxy_endp = addressInfo->endPoint; zamsgin->zaproxy_ClusterID = addressInfo->clusterId; zamsgin->zaproxy_msglen = len; osal_memcpy(zamsgin->zaproxy_payload, zlmsg, len); return zamsgin; } static zahdrin_t *zlBuildInternalInboundSerialMSG(uint8 *zlmsg, uint8 len) { zahdrin_t *zamsgin; // build message zamsgin = (zahdrin_t *)osal_mem_alloc(SIZEOF_ZAIN_HDR + len); if (!zamsgin) { return 0; } zamsgin->zaproxy_nwkAddr = s_myNwkAddr; zamsgin->zaproxy_endp = OAD_ENDPOINT; zamsgin->zaproxy_ClusterID = OAD_CLUSTERID_CS; zamsgin->zaproxy_msglen = len; osal_memcpy(zamsgin->zaproxy_payload, zlmsg, len); return zamsgin; } /********************************************************************* * @fn zlHandleKeys * * @brief Handles all key events for this device. * * @param shift - true if in shift/alt. * @param keys - bit field for key events. Valid entries: * EVAL_SW4 * EVAL_SW3 * EVAL_SW2 * EVAL_SW1 * * @return none */ static void zlHandleKeys(uint8 shift, uint8 keys) { // Shift is used to make each button/switch dual purpose. if ( shift ) { if ( keys & HAL_KEY_SW_1 ) { } if ( keys & HAL_KEY_SW_2 ) { } if ( keys & HAL_KEY_SW_3 ) { } if ( keys & HAL_KEY_SW_4 ) { } } else { if ( keys & HAL_KEY_SW_1 ) { mtxMode = TRUE; } if ( keys & HAL_KEY_SW_2 ) { } if ( keys & HAL_KEY_SW_3 ) { mtxMode = FALSE; } if ( keys & HAL_KEY_SW_4 ) { } } } #else #if (HAL_OAD_XNV_IS_INT && ((HAL_OAD_DL_OSET % HAL_FLASH_PAGE_SIZE) != 0)) // Bug 2946 - HalXNVWrite() only triggers a page erase when the first byte of a page // boundary is written, so when dividing the available internal flash in half results in a // page being split in half, this remedial measure is necessary. static Status_t zlEraseHalfPage(void) { const uint16 hPgSz = HAL_FLASH_PAGE_SIZE / 2; // HalXNVRead/Write routines add HAL_OAD_DL_OSET to the 'addr' parameter, so pass in a 'negative'. const uint32 addr = 0L - hPgSz; uint8 *pBuf = osal_mem_alloc(hPgSz); if (NULL == pBuf) { return FAILURE; } HalXNVRead(addr, pBuf, hPgSz); // This triggers the full page erase and restores the lower half. HalXNVWrite(addr, pBuf, hPgSz); osal_mem_free(pBuf); return SUCCESS; } #endif #endif #pragma diag_default=Pa039