Работает. Есть CiA402, OD из SSC Tool. Автономный со своим main(). Проверялся тестами на SOEM.
396 lines
15 KiB
C
396 lines
15 KiB
C
/*
|
|
* 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
|
|
|
|
<br>Changes to version V5.12:<br>
|
|
V5.13 EOE1: <br>
|
|
V5.13 FOE1: handle failure on continue indication<br>
|
|
<br>Changes to version V5.11:<br>
|
|
V5.12 FOE2: handle 16bit only access<br>
|
|
V5.12 MBX4: in case of a disable mailbox queue and two consecutive foe uploads the mailbox receive handler is blocked<br>
|
|
<br>Changes to version V5.10:<br>
|
|
V5.11 FOE1: handle busy on a read request, change OPMode to OPCode (same terms as in the spec)<br>
|
|
<br>Changes to version V5.0:<br>
|
|
V5.10 MBX1: Remove multiple swapping of length filed in mailbox header<br>
|
|
<br>Changes to version V4.40:<br>
|
|
V5.0 FOE1: Free memory if FoE_ServciceInd() is left.<br>
|
|
V5.0 FOE2: Update error code (Bit 15 shall not be set).<br>
|
|
V5.0 FOE3: Update byte swapping for big endian and big endian 16 configurations.<br>
|
|
V5.0 FOE4: Update Error text handling for 16Bit operations.<br>
|
|
<br>Changes to version V4.11:<br>
|
|
V4.40 FOE1: correct FoE busy values<br>
|
|
V4.40 MBX2: HW_CopyToSendMailbox merge to a generic function (new: MBX_CopyToSendMailbox)<br>
|
|
<br>Changes to version V4.10:<br>
|
|
V4.11 FOE 1 fixed a problem with a huge FOE_MADATA define<br>
|
|
V4.11 FOE 2 u16PacketNo is now a 32bit variable (u32PacketNo) to conform with the spec<br>
|
|
<br>Changes to version V4.00:<br>
|
|
V4.10 FOE 1-3 when downloading the firmware the FoE state machine was not set to FOE_READY<br>
|
|
when the last data segment was downloaded so that the next FoE service failed<br>
|
|
<br>Changes to version V3.20:<br>
|
|
V4.00 FOE 1 - if a FoE Error service is received it should not be acknowledged<br>
|
|
V4.00 FOE 2 - if a FoE service response could not be sent because the mailbox is full<br>
|
|
it has to be re-transmit otherwise the response get lost<br>
|
|
|
|
*/
|
|
|
|
/*-----------------------------------------------------------------------------------------
|
|
------
|
|
------ 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*/
|
|
}
|
|
|
|
/** @} */
|
|
|