/************************************************************************************************** Filename: MT_X.c Revised: $Date: 2009-08-21 13:10:13 -0700 (Fri, 21 Aug 2009) $ Revision: $Revision: 20630 $ Description: This file contains the MT cross-over interface to simultaneously support ZOAD on MT V1.0 and Z-TOOL on MT V2.0. Copyright 2008-2009 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 "hal_uart.h" #include "hal_types.h" #include "mt.h" #include "mt_rpc.h" #include "mt_x.h" #include "mt_nwk.h" #include "mt_uart.h" #include "mt_zdo.h" #include "NLMEDE.h" #include "oad_app.h" #include "ZDApp.h" #include "ZGlobals.h" /* ------------------------------------------------------------------------------------------------ * Macros * ------------------------------------------------------------------------------------------------ */ /* ------------------------------------------------------------------------------------------------ * Constants * ------------------------------------------------------------------------------------------------ */ #define SOP_STATE 0x00 #define CMD_STATE1 0x01 #define CMD_STATE2 0x02 #define LEN_STATE 0x03 #define DATA_STATE 0x04 #define FCS_STATE 0x05 #define SOP_VALUE 0x02 // Index into the cross-over command array. #define MTX_OLD 0x00 #define MTX_NEW 0x01 // Max entries in the cross-over command array. #define MTX_MAX 12 // MT_RPC_SUBSYSTEM_MASK for the equivalent cmd as a uint16. #define MTX_SSM (((uint16)MT_RPC_SUBSYSTEM_MASK << 8) | 0xFF) // SPI_RESPONSE_BIT mask for the equivalent cmd as a uint8. #define MTX_RSP ((uint8)(SPI_RESPONSE_BIT >> 8)) // Old MT commands from MTEL.h, MT_NWK.h & MT_ZDO.h. #define SPI_CMD_SYS_PING 0x0007 #define SPI_CMD_SYS_VERSION 0x0008 #define SPI_CMD_SYS_GET_DEVICE_INFO 0x0014 #define SPI_CMD_SYS_APP_MSG 0x0018 #define SPI_CMD_SYS_LED_CONTROL 0x0019 #define SPI_CMD_NLME_JOIN_REQ 0x0104 #define SPI_CMD_NLME_LEAVE_REQ 0x0105 #define SPI_CB_NLME_JOIN_CNF 0x0183 #define SPI_CMD_NLME_NWK_DISC_REQ 0x010B #define SPI_CB_NLME_NWK_DISC_CNF 0x018D #define SPI_CMD_ZDO_IEEE_ADDR_REQ 0x0A03 #define SPI_CB_ZDO_IEEE_ADDR_RSP 0x0A81 // Hard-code the most simple sys version response to indicate the ZPortApp capability: -ZP const uint8 msgVersion[] = { SOP_VALUE, // 0x02 MTX_RSP, // 0x10 SPI_CMD_SYS_VERSION, // 0x08 18, // 0x12 '1', // 0x31 '.', // 0x2E '1', // 0x31 '0', // 0x30 ' ', // 0x20 '(', // 0x28 'F', // 0x46 '8', // 0x38 'W', // 0x57 '1', // 0x31 '.', // 0x2E '4', // 0x34 '.', // 0x2E '2', // 0x32 '-', // 0x2D 'Z', // 0x5A 'P', // 0x50 ')', // 0x29 0x0C // FCS }; const uint16 mtxCmd[MTX_MAX][2] = { {SPI_CMD_SYS_APP_MSG, 0x2900}, // MT_APP_MSG {SPI_CMD_SYS_APP_MSG, 0x6980}, // MT_APP_RSP {SPI_CMD_SYS_PING, 0x2101}, {SPI_CMD_SYS_GET_DEVICE_INFO, 0x2700}, {SPI_CMD_SYS_VERSION, 0x2102}, {SPI_CMD_ZDO_IEEE_ADDR_REQ, 0x2501}, {SPI_CB_ZDO_IEEE_ADDR_RSP, 0x4581}, {SPI_CMD_NLME_NWK_DISC_REQ, 0x2309}, {SPI_CB_NLME_NWK_DISC_CNF, 0x4389}, {SPI_CMD_NLME_JOIN_REQ, 0x2304}, {SPI_CMD_NLME_LEAVE_REQ, 0x2305}, {SPI_CB_NLME_JOIN_CNF, 0x4383} }; /* ------------------------------------------------------------------------------------------------ * Typedefs * ------------------------------------------------------------------------------------------------ */ /* ------------------------------------------------------------------------------------------------ * Global Variables * ------------------------------------------------------------------------------------------------ */ extern byte MT_TaskID; uint8 mtxMode; /* ------------------------------------------------------------------------------------------------ * Global Functions * ------------------------------------------------------------------------------------------------ */ extern void MT_UartProcessZToolByte(uint8 ch); /* ------------------------------------------------------------------------------------------------ * Local Variables * ------------------------------------------------------------------------------------------------ */ static uint8 mtxCmdToken[2]; static uint8 mtxFCSToken; static uint8 mtxIdxToken; static uint8 mtxLenToken; static uint8 mtxState; static mtOSALSerialData_t *mtxMsg; static uint8 mtxRsp[128]; /* ------------------------------------------------------------------------------------------------ * Local Functions * ------------------------------------------------------------------------------------------------ */ static uint8 cmdO2N(uint8 *cmd); static uint8 cmdN2O(uint8 *cmd); static uint8 msgO2N(uint8 *cmd, uint8 *msg); static uint8 msgN2O(uint8 *cmd, uint8 idx); static void mtxIEEEAddrRspCB(uint8 *cmd); /************************************************************************************************** * @fn cmdO2N * * @brief This function translates from an old MT command to the corresponding new one and * returns TRUE if the command is recognized as pertinent to ZOAD traffic. * This function must only be called to convert incoming commands. * * input parameters * * @param cmd - A command that is to be translated if it is pertinent to ZOAD traffic. * * output parameters * * @param cmd - The translated command. * * @return TRUE if a command that is pertinent to OAD traffic is recognized and converted. * FALSE otherwise. ************************************************************************************************** */ static uint8 cmdO2N(uint8 *cmd) { uint16 tmp; uint8 idx; // Incoming command, so no need to mask for SPI_RESPONSE_BIT. tmp = BUILD_UINT16(cmd[1], cmd[0]); for (idx = 0; idx < MTX_MAX; idx++) { if (mtxCmd[idx][MTX_OLD] == tmp) { // Incoming command cross-over to SREQ or AREQ done by table value. cmd[0] = HI_UINT16(mtxCmd[idx][MTX_NEW]); cmd[1] = LO_UINT16(mtxCmd[idx][MTX_NEW]); break; } } return (idx == MTX_MAX) ? FALSE : TRUE; } /************************************************************************************************** * @fn cmdN2O * * @brief This function translates from a new MT command to the corresponding old one and * returns TRUE if the command is recognized as pertinent to ZOAD traffic. * This function must only be called to convert outgoing commands. * * input parameters * * @param cmd - A command that is to be translated if it is pertinent to ZOAD traffic. * * output parameters * * @param cmd - The translated command. * * @return TRUE if a command that is pertinent to OAD traffic is recognized and converted. * FALSE otherwise. ************************************************************************************************** */ static uint8 cmdN2O(uint8 *cmd) { uint16 tmp; uint8 idx; tmp = BUILD_UINT16(cmd[1], (cmd[0] & MT_RPC_SUBSYSTEM_MASK)); for (idx = 0; idx < MTX_MAX; idx++) { if ((mtxCmd[idx][MTX_NEW] & MTX_SSM) == tmp) { return msgN2O(cmd, idx); } } return FALSE; } /************************************************************************************************** * @fn msgO2N * * @brief This function translates from the old MT message data format to the corresponding * new one. This function must only be called to convert incoming message data. * * input parameters * * @param cmd - The command that has been translated to the new format. * @param msg - The message data to be translated to the new format. * * output parameters * * @param msg - The translated message data. * * @return TRUE if the message should be sent to the destination OSAL task. ************************************************************************************************** */ static uint8 msgO2N(uint8 *cmd, uint8 *msg) { uint16 tmp = BUILD_UINT16(cmd[1], cmd[0]); uint8 rtrn = TRUE; if (tmp == 0x2309) // SPI_CMD_NLME_NWK_DISC_REQ { MT_ReverseBytes(msg, 4); _nwkCallbackSub = 0xFFFF; } else if (tmp == 0x2304) // SPI_CMD_NLME_JOIN_REQ { // Fake success to the NLME_JOIN_REQ while making a ZDO Init device request. const uint8 fakeNwkJoinAck[] = { 0x02, 0x11, 0x04, 0x01, 0x00, 0x14 }; zgConfigPANID = BUILD_UINT16(msg[1], msg[0]); zgDefaultChannelList = 0x00000800; zgDefaultChannelList <<= (msg[2] - 11); zgDefaultStartingScanDuration = 0; ZDOInitDevice(0); _nwkCallbackSub = 0; HalUARTWrite(MT_UART_DEFAULT_PORT, (uint8 *)fakeNwkJoinAck, sizeof(fakeNwkJoinAck)); rtrn = FALSE; } else if (tmp == 0x2501) // SPI_CMD_ZDO_IEEE_ADDR_REQ { uint16 addr = BUILD_UINT16(msg[0], msg[1]); if (addr != NLME_GetShortAddr()) { (void)ZDP_IEEEAddrReq(addr, msg[2], msg[3], 0); } rtrn = FALSE; } else if (tmp == 0x2305) // SPI_CMD_NLME_LEAVE_REQ { // Fake a leave success and reset. mtxRsp[0] = SOP_VALUE; mtxRsp[1] = 0x01; mtxRsp[2] = 0x85; mtxRsp[3] = Z_EXTADDR_LEN + 1; osal_cpyExtAddr(mtxRsp+4, aExtendedAddress); MT_ReverseBytes(mtxRsp+4, Z_EXTADDR_LEN); mtxRsp[4+Z_EXTADDR_LEN] = ZSuccess; mtxRsp[SPI_0DATA_MSG_LEN + Z_EXTADDR_LEN] = MT_UartCalcFCS(mtxRsp+1, Z_EXTADDR_LEN+3); HalUARTWrite(MT_UART_DEFAULT_PORT, mtxRsp, SPI_0DATA_MSG_LEN+Z_EXTADDR_LEN+1); osal_start_timerEx(oad_app_taskId, ZLOAD_RESET_BOARD_EVT, 6000); } return rtrn; } /************************************************************************************************** * @fn msgN2O * * @brief This function translates from the new MT message data format to the corresponding * old one. This function must only be called to convert outgoing message data. * * input parameters * * @param cmd - The command that has been translated to the old format. * @param msg - The message data to be translated to the old format. * * output parameters * * @param msg - The translated message data. * * @return TRUE if message should be sent on UART; FALSE if handled here. ************************************************************************************************** */ static uint8 msgN2O(uint8 *cmd, uint8 idx) { if (mtxCmd[idx][MTX_OLD] == SPI_CMD_SYS_VERSION) { HalUARTWrite(MT_UART_DEFAULT_PORT, (uint8 *)msgVersion, sizeof(msgVersion)); idx = MTX_MAX; // Force return of FALSE since command handled here. } else { // Outgoing command needs to determine if it is a response or not. if ((cmd[0] & MT_RPC_CMD_SRSP) == MT_RPC_CMD_SRSP) { cmd[0] = MTX_RSP; } else { cmd[0] = 0; } cmd[0] |= HI_UINT16(mtxCmd[idx][MTX_OLD]); cmd[1] = LO_UINT16(mtxCmd[idx][MTX_OLD]); if (mtxCmd[idx][MTX_OLD] == SPI_CMD_SYS_GET_DEVICE_INFO) { MT_ReverseBytes(cmd+3, Z_EXTADDR_LEN); // Reverse the IEEE. MT_ReverseBytes(cmd+3+Z_EXTADDR_LEN, 2); // Reverse the Nwk Addr. } else if (mtxCmd[idx][MTX_OLD] == SPI_CB_NLME_NWK_DISC_CNF) { idx = cmd[2]; cmd += 3; while (idx--) { MT_ReverseBytes(cmd, 2); // Reverse the PanId for every network discovered. cmd += 9; } } /*else if (mtxCmd[idx][MTX_OLD] == SPI_CB_NLME_JOIN_CNF) { MT_ReverseBytes(cmd+2, Z_EXTADDR_LEN); // Reverse the IEEE. MT_ReverseBytes(cmd+10, 2); // Reverse the PanId. }*/ else if (mtxCmd[idx][MTX_OLD] == SPI_CB_ZDO_IEEE_ADDR_RSP) { mtxIEEEAddrRspCB(cmd-1); idx = MTX_MAX; // Force return of FALSE since command handled here. } } return (idx == MTX_MAX) ? FALSE : TRUE; } /************************************************************************************************** * @fn mtxIEEEAddrRspCB * * @brief Make the big conversion from new to old IEEE MT response. * * input parameters * * @param cmd - New response buffer beginning with a converted command. * * output parameters * * None. * * @return None. ************************************************************************************************** */ static void mtxIEEEAddrRspCB(uint8 *cmd) { uint8 cnt = cmd[15] * 2; if (cnt > 12) cnt = 12; mtxRsp[0] = SOP_VALUE; mtxRsp[1] = cmd[1]; mtxRsp[2] = cmd[2]; mtxRsp[3] = 36; // Set the network address. mtxRsp[4] = Addr16Bit; (void)memset(mtxRsp+5, 0, Z_EXTADDR_LEN-2); mtxRsp[11] = cmd[13]; mtxRsp[12] = cmd[12]; mtxRsp[13] = cmd[3]; // Status. // Copy and reverse the IEEE. (void)memcpy(mtxRsp+14, cmd+4, Z_EXTADDR_LEN); MT_ReverseBytes(mtxRsp+14, Z_EXTADDR_LEN); // Copy and reverse the start index and count. mtxRsp[22] = cmd[15]; mtxRsp[23] = cmd[14]; // Copy each Nwk Addr. (void)memcpy(mtxRsp+24, cmd+16, cnt); // Zero out unused addresses. (void)memset(mtxRsp+24+cnt, 0, 12-cnt); mtxRsp[SPI_0DATA_MSG_LEN-1 + 36] = MT_UartCalcFCS(mtxRsp+1, (MT_RPC_FRAME_HDR_SZ + 36)); HalUARTWrite(MT_UART_DEFAULT_PORT, mtxRsp, SPI_0DATA_MSG_LEN+36); } /************************************************************************************************** * @fn MT_X_UartProcessZToolData * * @brief This function is called by Hal_UART_SendCallBack to read incoming Rx data. * * Attempt to sync on and parse the old MT 1.0 format: * | SOP | Data Length | CMD | Data | FCS | * | 1 | 1 | 2 | 0-Len | 1 | * * And then pass on all data to the MT 2.0 by invoking * void MT_UartProcessZToolData ( uint8 port, uint8 event ) * * input parameters * * @param port - UART port. * @param event - Event that causes the callback. * * output parameters * * None. * * @return None. ************************************************************************************************** */ void MT_X_UartProcessZToolData(uint8 port, uint8 event) { uint8 ch; (void)event; while (HalUARTRead(port, &ch, 1)) { if (mtxMode) { switch (mtxState) { case SOP_STATE: if (ch == SOP_VALUE) { mtxState = CMD_STATE1; } break; case CMD_STATE1: mtxCmdToken[0] = ch; mtxFCSToken = 0; mtxState = CMD_STATE2; break; case CMD_STATE2: mtxCmdToken[1] = ch; /* If it is an old command that is pertinent to the ZOAD traffic, convert it to new and * continue to parse, otherwise re-start the sync. */ if (cmdO2N(mtxCmdToken)) { mtxState = LEN_STATE; } else { mtxState = SOP_STATE; } break; case LEN_STATE: if (ch == 0) { mtxState = FCS_STATE; } else { mtxState = DATA_STATE; } mtxLenToken = ch; mtxIdxToken = 0; /* Allocate memory for the data */ mtxMsg = (mtOSALSerialData_t *)osal_msg_allocate(sizeof(mtOSALSerialData_t) + MT_RPC_FRAME_HDR_SZ + mtxLenToken); if (mtxMsg) { mtxMsg->hdr.event = CMD_SERIAL_MSG; mtxMsg->msg = (uint8*)(mtxMsg+1); mtxMsg->msg[MT_RPC_POS_LEN] = mtxLenToken; mtxMsg->msg[MT_RPC_POS_CMD0] = mtxCmdToken[0]; mtxMsg->msg[MT_RPC_POS_CMD1] = mtxCmdToken[1]; } else { mtxState = SOP_STATE; } break; case DATA_STATE: mtxMsg->msg[MT_RPC_FRAME_HDR_SZ + mtxIdxToken++] = ch; if (--mtxLenToken == 0) { mtxState = FCS_STATE; } break; case FCS_STATE: if (mtxFCSToken == ch) { if (msgO2N(mtxCmdToken, (mtxMsg->msg)+MT_RPC_POS_DAT0)) { osal_msg_send(MT_TaskID, (byte *)mtxMsg); } else { osal_msg_deallocate((uint8 *)mtxMsg); } } else { osal_msg_deallocate ( (uint8 *)mtxMsg ); } mtxState = SOP_STATE; break; default: break; } mtxFCSToken ^= ch; } else { MT_UartProcessZToolByte(ch); } } } /************************************************************************************************** * @fn MT_X_TransportSend * * @brief Prepare message to send by old MT format if it is pertinent to ZOAD. * Copy message header to re-use to also send in new MT format and then * fill in SOP and FCS then send out the msg. * * input parameters * * @param msg - pointer to the message that contains spare byte, CMD, length, data and FCS. * * output parameters * * None. * * @return None. ************************************************************************************************** */ void MT_X_TransportSend(uint8 *msg) { if (mtxMode) { if (cmdN2O(msg+2)) { // Re-position the 2 cmd bytes & length to the old format positions. msg[0] = msg[1]; // Temporarily save data len. (void)memcpy(msg+1, msg+2, 2); // Move command bytes to first location after SOP. msg[3] = msg[0]; // Data len. msg[0] = SOP_VALUE; msg[SPI_0DATA_MSG_LEN-1 + msg[3]] = MT_UartCalcFCS(msg+1, (MT_RPC_FRAME_HDR_SZ + msg[3])); HalUARTWrite(MT_UART_DEFAULT_PORT, msg, SPI_0DATA_MSG_LEN + msg[3]); } } else { msg[0] = MT_UART_SOF; msg[SPI_0DATA_MSG_LEN - 1 + msg[1]] = MT_UartCalcFCS (msg+1, (3 + msg[1])); HalUARTWrite(MT_UART_DEFAULT_PORT, msg, msg[1] + SPI_0DATA_MSG_LEN); } osal_msg_deallocate(msg); } /************************************************************************************************** * @fn MT_X_FakeNwkJoinCnf * * @brief Fake a SPI_CB_NLME_JOIN_CNF to the old ZOAD. * * input parameters * * None. * * output parameters * * None. * * @return None. ************************************************************************************************** */ void MT_X_FakeNwkJoinCnf(void) { uint8 *msg; if ((msg=osal_mem_alloc(SPI_0DATA_MSG_LEN+11))) { msg[0] = SOP_VALUE; msg[1] = 0x01; msg[2] = 0x83; msg[3] = 11; osal_nv_read(ZCD_NV_EXTADDR, 0, Z_EXTADDR_LEN, msg+4); MT_ReverseBytes(msg+4, Z_EXTADDR_LEN); msg[12] = HI_UINT16(zgConfigPANID); msg[13] = LO_UINT16(zgConfigPANID); msg[14] = 0; msg[SPI_0DATA_MSG_LEN-1 + 11] = MT_UartCalcFCS(msg+1, (MT_RPC_FRAME_HDR_SZ + 11)); HalUARTWrite(MT_UART_DEFAULT_PORT, msg, SPI_0DATA_MSG_LEN+11); osal_mem_free(msg); } } /************************************************************************************************** */