#include "can.h" // TODO remove for testing only #include // For memcpy and memset #include "CommBuffers.h" #include "Download.h" #include "FPGA.h" #include "NVDataMgmt.h" #include "OperationModes.h" #include "SystemComm.h" #include "Utilities.h" /** * @addtogroup Download * @{ */ // ********** private definitions ********** #define SHIFT_BITS_TO_GET_TARGET 4 ///< Shift bits by 4 to get the update target. static const U32 NUM_OF_CAN_BYTES_FOR_UPDATE = SW_UPDATE_FLASH_BUFFER_SIZE + CAN_MESSAGE_FRAME_SIZE; ///< Number of CAN bytes for update. // ********** private data ********** /// Software update response status structure typedef struct { U08 msgID; ///< Message ID. U08 msgAckNackStatus; ///< Message ack or nack status. U16 cyberRandom; ///< Message cyber random. U32 msgCRC; ///< Message CRC. } SW_UPDATE_RESP_STATUS_T; /// Software update command status structure typedef struct { U08 msgID; ///< Message ID. U08 updateCmd; ///< Update command. U16 cyberRandom; ///< Cyber random. U32 msgCRC; ///< Message CRC. } SW_UPDATE_CMD_STATUS_T; /// Software update receive update status structure typedef struct { U08 msgID; ///< Message ID. U08 updateDest; ///< Update destination (FW, FPGA). U16 updatePayloadLen; ///< Update payload length in bytes. U32 msgCRC; ///< Message CRC. U08 SWUpdateBuffer[ SW_UPDATE_FLASH_BUFFER_SIZE ]; ///< Software update buffer. } SW_UPDATE_RCV_STATUS_T; static SW_UPDATE_RCV_STATUS_T SWUpdateRCVStatus; ///< Software update receive status. static SW_UPDATE_CMD_T SWUpdateCommandState; ///< Software update command state. static SW_UPDATE_CAN_MAIL_BOX_T thisStackMailBox; ///< The mailbox of this stack. static U32 sizeToWrite; ///< Size to write to destination. static U32 REMOVETHEVAR = 0; // TODO remove // ********** private function prototypes ********** static void handleIncomingCmdMessage( SW_UPDATE_CAN_MAIL_BOX_T mailBox ); static void handleIncomingUpdateMessage( SW_UPDATE_CAN_MAIL_BOX_T mailBox ); static void prepareAndSendFWResponseMessage( U08 respOfMsgID, ACK_NACK_STATUS_T ackNack ); static void clearSWUpdateBuffer( void ); static ACK_NACK_STATUS_T handleFirmwareUpdate( void ); static ACK_NACK_STATUS_T handleFPGAUpdate( void ); /*********************************************************************//** * @brief * The initDownload function initializes the download unit. * @details \b Inputs: none * @details \b Outputs: thisStackMailBox * @return none *************************************************************************/ void initDownload( void ) { thisStackMailBox = RECEIVE_MSG_ID[ BL_STACK_ID ][ FW_STACKS_RCV_MAIL_BOX_INDEX ]; clearSWUpdateBuffer(); clearSWUpdateCommandState(); } /*********************************************************************//** * @brief * The execDownload function executes the download state machine. * @details \b Inputs: none * @details \b Outputs: none * @return none *************************************************************************/ void execDownload( void ) { handleIncomingCmdMessage( SW_UPDATE_COMMAD ); handleIncomingUpdateMessage( thisStackMailBox ); } /*********************************************************************//** * @brief * The getSWUpdateCommandState function returns the current software update * command state. * @details \b Inputs: SWUpdateCommandState * @details \b Outputs: none * @return current software update command state *************************************************************************/ SW_UPDATE_CMD_T getSWUpdateCommandState( void ) { return SWUpdateCommandState; } /*********************************************************************//** * @brief * The getSWUpdateDestination function returns the current software update * destination (firmware or FPGA). * @details \b Inputs: SWUpdateRCVStatus * @details \b Outputs: none * @return current software update destination *************************************************************************/ SW_UPDATE_DESINTATION_T getSWUpdateDestination( void ) { return (SW_UPDATE_DESINTATION_T)SWUpdateRCVStatus.updateDest; } /*********************************************************************//** * @brief * The clearSWUpdateCommandState function clears the software update command * state to idle state. * @details \b Inputs: none * @details \b Outputs: SWUpdateCommandState * @return none *************************************************************************/ void clearSWUpdateCommandState( void ) { SWUpdateCommandState = UPDATE_CMD_IDLE; } /*********************************************************************//** * @brief * The sendFPGAAckNackStatus function sends the ack or nack status to the * application. * @details \b Inputs: SWUpdateRCVStatus, thisStackMailBox * @details \b Outputs: none * @param ack or nack status to be sent * @return none *************************************************************************/ void sendFPGAAckNackStatus( ACK_NACK_STATUS_T ackNackStatus ) { prepareAndSendFWResponseMessage( SWUpdateRCVStatus.msgID, ackNackStatus ); clearCommBuffer( thisStackMailBox ); } U08 getTempRemoveMSGID() { return SWUpdateRCVStatus.msgID; } // ********** private functions ********** /*********************************************************************//** * @brief * The handleIncomingCmdMessage function handles the incoming command message * from the CAN bus. Once the entire message has been received, it is processed * and the corresponding destination is informed that an update is available. * @details \b Inputs: SWUpdateCmdStatus * @details \b Outputs: SWUpdateCmdStatus * @param mailbox of the buffer that has been received * @return none *************************************************************************/ static void handleIncomingCmdMessage( SW_UPDATE_CAN_MAIL_BOX_T mailBox ) { if ( TRUE == isMessageComplete( mailBox ) ) { // If the command buffer has been received, get it from the comm buffer and process it SW_UPDATE_CMD_STATUS_T SWUpdateCmdStatus; getCommBuffer( mailBox, (U08*)&SWUpdateCmdStatus, sizeof( SW_UPDATE_CMD_STATUS_T ) ); BOOL hasCRCPassed = FALSE; ACK_NACK_STATUS_T ackStatus = NACK; U32 calcCRC = 0; U08 msgID = SWUpdateCmdStatus.msgID; SW_UPDATE_DESINTATION_T dest = (SW_UPDATE_DESINTATION_T)( SWUpdateCmdStatus.updateCmd >> SHIFT_BITS_TO_GET_TARGET ); // TODO add more logic for other stacks // Calculate the CRC of the message and ack or nack based on the matching of the CRCs calcCRC = crc32( calcCRC, (U08*)&SWUpdateCmdStatus, sizeof( SW_UPDATE_CMD_STATUS_T ) - sizeof( U32 ) ); hasCRCPassed = ( SWUpdateCmdStatus.msgCRC == calcCRC ? TRUE : FALSE ); if ( TRUE == hasCRCPassed ) { ackStatus = ACK; SWUpdateCommandState = (SW_UPDATE_CMD_T)( SWUpdateCmdStatus.updateCmd & MASK_OFF_NIBBLE_MSB ); if ( UPDATE_FPGA == dest ) // TODO once verify command is received for FPGA, make sure the prepare signal is not sent again { // If the update destination is FPGA, signal FPGA to prepare for the update. signalFPGAToPrepareForUpdate(); } } // Send the result of the command received prepareAndSendFWResponseMessage( msgID, ackStatus ); clearCommBuffer( mailBox ); } } /*********************************************************************//** * @brief * The handleIncomingUpdateMessage function handles the incoming update message * from the CAN bus. Once the entire update message has been received, it then * is sent to the corresponding destination that is under update. * @details \b Inputs: SWUpdateRCVStatus * @details \b Outputs: SWUpdateRCVStatus * @param mailbox of the buffer that has been received * @return none *************************************************************************/ static void handleIncomingUpdateMessage( SW_UPDATE_CAN_MAIL_BOX_T mailBox ) { if ( ( TRUE == isMessageComplete( mailBox ) ) && ( MODE_UPDATE == getCurrentOpMode() ) ) { U08 bufferWithNoCRC[ SW_UPDATE_FLASH_BUFFER_SIZE + sizeof( U32 ) ]; U32 calcCRC = 0; BOOL hasCRCPassed = FALSE; ACK_NACK_STATUS_T ackStatus = NACK; U08 msgID = 0; U16 payloadLength = 0; getCommBuffer( mailBox, (U08*)&SWUpdateRCVStatus, NUM_OF_CAN_BYTES_FOR_UPDATE ); payloadLength = SWUpdateRCVStatus.updatePayloadLen; // Create a local buffer and copy the header into the buffer excluding the CRC so only 4 bytes of ID, Destination, and payload length memcpy( bufferWithNoCRC, (U08*)&SWUpdateRCVStatus, sizeof( U32 ) ); // Copy the entire update buffer that is going either to firmware or FPGA into the local buffer. memcpy( &bufferWithNoCRC[ sizeof( U32 ) ], SWUpdateRCVStatus.SWUpdateBuffer, payloadLength ); // Calculate the CRC of the local copied buffer and compare it against the message CRC calcCRC = crc32( calcCRC, bufferWithNoCRC, sizeof( U32 ) + payloadLength ); hasCRCPassed = ( SWUpdateRCVStatus.msgCRC == calcCRC ? TRUE : FALSE ); msgID = SWUpdateRCVStatus.msgID; if ( FALSE == hasCRCPassed ) { BOOL test = TRUE; // TODO remove } if ( TRUE == hasCRCPassed ) { // CRC passed, call the corresponding the handlers to update either FPGA or firmware switch ( SWUpdateRCVStatus.updateDest ) { case UPDATE_FIRMWARE: ackStatus = handleFirmwareUpdate(); break; case UPDATE_FPGA: ackStatus = handleFPGAUpdate(); break; default: // Do nothing break; } } prepareAndSendFWResponseMessage( msgID, ackStatus ); clearCommBuffer( mailBox ); clearSWUpdateBuffer(); } } /*********************************************************************//** * @brief * The prepareAndSendFWResponseMessage function prepares the message body that is * used to respond to the updater app and sends it. * @details \b Inputs: none * @details \b Outputs: none * @param message ID that is being responded for * @param ack nack status * @return none *************************************************************************/ static void prepareAndSendFWResponseMessage( U08 respOfMsgID, ACK_NACK_STATUS_T ackNack ) { SW_UPDATE_RESP_STATUS_T resp; U32 calcCRC = 0; resp.msgID = respOfMsgID; resp.msgAckNackStatus = ackNack; resp.cyberRandom = 0; resp.msgCRC = crc32( calcCRC, (U08*)&resp, sizeof( SW_UPDATE_RESP_STATUS_T ) - sizeof( U32 ) ); sendAckNackStatusFromFirmware( (U08*)&resp ); // TODO do we have to retry if send failed? } /*********************************************************************//** * @brief * The clearSWUpdateBuffer function clears the software update buffer. * @details \b Inputs: none * @details \b Outputs: SWUpdateRCVStatus * @return none *************************************************************************/ static void clearSWUpdateBuffer( void ) { memset( &SWUpdateRCVStatus, 0x0, sizeof( SW_UPDATE_RCV_STATUS_T ) ); } /*********************************************************************//** * @brief * The handleFirmwareUpdate function handles the firmware update data. This * function checks that the message is not the final message and then signal * the non-volatile memory to update the flash. * @details \b Inputs: SWUpdateRCVStatus * @details \b Outputs: none * @return Ack if the write was successful otherwise, Nack *************************************************************************/ static ACK_NACK_STATUS_T handleFirmwareUpdate( void ) { ACK_NACK_STATUS_T ackStatus = ACK; BOOL status = FALSE; _disable_IRQ(); status = handleUpdatingFlash( SWUpdateRCVStatus.SWUpdateBuffer ); ackStatus = ( TRUE == status ? ACK : NACK ); _enable_IRQ(); return ackStatus; } /*********************************************************************//** * @brief * The handleFPGAUpdate function handles the FPGA update data. This * function checks that the message is not the final message and then signals * the FPGA driver to update the FPGA. * @details \b Inputs: SWUpdateRCVStatus * @details \b Outputs: none * @return Ack if the write was successful otherwise, Nack *************************************************************************/ static ACK_NACK_STATUS_T handleFPGAUpdate( void ) { ACK_NACK_STATUS_T ackStatus = NACK; // TODO why the firmware handler is slightly different? Make them consistent. #define SW_UPDATE_FINAL_MSG_INDEX 0xFFFF // TODO: Remove. NOTE: once FPGA is ready investigate and remove 0xFFFF if ( SWUpdateRCVStatus.updatePayloadLen != SW_UPDATE_FINAL_MSG_INDEX ) { sizeToWrite = SWUpdateRCVStatus.updatePayloadLen; REMOVETHEVAR += sizeToWrite; signalFPGAToWriteToFlash( SWUpdateRCVStatus.SWUpdateBuffer, sizeToWrite ); if ( sizeToWrite < SW_UPDATE_FLASH_BUFFER_SIZE ) { signalFPGAToSelfConfigure(); } } else { prepareAndSendFWResponseMessage( SWUpdateRCVStatus.msgID, ACK ); } return ackStatus; }