/* * This source file is part of the EtherCAT Slave Stack Code licensed by Beckhoff Automation GmbH & Co KG, 33415 Verl, Germany. * The corresponding license agreement applies. This hint shall not be removed. * https://www.beckhoff.com/media/downloads/slave-stack-code/ethercat_ssc_license.pdf */ /** \addtogroup FoE FileTransfer over EtherCAT @{ */ /** \file ecatfoe.c \author EthercatSSC@beckhoff.com \brief Implementation This file contains the FoE mailbox interface \version 5.13
Changes to version V5.12:
V5.13 EOE1:
V5.13 FOE1: handle failure on continue indication

Changes to version V5.11:
V5.12 FOE2: handle 16bit only access
V5.12 MBX4: in case of a disable mailbox queue and two consecutive foe uploads the mailbox receive handler is blocked

Changes to version V5.10:
V5.11 FOE1: handle busy on a read request, change OPMode to OPCode (same terms as in the spec)

Changes to version V5.0:
V5.10 MBX1: Remove multiple swapping of length filed in mailbox header

Changes to version V4.40:
V5.0 FOE1: Free memory if FoE_ServciceInd() is left.
V5.0 FOE2: Update error code (Bit 15 shall not be set).
V5.0 FOE3: Update byte swapping for big endian and big endian 16 configurations.
V5.0 FOE4: Update Error text handling for 16Bit operations.

Changes to version V4.11:
V4.40 FOE1: correct FoE busy values
V4.40 MBX2: HW_CopyToSendMailbox merge to a generic function (new: MBX_CopyToSendMailbox)

Changes to version V4.10:
V4.11 FOE 1 fixed a problem with a huge FOE_MADATA define
V4.11 FOE 2 u16PacketNo is now a 32bit variable (u32PacketNo) to conform with the spec

Changes to version V4.00:
V4.10 FOE 1-3 when downloading the firmware the FoE state machine was not set to FOE_READY
when the last data segment was downloaded so that the next FoE service failed

Changes to version V3.20:
V4.00 FOE 1 - if a FoE Error service is received it should not be acknowledged
V4.00 FOE 2 - if a FoE service response could not be sent because the mailbox is full
it has to be re-transmit otherwise the response get lost
*/ /*----------------------------------------------------------------------------------------- ------ ------ Includes ------ -----------------------------------------------------------------------------------------*/ #include "ecat_def.h" #include "ecatslv.h" #define _ECATFOE_ 1 #include "ecatfoe.h" #undef _ECATFOE_ #define _ECATFOE_ 0 #include "foeappl.h" /*----------------------------------------------------------------------------------------- ------ ------ functions ------ -----------------------------------------------------------------------------------------*/ ///////////////////////////////////////////////////////////////////////////////////////// /** \brief This function initialize the FoE Interface. *//////////////////////////////////////////////////////////////////////////////////////// void FOE_Init(void) { /* no file transmission sequence is running */ u16FileAccessState = FOE_READY; /* initialize the expected packet number */ u32PacketNo = 0; pFoeSendStored = NULL; } ///////////////////////////////////////////////////////////////////////////////////////// /** \param pFoeInd Pointer to the received mailbox data from the master. \return result of the operation (0 (success) or mailbox error code (MBXERR_.... defined in mailbox.h)) \brief This function is called when a FoE (File Access over EtherCAT) service is received from the master. *//////////////////////////////////////////////////////////////////////////////////////// UINT8 FOE_ServiceInd(TFOEMBX MBXMEM * pFoeInd) { /* initialize the result of the service checking */ UINT16 nextState = ECAT_FOE_ERRCODE_ILLEGAL; /* dataSize contains the size of the file data */ UINT16 dataSize = SWAPWORD(pFoeInd->MbxHeader.Length) - FOE_HEADER_SIZE; /* it has to be checked if the mailbox protocol is correct, the sent mailbox data length has to great enough for the service header of the FoE service */ if ( SWAPWORD(pFoeInd->MbxHeader.Length) < FOE_HEADER_SIZE ) { return MBXERR_SIZETOOSHORT; } switch ( SWAPWORD(pFoeInd->FoeHeader.OpCode) ) { case ECAT_FOE_OPCODE_RRQ: /* file read is requested */ if ( u16FileAccessState == FOE_READY ) { UINT32 u32Password = SWAPDWORD((((UINT32)pFoeInd->FoeHeader.Cmd.Password[FOE_COMMAND_LOWWORD]) | ((UINT32)pFoeInd->FoeHeader.Cmd.Password[FOE_COMMAND_HIGHWORD]) << 16)); /* last FoE sequence was finished, call application function */ nextState = FOE_Read((UINT16 MBXMEM *)pFoeInd->Data, dataSize, (UINT16*)pFoeInd->Data, u32Password); /* u32LastFileOffset contains the offset of the file which is sent now */ u32LastFileOffset = 0; if (nextState <= FOE_MAXDATA) { /* the first data packet shall be sent */ u32PacketNo = 1; /* u32FileOffset contains the offset of the file which shall be sent when the next FoE ACK is received */ u32FileOffset = nextState; } else if (nextState <= FOE_MAXBUSY) { u32PacketNo = 0; u32FileOffset = 0; u16FileAccessState = FOE_WAIT_FOR_ACK; } } break; case ECAT_FOE_OPCODE_WRQ: /* file write is requested */ if ( u16FileAccessState == FOE_READY ) { UINT32 u32Password = SWAPDWORD((((UINT32)pFoeInd->FoeHeader.Cmd.Password[FOE_COMMAND_LOWWORD]) | ((UINT32)pFoeInd->FoeHeader.Cmd.Password[FOE_COMMAND_HIGHWORD]) << 16)); /* last FoE sequence was finished, call application function */ nextState = FOE_Write((UINT16 MBXMEM *)pFoeInd->Data, dataSize, u32Password); if ( nextState == 0 ) { /* checking was successful, sent a FoE Ack service */ nextState = FOE_ACK; } /* initialize the packet number */ u32PacketNo = 0; } break; case ECAT_FOE_OPCODE_DATA: /* file data is received */ if ( u16FileAccessState == FOE_WAIT_FOR_DATA || u16FileAccessState == FOE_WAIT_FOR_LAST_DATA ) { UINT32 u32CmdPacketNo = SWAPDWORD((((UINT32)pFoeInd->FoeHeader.Cmd.PacketNo[FOE_COMMAND_LOWWORD]) | ((UINT32)pFoeInd->FoeHeader.Cmd.PacketNo[FOE_COMMAND_HIGHWORD]) << 16)); /*ET9300 Project Handler :(#if BIG_ENDIAN_16BIT) lines 170 to 172 deleted*/ /* we are waiting for file data, service is correct */ if ( u32CmdPacketNo == u32PacketNo ) { /* the packet number is correct, call application function to store the file data */ nextState = FOE_Data((UINT16 MBXMEM *)pFoeInd->Data, dataSize); if ( nextState == 0 ) { /* checking was successful, sent a FoE Ack service */ nextState = FOE_ACK; } } else { nextState = ECAT_FOE_ERRCODE_PACKENO; } } break; case ECAT_FOE_OPCODE_ACK: /* acknowledge is received, next file part can be sent */ if ( u16FileAccessState == FOE_WAIT_FOR_ACK ) { /* we are waiting for an acknowledge, service is correct, call the application function to get the next part of the file */ nextState = FOE_Ack( u32FileOffset, (UINT16 MBXMEM *)pFoeInd->Data ); /* u32LastFileOffset contains the offset of the file which is sent now */ u32LastFileOffset = u32FileOffset; if (nextState <= FOE_MAXDATA) { /* u32FileOffset contains the offset of the file which shall be sent when the next FoE ACK is received */ u32FileOffset += nextState; /* increment the packet number */ u32PacketNo++; } } else if ( u16FileAccessState == FOE_WAIT_FOR_LAST_ACK ) { /* we were waiting for the last acknowledge, now the sequence is finished */ nextState = FOE_FINISHED_NOACK; } break; case ECAT_FOE_OPCODE_ERR: /* a FoE Error service is received */ if ( u16FileAccessState != FOE_READY ) { UINT32 u32CmdErrorCode = SWAPDWORD((((UINT32)pFoeInd->FoeHeader.Cmd.ErrorCode[FOE_COMMAND_LOWWORD]) | ((UINT32)pFoeInd->FoeHeader.Cmd.ErrorCode[FOE_COMMAND_HIGHWORD]) << 16)); /* a file transmission sequence is active, inform the application, that this sequence was stopped */ FOE_Error( u32CmdErrorCode ); nextState = FOE_FINISHED; } break; case ECAT_FOE_OPCODE_BUSY: /* a FoE Busy service is received */ if ( u16FileAccessState == FOE_WAIT_FOR_ACK || u16FileAccessState == FOE_WAIT_FOR_LAST_ACK ) { /* we are waiting for an acknowledge, service is correct, call the application function to resend the last part of the file */ nextState = FOE_Busy( SWAPWORD(pFoeInd->FoeHeader.Cmd.Busy.Done), u32LastFileOffset, (UINT16 MBXMEM *)pFoeInd->Data ); } break; } if ( nextState <= FOE_MAXDATA ) { /* we send DATA and wait for ACK */ UINT32 d = SWAPDWORD(u32PacketNo); /* store the OpCode in the mailbox buffer */ pFoeInd->FoeHeader.OpCode = SWAPWORD(ECAT_FOE_OPCODE_DATA); /* store the packet number in the mailbox buffer */ pFoeInd->FoeHeader.Cmd.PacketNo[FOE_COMMAND_LOWWORD] = ((d & 0x0000FFFF)); pFoeInd->FoeHeader.Cmd.PacketNo[FOE_COMMAND_HIGHWORD] = ((d & 0xFFFF0000) >> 16); /* store the size of the mailbox data in the mailbox buffer */ pFoeInd->MbxHeader.Length = FOE_HEADER_SIZE + nextState; if ( nextState == u16SendMbxSize - FOE_HEADER_SIZE - MBX_HEADER_SIZE ) { /* packets still following, we wait for an ACK */ u16FileAccessState = FOE_WAIT_FOR_ACK; } else { /* it was the last Packet, we wait for the last ACK */ u16FileAccessState = FOE_WAIT_FOR_LAST_ACK; } } else if ( nextState <= FOE_MAXBUSY ) { /* we are still storing the received file data (in flash for example) and send BUSY and wait for the DATA to be sent again */ /* store the OpCode in the mailbox buffer */ pFoeInd->FoeHeader.OpCode = SWAPWORD(ECAT_FOE_OPCODE_BUSY); /* store the information how much progress we made until we can receive file data again */ pFoeInd->FoeHeader.Cmd.Busy.Done = SWAPWORD(nextState-FOE_MAXBUSY_ZERO); pFoeInd->FoeHeader.Cmd.Busy.Entire = 0; /* store the size of the mailbox data in the mailbox buffer */ pFoeInd->MbxHeader.Length = FOE_HEADER_SIZE; } else if ( nextState == FOE_ACK || nextState == FOE_ACKFINISHED ) { /* we send ACK and wait for DATA the next file data is expected with an incremented packet number, but we have to acknowledge the old packet first */ UINT32 d = SWAPDWORD(u32PacketNo); u32PacketNo++; /* store the OpCode in the mailbox buffer */ pFoeInd->FoeHeader.OpCode = SWAPWORD(ECAT_FOE_OPCODE_ACK); /* store the packet number in the mailbox buffer */ pFoeInd->FoeHeader.Cmd.PacketNo[FOE_COMMAND_LOWWORD] = ((d & 0x0000FFFF)); pFoeInd->FoeHeader.Cmd.PacketNo[FOE_COMMAND_HIGHWORD] = ((d & 0xFFFF0000) >> 16); /* store the size of the mailbox data in the mailbox buffer */ pFoeInd->MbxHeader.Length = SIZEOF(TFOEHEADER); /* we wait for the next data part */ if ( nextState == FOE_ACK ) { /* we wait for the next data part */ u16FileAccessState = FOE_WAIT_FOR_DATA; } else { /* the last data part was received */ u16FileAccessState = FOE_READY; } } else if ( nextState < FOE_ERROR ) { /* the file transmission sequence is finished, we have to send nothing */ u16FileAccessState = FOE_READY; APPL_FreeMailboxBuffer(pFoeInd); /*ET9300 Project Handler :(#if MAILBOX_QUEUE #else) lines 317 to 320 deleted*/ return 0; } else { UINT32 d = SWAPDWORD(nextState); UINT8 b = 0; /* store the OpCode in the mailbox buffer */ pFoeInd->FoeHeader.OpCode = SWAPWORD(ECAT_FOE_OPCODE_ERR); /* store the ErrorCode in the mailbox buffer */ pFoeInd->FoeHeader.Cmd.ErrorCode[FOE_COMMAND_LOWWORD] = ((d & 0x0000FFFF)); pFoeInd->FoeHeader.Cmd.ErrorCode[FOE_COMMAND_HIGHWORD] = ((d & 0xFFFF0000) >> 16); /* store the size of the mailbox data in the mailbox buffer */ pFoeInd->MbxHeader.Length = SIZEOF(TFOEHEADER); /* Error Text is returned in pFoeInd->Data */ while (b < 32) { UINT16 data = ((UINT16 MBXMEM *) pFoeInd->Data)[(b >> 1)]; if ((data & 0x00FF) == 0) { break; } b++; if ((data & 0xFF00) == 0) { break; } b++; } if ( b < 32 ) { pFoeInd->MbxHeader.Length += b+1; } /* the file transmission sequence is finished */ u16FileAccessState = FOE_READY; } /*ET9300 Project Handler :(#if BOOTSTRAPMODE_SUPPORTED) lines 363 to 385 deleted*/ { if ( MBX_MailboxSendReq((TMBX MBXMEM *) pFoeInd, FOE_SERVICE) != 0 ) { /* if the mailbox service could not be sent (or stored), the response will be stored in the variable pFoeSendStored and will be sent automatically from the mailbox handler (FOE_ContinueInd) when the send mailbox will be read the next time from the master */ pFoeSendStored = (TMBX MBXMEM *) pFoeInd; } } return 0; } ///////////////////////////////////////////////////////////////////////////////////////// /** \param pMbx Pointer to the free mailbox buffer \brief This function is called when the next mailbox fragment can be sent. *//////////////////////////////////////////////////////////////////////////////////////// void FOE_ContinueInd(TMBX MBXMEM * pMbx) { if ( pFoeSendStored ) { /* send the stored FoE service which could not be sent before */ /*ECATCHANGE_START(V5.13) FOE1*/ if (MBX_MailboxSendReq(pFoeSendStored, FOE_SERVICE) == 0) { pFoeSendStored = 0; } } /*ECATCHANGE_END(V5.13) EOE1*/ } /** @} */