#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 "SystemComm.h" #include "Utilities.h" /** * @addtogroup Download * @{ */ // ********** private definitions ********** #define SW_UPDATE_FINAL_MSG_INDEX 0xFFFF ///< Software update final message index. #define SHIFT_BITS_TO_GET_TARGET 4 ///< Shift bits by 4 to get the update target. // ********** private data ********** static const U32 NUM_OF_CAN_BYTES_FOR_UPDATE = SW_UPDATE_FLASH_BUFFER_SIZE + CAN_MESSAGE_PAYLOAD_SIZE; ///< Number of CAN bytes for update. /// 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 prepareResponseMessage( U08 respOfMsgID, ACK_NACK_STATUS_T ackNack, SW_UPDATE_RESP_STATUS_T* respBuffer ); 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 ]; clearSWUpdateBuffer(); clearSWUpdateCommandState(); } /*********************************************************************//** * @brief * The execDownload function executes the download state machine. * @details \b Inputs: none * @details \b Outputs: none * @return none TODO change the design to a state machine *************************************************************************/ void execDownload( void ) { // TODO make this a state machine 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 ) { SW_UPDATE_RESP_STATUS_T resp; prepareResponseMessage( SWUpdateRCVStatus.msgID, ackNackStatus, &resp ); sendAckNackStatusFromFirmware( (U08*)&resp ); 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 ) { // Peek into the number of bytes received for the command buffer S32 bytesInBuffer = getNumberOfBytesInBuffer( mailBox ); if ( bytesInBuffer == CAN_MESSAGE_PAYLOAD_SIZE ) { // If the command buffer has been received, get it from the comm buffer and process it SW_UPDATE_CMD_STATUS_T SWUpdateCmdStatus; SW_UPDATE_RESP_STATUS_T resp; 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 ); // TODO remove hasCRCPassed = TRUE; // TODO remove if ( TRUE == hasCRCPassed ) { ackStatus = ACK; SWUpdateCommandState = (SW_UPDATE_CMD_T)( SWUpdateCmdStatus.updateCmd & MASK_OFF_NIBBLE_MSB ); if ( UPDATE_FPGA == dest ) { // If the update destination is FPGA, signal FPGA to prepare for the update. signalFPGAToPrepareForUpdate(); } } // Send the result of the command received prepareResponseMessage( msgID, ackStatus, &resp ); sendAckNackStatusFromFirmware( (U08*)&resp ); 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 ) { // Peek into the comm buffer to see if the entire data has been received S32 bytesInBuffer = getNumberOfBytesInBuffer( mailBox ); if ( bytesInBuffer == NUM_OF_CAN_BYTES_FOR_UPDATE ) { SW_UPDATE_RESP_STATUS_T resp; U08 bufferWithNoCRC[ SW_UPDATE_FLASH_BUFFER_SIZE + sizeof( U32 ) ]; BOOL status = FALSE; U32 calcCRC = 0; BOOL hasCRCPassed = FALSE; ACK_NACK_STATUS_T ackNackStatus = NACK; getCommBuffer( mailBox, (U08*)&SWUpdateRCVStatus, NUM_OF_CAN_BYTES_FOR_UPDATE ); // 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, sizeof( SWUpdateRCVStatus.SWUpdateBuffer ) ); // Calculate the CRC of the local copied buffer and compare it against the message CRC calcCRC = crc32( calcCRC, bufferWithNoCRC, sizeof( bufferWithNoCRC ) ); hasCRCPassed = ( SWUpdateRCVStatus.msgCRC == calcCRC ? TRUE : FALSE ); // TODO remove hasCRCPassed = 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: ackNackStatus = handleFirmwareUpdate(); break; case UPDATE_FPGA: ackNackStatus = handleFPGAUpdate(); break; default: // Do nothing break; } } // TODo send nack if the CRC failed //prepareResponseMessage( SWUpdateRCVStatus.msgID, ackNackStatus, &resp ); //status = sendAckNackStatusFromFirmware( (U08*)&resp ); // TODO do we have to retry if send failed? clearCommBuffer( mailBox ); // TODo does this need to be here? How about resync? //clearSWUpdateBuffer(); // TODO uncomment } } /*********************************************************************//** * @brief * The prepareResponseMessage function prepares the message body that is * used to respond to the updater app. * @details \b Inputs: none * @details \b Outputs: none * @param message ID that is being responded for * @param ack nack status * @param response buffer which is the pointer to the buffer that is used * to be sent to the app * @return none *************************************************************************/ static void prepareResponseMessage( U08 respOfMsgID, ACK_NACK_STATUS_T ackNack, SW_UPDATE_RESP_STATUS_T* respBuffer ) { U32 calcCRC = 0; respBuffer->msgID = respOfMsgID; respBuffer->msgAckNackStatus = ackNack; respBuffer->cyberRandom = 0; respBuffer->msgCRC = crc32( calcCRC, (U08*)&respBuffer, sizeof( SW_UPDATE_RESP_STATUS_T ) - sizeof( U32 ) ); } /*********************************************************************//** * @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 ) { SW_UPDATE_RESP_STATUS_T resp; ACK_NACK_STATUS_T ackStatus = ACK; BOOL status = FALSE; if ( SWUpdateRCVStatus.updatePayloadLen != SW_UPDATE_FINAL_MSG_INDEX ) { _disable_IRQ(); status = handleUpdatingFlash( SWUpdateRCVStatus.SWUpdateBuffer ); ackStatus = ( TRUE == status ? ACK : NACK ); _enable_IRQ(); } prepareResponseMessage( SWUpdateRCVStatus.msgID, ackStatus, &resp ); status = sendAckNackStatusFromFirmware( (U08*)&resp ); // TODO do we have to retry if send failed? clearCommBuffer( thisStackMailBox ); // TODo does this need to be here? How about resync? 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. 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 { SW_UPDATE_RESP_STATUS_T resp; prepareResponseMessage( SWUpdateRCVStatus.msgID, ACK, &resp ); sendAckNackStatusFromFirmware( (U08*)&resp ); } return ackStatus; }