SOEM/soem/ethercatfoe.c

409 lines
14 KiB
C

/*
* Simple Open EtherCAT Master Library
*
* File : ethercatfoe.c
* Version : 1.3.1
* Date : 11-03-2015
* Copyright (C) 2005-2015 Speciaal Machinefabriek Ketels v.o.f.
* Copyright (C) 2005-2015 Arthur Ketels
* Copyright (C) 2008-2009 TU/e Technische Universiteit Eindhoven
* Copyright (C) 2014-2015 rt-labs AB , Sweden
*
* SOEM is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License version 2 as published by the Free
* Software Foundation.
*
* SOEM is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* As a special exception, if other files instantiate templates or use macros
* or inline functions from this file, or you compile this file and link it
* with other works to produce a work based on this file, this file does not
* by itself cause the resulting work to be covered by the GNU General Public
* License. However the source code for this file must still be made available
* in accordance with section (3) of the GNU General Public License.
*
* This exception does not invalidate any other reasons why a work based on
* this file might be covered by the GNU General Public License.
*
* The EtherCAT Technology, the trade name and logo “EtherCAT” are the intellectual
* property of, and protected by Beckhoff Automation GmbH. You can use SOEM for
* the sole purpose of creating, using and/or selling or otherwise distributing
* an EtherCAT network master provided that an EtherCAT Master License is obtained
* from Beckhoff Automation GmbH.
*
* In case you did not receive a copy of the EtherCAT Master License along with
* SOEM write to Beckhoff Automation GmbH, Eiserstraße 5, D-33415 Verl, Germany
* (www.beckhoff.com).
* 14-06-2010 : fixed bug in FOEread() by Torsten Bitterlich
*/
/** \file
* \brief
* File over EtherCAT (FoE) module.
*
* SDO read / write and SDO service functions
*/
#include <stdio.h>
#include <string.h>
#include "osal.h"
#include "oshw.h"
#include "ethercattype.h"
#include "ethercatbase.h"
#include "ethercatmain.h"
#include "ethercatfoe.h"
#define EC_MAXFOEDATA 512
/** FOE structure.
* Used for Read, Write, Data, Ack and Error mailbox packets.
*/
PACKED_BEGIN
typedef struct PACKED
{
ec_mbxheadert MbxHeader;
uint8 OpCode;
uint8 Reserved;
union
{
uint32 Password;
uint32 PacketNumber;
uint32 ErrorCode;
};
union
{
char FileName[EC_MAXFOEDATA];
uint8 Data[EC_MAXFOEDATA];
char ErrorText[EC_MAXFOEDATA];
};
} ec_FOEt;
PACKED_END
/** FoE progress hook.
*
* @param[in] context = context struct
* @param[in] hook = Pointer to hook function.
* @return 1
*/
int ecx_FOEdefinehook(ecx_contextt *context, void *hook)
{
context->FOEhook = hook;
return 1;
}
/** FoE read, blocking.
*
* @param[in] context = context struct
* @param[in] slave = Slave number.
* @param[in] filename = Filename of file to read.
* @param[in] password = password.
* @param[in,out] psize = Size in bytes of file buffer, returns bytes read from file.
* @param[out] p = Pointer to file buffer
* @param[in] timeout = Timeout per mailbox cycle in us, standard is EC_TIMEOUTRXM
* @return Workcounter from last slave response
*/
int ecx_FOEread(ecx_contextt *context, uint16 slave, char *filename, uint32 password, int *psize, void *p, int timeout)
{
ec_FOEt *FOEp, *aFOEp;
int wkc;
int32 dataread = 0;
int32 buffersize, packetnumber, prevpacket = 0;
uint16 fnsize, maxdata, segmentdata;
ec_mbxbuft MbxIn, MbxOut;
uint8 cnt;
boolean worktodo;
buffersize = *psize;
ec_clearmbx(&MbxIn);
/* Empty slave out mailbox if something is in. Timout set to 0 */
wkc = ecx_mbxreceive(context, slave, (ec_mbxbuft *)&MbxIn, 0);
ec_clearmbx(&MbxOut);
aFOEp = (ec_FOEt *)&MbxIn;
FOEp = (ec_FOEt *)&MbxOut;
fnsize = strlen(filename);
maxdata = context->slavelist[slave].mbx_l - 12;
if (fnsize > maxdata)
{
fnsize = maxdata;
}
FOEp->MbxHeader.length = htoes(0x0006 + fnsize);
FOEp->MbxHeader.address = htoes(0x0000);
FOEp->MbxHeader.priority = 0x00;
/* get new mailbox count value, used as session handle */
cnt = ec_nextmbxcnt(context->slavelist[slave].mbx_cnt);
context->slavelist[slave].mbx_cnt = cnt;
FOEp->MbxHeader.mbxtype = ECT_MBXT_FOE + (cnt << 4); /* FoE */
FOEp->OpCode = ECT_FOE_READ;
FOEp->Password = htoel(password);
/* copy filename in mailbox */
memcpy(&FOEp->FileName[0], filename, fnsize);
/* send FoE request to slave */
wkc = ecx_mbxsend(context, slave, (ec_mbxbuft *)&MbxOut, EC_TIMEOUTTXM);
if (wkc > 0) /* succeeded to place mailbox in slave ? */
{
do
{
worktodo = FALSE;
/* clean mailboxbuffer */
ec_clearmbx(&MbxIn);
/* read slave response */
wkc = ecx_mbxreceive(context, slave, (ec_mbxbuft *)&MbxIn, timeout);
if (wkc > 0) /* succeeded to read slave response ? */
{
/* slave response should be FoE */
if ((aFOEp->MbxHeader.mbxtype & 0x0f) == ECT_MBXT_FOE)
{
if(aFOEp->OpCode == ECT_FOE_DATA)
{
segmentdata = etohs(aFOEp->MbxHeader.length) - 0x0006;
packetnumber = etohl(aFOEp->PacketNumber);
if ((packetnumber == ++prevpacket) && (dataread + segmentdata <= buffersize))
{
memcpy(p, &aFOEp->Data[0], segmentdata);
dataread += segmentdata;
p = (uint8 *)p + segmentdata;
if (segmentdata == maxdata)
{
worktodo = TRUE;
}
FOEp->MbxHeader.length = htoes(0x0006);
FOEp->MbxHeader.address = htoes(0x0000);
FOEp->MbxHeader.priority = 0x00;
/* get new mailbox count value */
cnt = ec_nextmbxcnt(context->slavelist[slave].mbx_cnt);
context->slavelist[slave].mbx_cnt = cnt;
FOEp->MbxHeader.mbxtype = ECT_MBXT_FOE + (cnt << 4); /* FoE */
FOEp->OpCode = ECT_FOE_ACK;
FOEp->PacketNumber = htoel(packetnumber);
/* send FoE ack to slave */
wkc = ecx_mbxsend(context, slave, (ec_mbxbuft *)&MbxOut, EC_TIMEOUTTXM);
if (wkc <= 0)
{
worktodo = FALSE;
}
if (context->FOEhook)
{
context->FOEhook(slave, packetnumber, dataread);
}
}
else
{
/* FoE error */
wkc = -EC_ERR_TYPE_FOE_BUF2SMALL;
}
}
else
{
if(aFOEp->OpCode == ECT_FOE_ERROR)
{
/* FoE error */
wkc = -EC_ERR_TYPE_FOE_ERROR;
}
else
{
/* unexpected mailbox received */
wkc = -EC_ERR_TYPE_PACKET_ERROR;
}
}
}
else
{
/* unexpected mailbox received */
wkc = -EC_ERR_TYPE_PACKET_ERROR;
}
*psize = dataread;
}
} while (worktodo);
}
return wkc;
}
/** FoE write, blocking.
*
* @param[in] context = context struct
* @param[in] slave = Slave number.
* @param[in] filename = Filename of file to write.
* @param[in] password = password.
* @param[in] psize = Size in bytes of file buffer.
* @param[out] p = Pointer to file buffer
* @param[in] timeout = Timeout per mailbox cycle in us, standard is EC_TIMEOUTRXM
* @return Workcounter from last slave response
*/
int ecx_FOEwrite(ecx_contextt *context, uint16 slave, char *filename, uint32 password, int psize, void *p, int timeout)
{
ec_FOEt *FOEp, *aFOEp;
int wkc;
int32 packetnumber, sendpacket = 0;
uint16 fnsize, maxdata;
int segmentdata;
ec_mbxbuft MbxIn, MbxOut;
uint8 cnt;
boolean worktodo, dofinalzero;
int tsize;
ec_clearmbx(&MbxIn);
/* Empty slave out mailbox if something is in. Timout set to 0 */
wkc = ecx_mbxreceive(context, slave, (ec_mbxbuft *)&MbxIn, 0);
ec_clearmbx(&MbxOut);
aFOEp = (ec_FOEt *)&MbxIn;
FOEp = (ec_FOEt *)&MbxOut;
dofinalzero = FALSE;
fnsize = strlen(filename);
maxdata = context->slavelist[slave].mbx_l - 12;
if (fnsize > maxdata)
{
fnsize = maxdata;
}
FOEp->MbxHeader.length = htoes(0x0006 + fnsize);
FOEp->MbxHeader.address = htoes(0x0000);
FOEp->MbxHeader.priority = 0x00;
/* get new mailbox count value, used as session handle */
cnt = ec_nextmbxcnt(context->slavelist[slave].mbx_cnt);
context->slavelist[slave].mbx_cnt = cnt;
FOEp->MbxHeader.mbxtype = ECT_MBXT_FOE + (cnt << 4); /* FoE */
FOEp->OpCode = ECT_FOE_WRITE;
FOEp->Password = htoel(password);
/* copy filename in mailbox */
memcpy(&FOEp->FileName[0], filename, fnsize);
/* send FoE request to slave */
wkc = ecx_mbxsend(context, slave, (ec_mbxbuft *)&MbxOut, EC_TIMEOUTTXM);
if (wkc > 0) /* succeeded to place mailbox in slave ? */
{
do
{
worktodo = FALSE;
/* clean mailboxbuffer */
ec_clearmbx(&MbxIn);
/* read slave response */
wkc = ecx_mbxreceive(context, slave, (ec_mbxbuft *)&MbxIn, timeout);
if (wkc > 0) /* succeeded to read slave response ? */
{
/* slave response should be FoE */
if ((aFOEp->MbxHeader.mbxtype & 0x0f) == ECT_MBXT_FOE)
{
switch (aFOEp->OpCode)
{
case ECT_FOE_ACK:
{
packetnumber = etohl(aFOEp->PacketNumber);
if (packetnumber == sendpacket)
{
if (context->FOEhook)
{
context->FOEhook(slave, packetnumber, psize);
}
tsize = psize;
if (tsize > maxdata)
{
tsize = maxdata;
}
if(tsize || dofinalzero)
{
worktodo = TRUE;
dofinalzero = FALSE;
segmentdata = tsize;
psize -= segmentdata;
/* if last packet was full size, add a zero size packet as final */
/* EOF is defined as packetsize < full packetsize */
if (!psize && (segmentdata == maxdata))
{
dofinalzero = TRUE;
}
FOEp->MbxHeader.length = htoes(0x0006 + segmentdata);
FOEp->MbxHeader.address = htoes(0x0000);
FOEp->MbxHeader.priority = 0x00;
/* get new mailbox count value */
cnt = ec_nextmbxcnt(context->slavelist[slave].mbx_cnt);
context->slavelist[slave].mbx_cnt = cnt;
FOEp->MbxHeader.mbxtype = ECT_MBXT_FOE + (cnt << 4); /* FoE */
FOEp->OpCode = ECT_FOE_DATA;
sendpacket++;
FOEp->PacketNumber = htoel(sendpacket);
memcpy(&FOEp->Data[0], p, segmentdata);
p = (uint8 *)p + segmentdata;
/* send FoE data to slave */
wkc = ecx_mbxsend(context, slave, (ec_mbxbuft *)&MbxOut, EC_TIMEOUTTXM);
if (wkc <= 0)
{
worktodo = FALSE;
}
}
}
else
{
/* FoE error */
wkc = -EC_ERR_TYPE_FOE_PACKETNUMBER;
}
break;
}
case ECT_FOE_BUSY:
{
/* resend if data has been send before */
/* otherwise ignore */
if (sendpacket)
{
if (!psize)
{
dofinalzero = TRUE;
}
psize += segmentdata;
p = (uint8 *)p - segmentdata;
--sendpacket;
}
break;
}
case ECT_FOE_ERROR:
{
/* FoE error */
if (aFOEp->ErrorCode == 0x8001)
{
wkc = -EC_ERR_TYPE_FOE_FILE_NOTFOUND;
}
else
{
wkc = -EC_ERR_TYPE_FOE_ERROR;
}
break;
}
default:
{
/* unexpected mailbox received */
wkc = -EC_ERR_TYPE_PACKET_ERROR;
break;
}
}
}
else
{
/* unexpected mailbox received */
wkc = -EC_ERR_TYPE_PACKET_ERROR;
}
}
} while (worktodo);
}
return wkc;
}
#ifdef EC_VER1
int ec_FOEdefinehook(void *hook)
{
return ecx_FOEdefinehook(&ecx_context, hook);
}
int ec_FOEread(uint16 slave, char *filename, uint32 password, int *psize, void *p, int timeout)
{
return ecx_FOEread(&ecx_context, slave, filename, password, psize, p, timeout);
}
int ec_FOEwrite(uint16 slave, char *filename, uint32 password, int psize, void *p, int timeout)
{
return ecx_FOEwrite(&ecx_context, slave, filename, password, psize, p, timeout);
}
#endif