SOEM EtherCAT Master library for STM Nucleo F767ZI

Dependents:   EasyCAT_LAB_simple EasyCAT_LAB_very_simple EasyCAT_LAB

  • It has been developed for the EasyCAT LAB , a complete educational and experimental EtherCAT® system, composed of one master and two slaves .

Warning

/media/uploads/EasyCAT/easycat_lab.jpg

Files at this revision

API Documentation at this revision

Comitter:
EasyCAT
Date:
Tue Jun 11 10:29:09 2019 +0000
Commit message:
SOEM EtherCAT Master Library for STM Nucleo F767ZI

Changed in this revision

LICENSE.txt Show annotated file Show diff for this revision Revisions of this file
SOEM/ethercat.h Show annotated file Show diff for this revision Revisions of this file
SOEM/ethercatbase.c Show annotated file Show diff for this revision Revisions of this file
SOEM/ethercatbase.h Show annotated file Show diff for this revision Revisions of this file
SOEM/ethercatcoe.c Show annotated file Show diff for this revision Revisions of this file
SOEM/ethercatcoe.h Show annotated file Show diff for this revision Revisions of this file
SOEM/ethercatconfig.c Show annotated file Show diff for this revision Revisions of this file
SOEM/ethercatconfig.h Show annotated file Show diff for this revision Revisions of this file
SOEM/ethercatconfiglist.h Show annotated file Show diff for this revision Revisions of this file
SOEM/ethercatdc.c Show annotated file Show diff for this revision Revisions of this file
SOEM/ethercatdc.h Show annotated file Show diff for this revision Revisions of this file
SOEM/ethercateoe.c Show annotated file Show diff for this revision Revisions of this file
SOEM/ethercateoe.h Show annotated file Show diff for this revision Revisions of this file
SOEM/ethercatfoe.c Show annotated file Show diff for this revision Revisions of this file
SOEM/ethercatfoe.h Show annotated file Show diff for this revision Revisions of this file
SOEM/ethercatmain.c Show annotated file Show diff for this revision Revisions of this file
SOEM/ethercatmain.h Show annotated file Show diff for this revision Revisions of this file
SOEM/ethercatprint.c Show annotated file Show diff for this revision Revisions of this file
SOEM/ethercatprint.h Show annotated file Show diff for this revision Revisions of this file
SOEM/ethercatsoe.c Show annotated file Show diff for this revision Revisions of this file
SOEM/ethercatsoe.h Show annotated file Show diff for this revision Revisions of this file
SOEM/ethercattype.h Show annotated file Show diff for this revision Revisions of this file
osal/osal.cpp Show annotated file Show diff for this revision Revisions of this file
osal/osal.h Show annotated file Show diff for this revision Revisions of this file
osal/osal_defs.h Show annotated file Show diff for this revision Revisions of this file
oshw/nicdrv.cpp Show annotated file Show diff for this revision Revisions of this file
oshw/nicdrv.h Show annotated file Show diff for this revision Revisions of this file
oshw/oshw.cpp Show annotated file Show diff for this revision Revisions of this file
oshw/oshw.h Show annotated file Show diff for this revision Revisions of this file
diff -r 000000000000 -r 543d6784d4cc LICENSE.txt
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/LICENSE.txt	Tue Jun 11 10:29:09 2019 +0000
@@ -0,0 +1,34 @@
+Simple Open EtherCAT Master Library
+
+Copyright (C) 2005-2017 Speciaal Machinefabriek Ketels v.o.f.
+Copyright (C) 2005-2017 Arthur Ketels
+Copyright (C) 2008-2009 TU/e Technische Universiteit Eindhoven
+Copyright (C) 2009-2017 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, Eiserstrasse 5, D-33415 Verl, Germany
+(www.beckhoff.com).
diff -r 000000000000 -r 543d6784d4cc SOEM/ethercat.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SOEM/ethercat.h	Tue Jun 11 10:29:09 2019 +0000
@@ -0,0 +1,26 @@
+/*
+ * Licensed under the GNU General Public License version 2 with exceptions. See
+ * LICENSE file in the project root for full license information
+ */
+
+/** \file
+ * \brief
+ * Headerfile for all ethercat headers
+ */
+
+#ifndef _EC_ETHERCAT_H
+#define _EC_ETHERCAT_H
+
+#include "ethercattype.h"
+#include "nicdrv.h"
+#include "ethercatbase.h"
+#include "ethercatmain.h"
+#include "ethercatdc.h"
+#include "ethercatcoe.h"
+#include "ethercatfoe.h"
+#include "ethercatsoe.h"
+#include "ethercateoe.h"
+#include "ethercatconfig.h"
+#include "ethercatprint.h"
+
+#endif /* _EC_ETHERCAT_H */
diff -r 000000000000 -r 543d6784d4cc SOEM/ethercatbase.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SOEM/ethercatbase.c	Tue Jun 11 10:29:09 2019 +0000
@@ -0,0 +1,637 @@
+/*
+ * Licensed under the GNU General Public License version 2 with exceptions. See
+ * LICENSE file in the project root for full license information
+ */
+
+/** \file
+ * \brief
+ * Base EtherCAT functions.
+ *
+ * Setting up a datagram in an ethernet frame.
+ * EtherCAT datagram primitives, broadcast, auto increment, configured and
+ * logical addressed data transfers. All base transfers are blocking, so
+ * wait for the frame to be returned to the master or timeout. If this is
+ * not acceptable build your own datagrams and use the functions from nicdrv.c.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include "oshw.h"
+#include "osal.h"
+#include "ethercattype.h"
+#include "ethercatbase.h"
+
+/** Write data to EtherCAT datagram.
+ *
+ * @param[out] datagramdata   = data part of datagram
+ * @param[in]  com            = command
+ * @param[in]  length         = length of databuffer
+ * @param[in]  data           = databuffer to be copied into datagram
+ */
+static void ecx_writedatagramdata(void *datagramdata, ec_cmdtype com, uint16 length, const void * data)
+{
+   if (length > 0)
+   {
+      switch (com)
+      {
+         case EC_CMD_NOP:
+            /* Fall-through */
+         case EC_CMD_APRD:
+            /* Fall-through */
+         case EC_CMD_FPRD:
+            /* Fall-through */
+         case EC_CMD_BRD:
+            /* Fall-through */
+         case EC_CMD_LRD:
+            /* no data to write. initialise data so frame is in a known state */
+            memset(datagramdata, 0, length);
+            break;
+         default:
+            memcpy(datagramdata, data, length);
+            break;
+      }
+   }
+}
+
+/** Generate and set EtherCAT datagram in a standard ethernet frame.
+ *
+ * @param[in] port        = port context struct
+ * @param[out] frame       = framebuffer
+ * @param[in]  com         = command
+ * @param[in]  idx         = index used for TX and RX buffers
+ * @param[in]  ADP         = Address Position
+ * @param[in]  ADO         = Address Offset
+ * @param[in]  length      = length of datagram excluding EtherCAT header
+ * @param[in]  data        = databuffer to be copied in datagram
+ * @return always 0
+ */
+int ecx_setupdatagram(ecx_portt *port, void *frame, uint8 com, uint8 idx, uint16 ADP, uint16 ADO, uint16 length, void *data)
+{
+   ec_comt *datagramP;
+   uint8 *frameP;
+
+   frameP = frame;
+   /* Ethernet header is preset and fixed in frame buffers
+      EtherCAT header needs to be added after that */
+   datagramP = (ec_comt*)&frameP[ETH_HEADERSIZE];
+   datagramP->elength = htoes(EC_ECATTYPE + EC_HEADERSIZE + length);
+   datagramP->command = com;
+   datagramP->index = idx;
+   datagramP->ADP = htoes(ADP);
+   datagramP->ADO = htoes(ADO);
+   datagramP->dlength = htoes(length);
+   ecx_writedatagramdata(&frameP[ETH_HEADERSIZE + EC_HEADERSIZE], com, length, data);
+   /* set WKC to zero */
+   frameP[ETH_HEADERSIZE + EC_HEADERSIZE + length] = 0x00;
+   frameP[ETH_HEADERSIZE + EC_HEADERSIZE + length + 1] = 0x00;
+   /* set size of frame in buffer array */
+   port->txbuflength[idx] = ETH_HEADERSIZE + EC_HEADERSIZE + EC_WKCSIZE + length;
+
+   return 0;
+}
+
+/** Add EtherCAT datagram to a standard ethernet frame with existing datagram(s).
+ *
+ * @param[in] port        = port context struct
+ * @param[out] frame      = framebuffer
+ * @param[in]  com        = command
+ * @param[in]  idx        = index used for TX and RX buffers
+ * @param[in]  more       = TRUE if still more datagrams to follow
+ * @param[in]  ADP        = Address Position
+ * @param[in]  ADO        = Address Offset
+ * @param[in]  length     = length of datagram excluding EtherCAT header
+ * @param[in]  data       = databuffer to be copied in datagram
+ * @return Offset to data in rx frame, usefull to retrieve data after RX.
+ */
+int ecx_adddatagram(ecx_portt *port, void *frame, uint8 com, uint8 idx, boolean more, uint16 ADP, uint16 ADO, uint16 length, void *data)
+{
+   ec_comt *datagramP;
+   uint8 *frameP;
+   uint16 prevlength;
+
+   frameP = frame;
+   /* copy previous frame size */
+   prevlength = port->txbuflength[idx];
+   datagramP = (ec_comt*)&frameP[ETH_HEADERSIZE];
+   /* add new datagram to ethernet frame size */
+   datagramP->elength = htoes( etohs(datagramP->elength) + EC_HEADERSIZE + length );
+   /* add "datagram follows" flag to previous subframe dlength */
+   datagramP->dlength = htoes( etohs(datagramP->dlength) | EC_DATAGRAMFOLLOWS );
+   /* set new EtherCAT header position */
+   datagramP = (ec_comt*)&frameP[prevlength - EC_ELENGTHSIZE];
+   datagramP->command = com;
+   datagramP->index = idx;
+   datagramP->ADP = htoes(ADP);
+   datagramP->ADO = htoes(ADO);
+   if (more)
+   {
+      /* this is not the last datagram to add */
+      datagramP->dlength = htoes(length | EC_DATAGRAMFOLLOWS);
+   }
+   else
+   {
+      /* this is the last datagram in the frame */
+      datagramP->dlength = htoes(length);
+   }
+   ecx_writedatagramdata(&frameP[prevlength + EC_HEADERSIZE - EC_ELENGTHSIZE], com, length, data);
+   /* set WKC to zero */
+   frameP[prevlength + EC_HEADERSIZE - EC_ELENGTHSIZE + length] = 0x00;
+   frameP[prevlength + EC_HEADERSIZE - EC_ELENGTHSIZE + length + 1] = 0x00;
+   /* set size of frame in buffer array */
+   port->txbuflength[idx] = prevlength + EC_HEADERSIZE - EC_ELENGTHSIZE + EC_WKCSIZE + length;
+
+   /* return offset to data in rx frame
+      14 bytes smaller than tx frame due to stripping of ethernet header */
+   return prevlength + EC_HEADERSIZE - EC_ELENGTHSIZE - ETH_HEADERSIZE;
+}
+
+/** BRW "broadcast write" primitive. Blocking.
+ *
+ * @param[in] port        = port context struct
+ * @param[in] ADP         = Address Position, normally 0
+ * @param[in] ADO         = Address Offset, slave memory address
+ * @param[in] length      = length of databuffer
+ * @param[in] data        = databuffer to be written to slaves
+ * @param[in] timeout     = timeout in us, standard is EC_TIMEOUTRET
+ * @return Workcounter or EC_NOFRAME
+ */
+int ecx_BWR (ecx_portt *port, uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout)
+{
+   uint8 idx;
+   int wkc;
+
+   /* get fresh index */
+   idx = ecx_getindex (port);
+   /* setup datagram */
+   ecx_setupdatagram (port, &(port->txbuf[idx]), EC_CMD_BWR, idx, ADP, ADO, length, data);
+   /* send data and wait for answer */
+   wkc = ecx_srconfirm (port, idx, timeout);
+   /* clear buffer status */
+   ecx_setbufstat (port, idx, EC_BUF_EMPTY);
+
+   return wkc;
+}
+
+/** BRD "broadcast read" primitive. Blocking.
+ *
+ * @param[in] port        = port context struct
+ * @param[in]  ADP        = Address Position, normally 0
+ * @param[in]  ADO        = Address Offset, slave memory address
+ * @param[in]  length     = length of databuffer
+ * @param[out] data       = databuffer to put slave data in
+ * @param[in]  timeout    = timeout in us, standard is EC_TIMEOUTRET
+ * @return Workcounter or EC_NOFRAME
+ */
+int ecx_BRD(ecx_portt *port, uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout)
+{
+   uint8 idx;
+   int wkc;
+
+   /* get fresh index */
+   idx = ecx_getindex(port);
+   /* setup datagram */
+   ecx_setupdatagram(port, &(port->txbuf[idx]), EC_CMD_BRD, idx, ADP, ADO, length, data);
+   /* send data and wait for answer */
+   wkc = ecx_srconfirm (port, idx, timeout);
+   if (wkc > 0)
+   {
+      /* copy datagram to data buffer */
+      memcpy(data, &(port->rxbuf[idx][EC_HEADERSIZE]), length);
+   }
+   /* clear buffer status */
+   ecx_setbufstat(port, idx, EC_BUF_EMPTY);
+
+   return wkc;
+}
+
+/** APRD "auto increment address read" primitive. Blocking.
+ *
+ * @param[in] port        = port context struct
+ * @param[in]  ADP        = Address Position, each slave ++, slave that has 0 executes
+ * @param[in]  ADO        = Address Offset, slave memory address
+ * @param[in]  length     = length of databuffer
+ * @param[out] data       = databuffer to put slave data in
+ * @param[in]  timeout    = timeout in us, standard is EC_TIMEOUTRET
+ * @return Workcounter or EC_NOFRAME
+ */
+int ecx_APRD(ecx_portt *port, uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout)
+{
+   int wkc;
+   uint8 idx;
+
+   idx = ecx_getindex(port);
+   ecx_setupdatagram(port, &(port->txbuf[idx]), EC_CMD_APRD, idx, ADP, ADO, length, data);
+   wkc = ecx_srconfirm(port, idx, timeout);
+   if (wkc > 0)
+   {
+      memcpy(data, &(port->rxbuf[idx][EC_HEADERSIZE]), length);
+   }
+   ecx_setbufstat(port, idx, EC_BUF_EMPTY);
+
+   return wkc;
+}
+
+/** APRMW "auto increment address read, multiple write" primitive. Blocking.
+ *
+ * @param[in] port        = port context struct
+ * @param[in]  ADP        = Address Position, each slave ++, slave that has 0 reads,
+ *                          following slaves write.
+ * @param[in]  ADO        = Address Offset, slave memory address
+ * @param[in]  length     = length of databuffer
+ * @param[out] data       = databuffer to put slave data in
+ * @param[in]  timeout    = timeout in us, standard is EC_TIMEOUTRET
+ * @return Workcounter or EC_NOFRAME
+ */
+int ecx_ARMW(ecx_portt *port, uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout)
+{
+   int wkc;
+   uint8 idx;
+
+   idx = ecx_getindex(port);
+   ecx_setupdatagram(port, &(port->txbuf[idx]), EC_CMD_ARMW, idx, ADP, ADO, length, data);
+   wkc = ecx_srconfirm(port, idx, timeout);
+   if (wkc > 0)
+   {
+      memcpy(data, &(port->rxbuf[idx][EC_HEADERSIZE]), length);
+   }
+   ecx_setbufstat(port, idx, EC_BUF_EMPTY);
+
+   return wkc;
+}
+
+/** FPRMW "configured address read, multiple write" primitive. Blocking.
+ *
+ * @param[in] port        = port context struct
+ * @param[in]  ADP        = Address Position, slave that has address reads,
+ *                          following slaves write.
+ * @param[in]  ADO        = Address Offset, slave memory address
+ * @param[in]  length     = length of databuffer
+ * @param[out] data       = databuffer to put slave data in
+ * @param[in]  timeout    = timeout in us, standard is EC_TIMEOUTRET
+ * @return Workcounter or EC_NOFRAME
+ */
+int ecx_FRMW(ecx_portt *port, uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout)
+{
+   int wkc;
+   uint8 idx;
+
+   idx = ecx_getindex(port);
+   ecx_setupdatagram(port, &(port->txbuf[idx]), EC_CMD_FRMW, idx, ADP, ADO, length, data);
+   wkc = ecx_srconfirm(port, idx, timeout);
+   if (wkc > 0)
+   {
+      memcpy(data, &(port->rxbuf[idx][EC_HEADERSIZE]), length);
+   }
+   ecx_setbufstat(port, idx, EC_BUF_EMPTY);
+
+   return wkc;
+}
+
+/** APRDw "auto increment address read" word return primitive. Blocking.
+ *
+ * @param[in] port        = port context struct
+ * @param[in] ADP         = Address Position, each slave ++, slave that has 0 reads.
+ * @param[in] ADO         = Address Offset, slave memory address
+ * @param[in] timeout     = timeout in us, standard is EC_TIMEOUTRET
+ * @return word data from slave
+ */
+uint16 ecx_APRDw(ecx_portt *port, uint16 ADP, uint16 ADO, int timeout)
+{
+   uint16 w;
+
+   w = 0;
+   ecx_APRD(port, ADP, ADO, sizeof(w), &w, timeout);
+
+   return w;
+}
+
+/** FPRD "configured address read" primitive. Blocking.
+ *
+ * @param[in] port        = port context struct
+ * @param[in]  ADP        = Address Position, slave that has address reads.
+ * @param[in]  ADO        = Address Offset, slave memory address
+ * @param[in]  length     = length of databuffer
+ * @param[out] data       = databuffer to put slave data in
+ * @param[in]  timeout    = timeout in us, standard is EC_TIMEOUTRET
+ * @return Workcounter or EC_NOFRAME
+ */
+int ecx_FPRD(ecx_portt *port, uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout)
+{
+   int wkc;
+   uint8 idx;
+
+   idx = ecx_getindex(port);
+   ecx_setupdatagram(port, &(port->txbuf[idx]), EC_CMD_FPRD, idx, ADP, ADO, length, data);
+   wkc = ecx_srconfirm(port, idx, timeout);
+   if (wkc > 0)
+   {
+      memcpy(data, &(port->rxbuf[idx][EC_HEADERSIZE]), length);
+   }
+   ecx_setbufstat(port, idx, EC_BUF_EMPTY);
+
+   return wkc;
+}
+
+/** FPRDw "configured address read" word return primitive. Blocking.
+ *
+ * @param[in] port        = port context struct
+ * @param[in] ADP         = Address Position, slave that has address reads.
+ * @param[in] ADO         = Address Offset, slave memory address
+ * @param[in] timeout     = timeout in us, standard is EC_TIMEOUTRET
+ * @return word data from slave
+ */
+uint16 ecx_FPRDw(ecx_portt *port, uint16 ADP, uint16 ADO, int timeout)
+{
+   uint16 w;
+
+   w = 0;
+   ecx_FPRD(port, ADP, ADO, sizeof(w), &w, timeout);
+   return w;
+}
+
+/** APWR "auto increment address write" primitive. Blocking.
+ *
+ * @param[in] port        = port context struct
+ * @param[in] ADP         = Address Position, each slave ++, slave that has 0 writes.
+ * @param[in] ADO         = Address Offset, slave memory address
+ * @param[in] length      = length of databuffer
+ * @param[in] data        = databuffer to write to slave.
+ * @param[in] timeout     = timeout in us, standard is EC_TIMEOUTRET
+ * @return Workcounter or EC_NOFRAME
+ */
+int ecx_APWR(ecx_portt *port, uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout)
+{
+   uint8 idx;
+   int wkc;
+
+   idx = ecx_getindex(port);
+   ecx_setupdatagram(port, &(port->txbuf[idx]), EC_CMD_APWR, idx, ADP, ADO, length, data);
+   wkc = ecx_srconfirm(port, idx, timeout);
+   ecx_setbufstat(port, idx, EC_BUF_EMPTY);
+
+   return wkc;
+}
+
+/** APWRw "auto increment address write" word primitive. Blocking.
+ *
+ * @param[in] port        = port context struct
+ * @param[in] ADP         = Address Position, each slave ++, slave that has 0 writes.
+ * @param[in] ADO         = Address Offset, slave memory address
+ * @param[in] data        = word data to write to slave.
+ * @param[in] timeout     = timeout in us, standard is EC_TIMEOUTRET
+ * @return Workcounter or EC_NOFRAME
+ */
+int ecx_APWRw(ecx_portt *port, uint16 ADP, uint16 ADO, uint16 data, int timeout)
+{
+   return ecx_APWR(port, ADP, ADO, sizeof(data), &data, timeout);
+}
+
+/** FPWR "configured address write" primitive. Blocking.
+ *
+ * @param[in] port        = port context struct
+ * @param[in] ADP         = Address Position, slave that has address writes.
+ * @param[in] ADO         = Address Offset, slave memory address
+ * @param[in] length      = length of databuffer
+ * @param[in] data        = databuffer to write to slave.
+ * @param[in] timeout     = timeout in us, standard is EC_TIMEOUTRET
+ * @return Workcounter or EC_NOFRAME
+ */
+int ecx_FPWR(ecx_portt *port, uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout)
+{
+   int wkc;
+   uint8 idx;
+
+   idx = ecx_getindex(port);
+   ecx_setupdatagram(port, &(port->txbuf[idx]), EC_CMD_FPWR, idx, ADP, ADO, length, data);
+   wkc = ecx_srconfirm(port, idx, timeout);
+   ecx_setbufstat(port, idx, EC_BUF_EMPTY);
+
+   return wkc;
+}
+
+/** FPWR "configured address write" primitive. Blocking.
+ *
+ * @param[in] port        = port context struct
+ * @param[in] ADP         = Address Position, slave that has address writes.
+ * @param[in] ADO         = Address Offset, slave memory address
+ * @param[in] data        = word to write to slave.
+ * @param[in] timeout     = timeout in us, standard is EC_TIMEOUTRET
+ * @return Workcounter or EC_NOFRAME
+ */
+int ecx_FPWRw(ecx_portt *port, uint16 ADP, uint16 ADO, uint16 data, int timeout)
+{
+   return ecx_FPWR(port, ADP, ADO, sizeof(data), &data, timeout);
+}
+
+/** LRW "logical memory read / write" primitive. Blocking.
+ *
+ * @param[in] port        = port context struct
+ * @param[in]     LogAdr  = Logical memory address
+ * @param[in]     length  = length of databuffer
+ * @param[in,out] data    = databuffer to write to and read from slave.
+ * @param[in]     timeout = timeout in us, standard is EC_TIMEOUTRET
+ * @return Workcounter or EC_NOFRAME
+ */
+int ecx_LRW(ecx_portt *port, uint32 LogAdr, uint16 length, void *data, int timeout)
+{
+   uint8 idx;
+   int wkc;
+
+   idx = ecx_getindex(port);
+   ecx_setupdatagram(port, &(port->txbuf[idx]), EC_CMD_LRW, idx, LO_WORD(LogAdr), HI_WORD(LogAdr), length, data);
+   wkc = ecx_srconfirm(port, idx, timeout);
+   if ((wkc > 0) && (port->rxbuf[idx][EC_CMDOFFSET] == EC_CMD_LRW))
+   {
+      memcpy(data, &(port->rxbuf[idx][EC_HEADERSIZE]), length);
+   }
+   ecx_setbufstat(port, idx, EC_BUF_EMPTY);
+
+   return wkc;
+}
+
+/** LRD "logical memory read" primitive. Blocking.
+ *
+ * @param[in] port        = port context struct
+ * @param[in]  LogAdr     = Logical memory address
+ * @param[in]  length     = length of bytes to read from slave.
+ * @param[out] data       = databuffer to read from slave.
+ * @param[in]  timeout    = timeout in us, standard is EC_TIMEOUTRET
+ * @return Workcounter or EC_NOFRAME
+ */
+int ecx_LRD(ecx_portt *port, uint32 LogAdr, uint16 length, void *data, int timeout)
+{
+   uint8 idx;
+   int wkc;
+
+   idx = ecx_getindex(port);
+   ecx_setupdatagram(port, &(port->txbuf[idx]), EC_CMD_LRD, idx, LO_WORD(LogAdr), HI_WORD(LogAdr), length, data);
+   wkc = ecx_srconfirm(port, idx, timeout);
+   if ((wkc > 0) && (port->rxbuf[idx][EC_CMDOFFSET]==EC_CMD_LRD))
+   {
+      memcpy(data, &(port->rxbuf[idx][EC_HEADERSIZE]), length);
+   }
+   ecx_setbufstat(port, idx, EC_BUF_EMPTY);
+
+   return wkc;
+}
+
+/** LWR "logical memory write" primitive. Blocking.
+ *
+ * @param[in] port        = port context struct
+ * @param[in] LogAdr      = Logical memory address
+ * @param[in] length      = length of databuffer
+ * @param[in] data        = databuffer to write to slave.
+ * @param[in] timeout     = timeout in us, standard is EC_TIMEOUTRET
+ * @return Workcounter or EC_NOFRAME
+ */
+int ecx_LWR(ecx_portt *port, uint32 LogAdr, uint16 length, void *data, int timeout)
+{
+   uint8 idx;
+   int wkc;
+
+   idx = ecx_getindex(port);
+   ecx_setupdatagram(port, &(port->txbuf[idx]), EC_CMD_LWR, idx, LO_WORD(LogAdr), HI_WORD(LogAdr), length, data);
+   wkc = ecx_srconfirm(port, idx, timeout);
+   ecx_setbufstat(port, idx, EC_BUF_EMPTY);
+
+   return wkc;
+}
+
+/** LRW "logical memory read / write" primitive plus Clock Distribution. Blocking.
+ * Frame consists of two datagrams, one LRW and one FPRMW.
+ *
+ * @param[in] port        = port context struct
+ * @param[in]     LogAdr  = Logical memory address
+ * @param[in]     length  = length of databuffer
+ * @param[in,out] data    = databuffer to write to and read from slave.
+ * @param[in]     DCrs    = Distributed Clock reference slave address.
+ * @param[out]    DCtime  = DC time read from reference slave.
+ * @param[in]     timeout = timeout in us, standard is EC_TIMEOUTRET
+ * @return Workcounter or EC_NOFRAME
+ */
+int ecx_LRWDC(ecx_portt *port, uint32 LogAdr, uint16 length, void *data, uint16 DCrs, int64 *DCtime, int timeout)
+{
+   uint16 DCtO;
+   uint8 idx;
+   int wkc;
+   uint64 DCtE;
+
+   idx = ecx_getindex(port);
+   /* LRW in first datagram */
+   ecx_setupdatagram(port, &(port->txbuf[idx]), EC_CMD_LRW, idx, LO_WORD(LogAdr), HI_WORD(LogAdr), length, data);
+   /* FPRMW in second datagram */
+   DCtE = htoell(*DCtime);
+   DCtO = ecx_adddatagram(port, &(port->txbuf[idx]), EC_CMD_FRMW, idx, FALSE, DCrs, ECT_REG_DCSYSTIME, sizeof(DCtime), &DCtE);
+   wkc = ecx_srconfirm(port, idx, timeout);
+   if ((wkc > 0) && (port->rxbuf[idx][EC_CMDOFFSET] == EC_CMD_LRW))
+   {
+      memcpy(data, &(port->rxbuf[idx][EC_HEADERSIZE]), length);
+      memcpy(&wkc, &(port->rxbuf[idx][EC_HEADERSIZE + length]), EC_WKCSIZE);
+      memcpy(&DCtE, &(port->rxbuf[idx][DCtO]), sizeof(*DCtime));
+      *DCtime = etohll(DCtE);
+   }
+   ecx_setbufstat(port, idx, EC_BUF_EMPTY);
+
+   return wkc;
+}
+
+#ifdef EC_VER1
+int ec_setupdatagram(void *frame, uint8 com, uint8 idx, uint16 ADP, uint16 ADO, uint16 length, void *data)
+{
+   return ecx_setupdatagram (&ecx_port, frame, com, idx, ADP, ADO, length, data);
+}
+
+int ec_adddatagram (void *frame, uint8 com, uint8 idx, boolean more, uint16 ADP, uint16 ADO, uint16 length, void *data)
+{
+   return ecx_adddatagram (&ecx_port, frame, com, idx, more, ADP, ADO, length, data);
+}
+
+int ec_BWR(uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout)
+{
+   return ecx_BWR (&ecx_port, ADP, ADO, length, data, timeout);
+}
+
+int ec_BRD(uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout)
+{
+   return ecx_BRD(&ecx_port, ADP, ADO, length, data, timeout);
+}
+
+int ec_APRD(uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout)
+{
+   return ecx_APRD(&ecx_port, ADP, ADO, length, data, timeout);
+}
+
+int ec_ARMW(uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout)
+{
+   return ecx_ARMW(&ecx_port, ADP, ADO, length, data, timeout);
+}
+
+int ec_FRMW(uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout)
+{
+   return ecx_FRMW(&ecx_port, ADP, ADO, length, data, timeout);
+}
+
+uint16 ec_APRDw(uint16 ADP, uint16 ADO, int timeout)
+{
+   uint16 w;
+
+   w = 0;
+   ec_APRD(ADP, ADO, sizeof(w), &w, timeout);
+
+   return w;
+}
+
+int ec_FPRD(uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout)
+{
+   return ecx_FPRD(&ecx_port, ADP, ADO, length, data, timeout);
+}
+
+uint16 ec_FPRDw(uint16 ADP, uint16 ADO, int timeout)
+{
+   uint16 w;
+
+   w = 0;
+   ec_FPRD(ADP, ADO, sizeof(w), &w, timeout);
+   return w;
+}
+
+int ec_APWR(uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout)
+{
+   return ecx_APWR(&ecx_port, ADP, ADO, length, data, timeout);
+}
+
+int ec_APWRw(uint16 ADP, uint16 ADO, uint16 data, int timeout)
+{
+   return ec_APWR(ADP, ADO, sizeof(data), &data, timeout);
+}
+
+int ec_FPWR(uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout)
+{
+   return ecx_FPWR(&ecx_port, ADP, ADO, length, data, timeout);
+}
+
+int ec_FPWRw(uint16 ADP, uint16 ADO, uint16 data, int timeout)
+{
+   return ec_FPWR(ADP, ADO, sizeof(data), &data, timeout);
+}
+
+int ec_LRW(uint32 LogAdr, uint16 length, void *data, int timeout)
+{
+   return ecx_LRW(&ecx_port, LogAdr, length, data, timeout);
+}
+
+int ec_LRD(uint32 LogAdr, uint16 length, void *data, int timeout)
+{
+   return ecx_LRD(&ecx_port, LogAdr, length, data, timeout);
+}
+
+int ec_LWR(uint32 LogAdr, uint16 length, void *data, int timeout)
+{
+   return ecx_LWR(&ecx_port, LogAdr, length, data, timeout);
+}
+
+int ec_LRWDC(uint32 LogAdr, uint16 length, void *data, uint16 DCrs, int64 *DCtime, int timeout)
+{
+   return ecx_LRWDC(&ecx_port, LogAdr, length, data, DCrs, DCtime, timeout);
+}
+#endif
diff -r 000000000000 -r 543d6784d4cc SOEM/ethercatbase.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SOEM/ethercatbase.h	Tue Jun 11 10:29:09 2019 +0000
@@ -0,0 +1,63 @@
+/*
+ * Licensed under the GNU General Public License version 2 with exceptions. See
+ * LICENSE file in the project root for full license information
+ */
+
+/** \file
+ * \brief
+ * Headerfile for ethercatbase.c
+ */
+
+#ifndef _ethercatbase_
+#define _ethercatbase_
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+int ecx_setupdatagram(ecx_portt *port, void *frame, uint8 com, uint8 idx, uint16 ADP, uint16 ADO, uint16 length, void *data);
+int ecx_adddatagram(ecx_portt *port, void *frame, uint8 com, uint8 idx, boolean more, uint16 ADP, uint16 ADO, uint16 length, void *data);
+int ecx_BWR(ecx_portt *port, uint16 ADP,uint16 ADO,uint16 length,void *data,int timeout);
+int ecx_BRD(ecx_portt *port, uint16 ADP,uint16 ADO,uint16 length,void *data,int timeout);
+int ecx_APRD(ecx_portt *port, uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout);
+int ecx_ARMW(ecx_portt *port, uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout);
+int ecx_FRMW(ecx_portt *port, uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout);
+uint16 ecx_APRDw(ecx_portt *port, uint16 ADP, uint16 ADO, int timeout);
+int ecx_FPRD(ecx_portt *port, uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout);
+uint16 ecx_FPRDw(ecx_portt *port, uint16 ADP, uint16 ADO, int timeout);
+int ecx_APWRw(ecx_portt *port, uint16 ADP, uint16 ADO, uint16 data, int timeout);
+int ecx_APWR(ecx_portt *port, uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout);
+int ecx_FPWRw(ecx_portt *port, uint16 ADP, uint16 ADO, uint16 data, int timeout);
+int ecx_FPWR(ecx_portt *port, uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout);
+int ecx_LRW(ecx_portt *port, uint32 LogAdr, uint16 length, void *data, int timeout);
+int ecx_LRD(ecx_portt *port, uint32 LogAdr, uint16 length, void *data, int timeout);
+int ecx_LWR(ecx_portt *port, uint32 LogAdr, uint16 length, void *data, int timeout);
+int ecx_LRWDC(ecx_portt *port, uint32 LogAdr, uint16 length, void *data, uint16 DCrs, int64 *DCtime, int timeout);
+
+#ifdef EC_VER1
+int ec_setupdatagram(void *frame, uint8 com, uint8 idx, uint16 ADP, uint16 ADO, uint16 length, void *data);
+int ec_adddatagram(void *frame, uint8 com, uint8 idx, boolean more, uint16 ADP, uint16 ADO, uint16 length, void *data);
+int ec_BWR(uint16 ADP,uint16 ADO,uint16 length,void *data,int timeout);
+int ec_BRD(uint16 ADP,uint16 ADO,uint16 length,void *data,int timeout);
+int ec_APRD(uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout);
+int ec_ARMW(uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout);
+int ec_FRMW(uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout);
+uint16 ec_APRDw(uint16 ADP, uint16 ADO, int timeout);
+int ec_FPRD(uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout);
+uint16 ec_FPRDw(uint16 ADP, uint16 ADO, int timeout);
+int ec_APWRw(uint16 ADP, uint16 ADO, uint16 data, int timeout);
+int ec_APWR(uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout);
+int ec_FPWRw(uint16 ADP, uint16 ADO, uint16 data, int timeout);
+int ec_FPWR(uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout);
+int ec_LRW(uint32 LogAdr, uint16 length, void *data, int timeout);
+int ec_LRD(uint32 LogAdr, uint16 length, void *data, int timeout);
+int ec_LWR(uint32 LogAdr, uint16 length, void *data, int timeout);
+int ec_LRWDC(uint32 LogAdr, uint16 length, void *data, uint16 DCrs, int64 *DCtime, int timeout);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff -r 000000000000 -r 543d6784d4cc SOEM/ethercatcoe.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SOEM/ethercatcoe.c	Tue Jun 11 10:29:09 2019 +0000
@@ -0,0 +1,1505 @@
+/*
+ * Licensed under the GNU General Public License version 2 with exceptions. See
+ * LICENSE file in the project root for full license information
+ */
+
+/** \file
+ * \brief
+ * CAN over EtherCAT (CoE) 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 "ethercatcoe.h"
+
+/** SDO structure, not to be confused with EcSDOserviceT */
+PACKED_BEGIN
+typedef struct PACKED
+{
+   ec_mbxheadert   MbxHeader;
+   uint16          CANOpen;
+   uint8           Command;
+   uint16          Index;
+   uint8           SubIndex;
+   union
+   {
+      uint8   bdata[0x200]; /* variants for easy data access */
+      uint16  wdata[0x100];
+      uint32  ldata[0x80];
+   };
+} ec_SDOt;
+PACKED_END
+
+/** SDO service structure */
+PACKED_BEGIN
+typedef struct PACKED
+{
+   ec_mbxheadert   MbxHeader;
+   uint16          CANOpen;
+   uint8           Opcode;
+   uint8           Reserved;
+   uint16          Fragments;
+   union
+   {
+      uint8   bdata[0x200]; /* variants for easy data access */
+      uint16  wdata[0x100];
+      uint32  ldata[0x80];
+   };
+} ec_SDOservicet;
+PACKED_END
+
+/** Report SDO error.
+ *
+ * @param[in]  context    = context struct
+ * @param[in]  Slave      = Slave number
+ * @param[in]  Index      = Index that generated error
+ * @param[in]  SubIdx     = Subindex that generated error
+ * @param[in]  AbortCode  = Abortcode, see EtherCAT documentation for list
+ */
+void ecx_SDOerror(ecx_contextt *context, uint16 Slave, uint16 Index, uint8 SubIdx, int32 AbortCode)
+{
+   ec_errort Ec;
+
+   memset(&Ec, 0, sizeof(Ec));
+   Ec.Time = osal_current_time();
+   Ec.Slave = Slave;
+   Ec.Index = Index;
+   Ec.SubIdx = SubIdx;
+   *(context->ecaterror) = TRUE;
+   Ec.Etype = EC_ERR_TYPE_SDO_ERROR;
+   Ec.AbortCode = AbortCode;
+   ecx_pusherror(context, &Ec);
+}
+
+/** Report SDO info error
+ *
+ * @param[in]  context    = context struct
+ * @param[in]  Slave      = Slave number
+ * @param[in]  Index      = Index that generated error
+ * @param[in]  SubIdx     = Subindex that generated error
+ * @param[in]  AbortCode  = Abortcode, see EtherCAT documentation for list
+ */
+static void ecx_SDOinfoerror(ecx_contextt *context, uint16 Slave, uint16 Index, uint8 SubIdx, int32 AbortCode)
+{
+   ec_errort Ec;
+
+   memset(&Ec, 0, sizeof(Ec));
+   Ec.Slave = Slave;
+   Ec.Index = Index;
+   Ec.SubIdx = SubIdx;
+   *(context->ecaterror) = TRUE;
+   Ec.Etype = EC_ERR_TYPE_SDOINFO_ERROR;
+   Ec.AbortCode = AbortCode;
+   ecx_pusherror(context, &Ec);
+}
+
+/** CoE SDO read, blocking. Single subindex or Complete Access.
+ *
+ * Only a "normal" upload request is issued. If the requested parameter is <= 4bytes
+ * then a "expedited" response is returned, otherwise a "normal" response. If a "normal"
+ * response is larger than the mailbox size then the response is segmented. The function
+ * will combine all segments and copy them to the parameter buffer.
+ *
+ * @param[in]  context    = context struct
+ * @param[in]  slave      = Slave number
+ * @param[in]  index      = Index to read
+ * @param[in]  subindex   = Subindex to read, must be 0 or 1 if CA is used.
+ * @param[in]  CA         = FALSE = single subindex. TRUE = Complete Access, all subindexes read.
+ * @param[in,out] psize   = Size in bytes of parameter buffer, returns bytes read from SDO.
+ * @param[out] p          = Pointer to parameter buffer
+ * @param[in]  timeout    = Timeout in us, standard is EC_TIMEOUTRXM
+ * @return Workcounter from last slave response
+ */
+int ecx_SDOread(ecx_contextt *context, uint16 slave, uint16 index, uint8 subindex,
+               boolean CA, int *psize, void *p, int timeout)
+{
+   ec_SDOt *SDOp, *aSDOp;
+   uint16 bytesize, Framedatasize;
+   int wkc;
+   int32 SDOlen;
+   uint8 *bp;
+   uint8 *hp;
+   ec_mbxbuft MbxIn, MbxOut;
+   uint8 cnt, toggle;
+   boolean NotLast;
+
+   ec_clearmbx(&MbxIn);
+   /* Empty slave out mailbox if something is in. Timeout set to 0 */
+   wkc = ecx_mbxreceive(context, slave, (ec_mbxbuft *)&MbxIn, 0);
+   ec_clearmbx(&MbxOut);
+   aSDOp = (ec_SDOt *)&MbxIn;
+   SDOp = (ec_SDOt *)&MbxOut;
+   SDOp->MbxHeader.length = htoes(0x000a);
+   SDOp->MbxHeader.address = htoes(0x0000);
+   SDOp->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;
+   SDOp->MbxHeader.mbxtype = ECT_MBXT_COE + (cnt << 4); /* CoE */
+   SDOp->CANOpen = htoes(0x000 + (ECT_COES_SDOREQ << 12)); /* number 9bits service upper 4 bits (SDO request) */
+   if (CA)
+   {
+      SDOp->Command = ECT_SDO_UP_REQ_CA; /* upload request complete access */
+   }
+   else
+   {
+      SDOp->Command = ECT_SDO_UP_REQ; /* upload request normal */
+   }
+   SDOp->Index = htoes(index);
+   if (CA && (subindex > 1))
+   {
+      subindex = 1;
+   }
+   SDOp->SubIndex = subindex;
+   SDOp->ldata[0] = 0;
+   /* send CoE request to slave */
+   wkc = ecx_mbxsend(context, slave, (ec_mbxbuft *)&MbxOut, EC_TIMEOUTTXM);
+   if (wkc > 0) /* succeeded to place mailbox in slave ? */
+   {
+      /* 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 CoE, SDO response and the correct index */
+         if (((aSDOp->MbxHeader.mbxtype & 0x0f) == ECT_MBXT_COE) &&
+             ((etohs(aSDOp->CANOpen) >> 12) == ECT_COES_SDORES) &&
+              (aSDOp->Index == SDOp->Index))
+         {
+            if ((aSDOp->Command & 0x02) > 0)
+            {
+               /* expedited frame response */
+               bytesize = 4 - ((aSDOp->Command >> 2) & 0x03);
+               if (*psize >= bytesize) /* parameter buffer big enough ? */
+               {
+                  /* copy parameter in parameter buffer */
+                  memcpy(p, &aSDOp->ldata[0], bytesize);
+                  /* return the real parameter size */
+                  *psize = bytesize;
+               }
+               else
+               {
+                  wkc = 0;
+                  ecx_packeterror(context, slave, index, subindex, 3); /*  data container too small for type */
+               }
+            }
+            else
+            { /* normal frame response */
+               SDOlen = etohl(aSDOp->ldata[0]);
+               /* Does parameter fit in parameter buffer ? */
+               if (SDOlen <= *psize)
+               {
+                  bp = p;
+                  hp = p;
+                  /* calculate mailbox transfer size */
+                  Framedatasize = (etohs(aSDOp->MbxHeader.length) - 10);
+                  if (Framedatasize < SDOlen) /* transfer in segments? */
+                  {
+                     /* copy parameter data in parameter buffer */
+                     memcpy(hp, &aSDOp->ldata[1], Framedatasize);
+                     /* increment buffer pointer */
+                     hp += Framedatasize;
+                     *psize = Framedatasize;
+                     NotLast = TRUE;
+                     toggle= 0x00;
+                     while (NotLast) /* segmented transfer */
+                     {
+                        SDOp = (ec_SDOt *)&MbxOut;
+                        SDOp->MbxHeader.length = htoes(0x000a);
+                        SDOp->MbxHeader.address = htoes(0x0000);
+                        SDOp->MbxHeader.priority = 0x00;
+                        cnt = ec_nextmbxcnt(context->slavelist[slave].mbx_cnt);
+                        context->slavelist[slave].mbx_cnt = cnt;
+                        SDOp->MbxHeader.mbxtype = ECT_MBXT_COE + (cnt << 4); /* CoE */
+                        SDOp->CANOpen = htoes(0x000 + (ECT_COES_SDOREQ << 12)); /* number 9bits service upper 4 bits (SDO request) */
+                        SDOp->Command = ECT_SDO_SEG_UP_REQ + toggle; /* segment upload request */
+                        SDOp->Index = htoes(index);
+                        SDOp->SubIndex = subindex;
+                        SDOp->ldata[0] = 0;
+                        /* send segmented upload request to slave */
+                        wkc = ecx_mbxsend(context, slave, (ec_mbxbuft *)&MbxOut, EC_TIMEOUTTXM);
+                        /* is mailbox transferred to slave ? */
+                        if (wkc > 0)
+                        {
+                           ec_clearmbx(&MbxIn);
+                           /* read slave response */
+                           wkc = ecx_mbxreceive(context, slave, (ec_mbxbuft *)&MbxIn, timeout);
+                           /* has slave responded ? */
+                           if (wkc > 0)
+                           {
+                              /* slave response should be CoE, SDO response */
+                              if ((((aSDOp->MbxHeader.mbxtype & 0x0f) == ECT_MBXT_COE) &&
+                                   ((etohs(aSDOp->CANOpen) >> 12) == ECT_COES_SDORES) &&
+                                   ((aSDOp->Command & 0xe0) == 0x00)))
+                                        {
+                                 /* calculate mailbox transfer size */
+                                 Framedatasize = etohs(aSDOp->MbxHeader.length) - 3;
+                                 if ((aSDOp->Command & 0x01) > 0)
+                                 { /* last segment */
+                                    NotLast = FALSE;
+                                    if (Framedatasize == 7)
+                                       /* subtract unused bytes from frame */
+                                       Framedatasize = Framedatasize - ((aSDOp->Command & 0x0e) >> 1);
+                                    /* copy to parameter buffer */
+                                    memcpy(hp, &(aSDOp->Index), Framedatasize);
+                                 }
+                                 else /* segments follow */
+                                 {
+                                    /* copy to parameter buffer */
+                                    memcpy(hp, &(aSDOp->Index), Framedatasize);
+                                    /* increment buffer pointer */
+                                    hp += Framedatasize;
+                                 }
+                                 /* update parameter size */
+                                 *psize += Framedatasize;
+                              }
+                              /* unexpected frame returned from slave */
+                              else
+                              {
+                                 NotLast = FALSE;
+                                 if ((aSDOp->Command) == ECT_SDO_ABORT) /* SDO abort frame received */
+                                    ecx_SDOerror(context, slave, index, subindex, etohl(aSDOp->ldata[0]));
+                                 else
+                                    ecx_packeterror(context, slave, index, subindex, 1); /* Unexpected frame returned */
+                                 wkc = 0;
+                              }
+                           }
+                        }
+                        toggle = toggle ^ 0x10; /* toggle bit for segment request */
+                     }
+                  }
+                  /* non segmented transfer */
+                  else
+                  {
+                     /* copy to parameter buffer */
+                     memcpy(bp, &aSDOp->ldata[1], SDOlen);
+                     *psize = SDOlen;
+                  }
+               }
+               /* parameter buffer too small */
+               else
+               {
+                  wkc = 0;
+                  ecx_packeterror(context, slave, index, subindex, 3); /*  data container too small for type */
+               }
+            }
+         }
+         /* other slave response */
+         else
+         {
+            if ((aSDOp->Command) == ECT_SDO_ABORT) /* SDO abort frame received */
+            {
+               ecx_SDOerror(context, slave, index, subindex, etohl(aSDOp->ldata[0]));
+            }
+            else
+            {
+               ecx_packeterror(context, slave, index, subindex, 1); /* Unexpected frame returned */
+            }
+            wkc = 0;
+         }
+      }
+   }
+   return wkc;
+}
+
+/** CoE SDO write, blocking. Single subindex or Complete Access.
+ *
+ * A "normal" download request is issued, unless we have
+ * small data, then a "expedited" transfer is used. If the parameter is larger than
+ * the mailbox size then the download is segmented. The function will split the
+ * parameter data in segments and send them to the slave one by one.
+ *
+ * @param[in]  context    = context struct
+ * @param[in]  Slave      = Slave number
+ * @param[in]  Index      = Index to write
+ * @param[in]  SubIndex   = Subindex to write, must be 0 or 1 if CA is used.
+ * @param[in]  CA         = FALSE = single subindex. TRUE = Complete Access, all subindexes written.
+ * @param[in]  psize      = Size in bytes of parameter buffer.
+ * @param[out] p          = Pointer to parameter buffer
+ * @param[in]  Timeout    = Timeout in us, standard is EC_TIMEOUTRXM
+ * @return Workcounter from last slave response
+ */
+int ecx_SDOwrite(ecx_contextt *context, uint16 Slave, uint16 Index, uint8 SubIndex,
+                boolean CA, int psize, void *p, int Timeout)
+{
+   ec_SDOt *SDOp, *aSDOp;
+   int wkc, maxdata;
+   ec_mbxbuft MbxIn, MbxOut;
+   uint8 cnt, toggle;
+   uint16 framedatasize;
+   boolean  NotLast;
+   uint8 *hp;
+
+   ec_clearmbx(&MbxIn);
+   /* Empty slave out mailbox if something is in. Timeout set to 0 */
+   wkc = ecx_mbxreceive(context, Slave, (ec_mbxbuft *)&MbxIn, 0);
+   ec_clearmbx(&MbxOut);
+   aSDOp = (ec_SDOt *)&MbxIn;
+   SDOp = (ec_SDOt *)&MbxOut;
+   maxdata = context->slavelist[Slave].mbx_l - 0x10; /* data section=mailbox size - 6 mbx - 2 CoE - 8 sdo req */
+   /* if small data use expedited transfer */
+   if ((psize <= 4) && !CA)
+   {
+      SDOp->MbxHeader.length = htoes(0x000a);
+      SDOp->MbxHeader.address = htoes(0x0000);
+      SDOp->MbxHeader.priority = 0x00;
+      /* get new mailbox counter, used for session handle */
+      cnt = ec_nextmbxcnt(context->slavelist[Slave].mbx_cnt);
+      context->slavelist[Slave].mbx_cnt = cnt;
+      SDOp->MbxHeader.mbxtype = ECT_MBXT_COE + (cnt << 4); /* CoE */
+      SDOp->CANOpen = htoes(0x000 + (ECT_COES_SDOREQ << 12)); /* number 9bits service upper 4 bits */
+      SDOp->Command = ECT_SDO_DOWN_EXP | (((4 - psize) << 2) & 0x0c); /* expedited SDO download transfer */
+      SDOp->Index = htoes(Index);
+      SDOp->SubIndex = SubIndex;
+      hp = p;
+      /* copy parameter data to mailbox */
+      memcpy(&SDOp->ldata[0], hp, psize);
+      /* send mailbox SDO download request to slave */
+      wkc = ecx_mbxsend(context, Slave, (ec_mbxbuft *)&MbxOut, EC_TIMEOUTTXM);
+      if (wkc > 0)
+      {
+         ec_clearmbx(&MbxIn);
+         /* read slave response */
+         wkc = ecx_mbxreceive(context, Slave, (ec_mbxbuft *)&MbxIn, Timeout);
+         if (wkc > 0)
+         {
+            /* response should be CoE, SDO response, correct index and subindex */
+            if (((aSDOp->MbxHeader.mbxtype & 0x0f) == ECT_MBXT_COE) &&
+                ((etohs(aSDOp->CANOpen) >> 12) == ECT_COES_SDORES) &&
+                 (aSDOp->Index == SDOp->Index) &&
+                 (aSDOp->SubIndex == SDOp->SubIndex))
+            {
+                 /* all OK */
+            }
+            /* unexpected response from slave */
+            else
+            {
+               if (aSDOp->Command == ECT_SDO_ABORT) /* SDO abort frame received */
+               {
+                  ecx_SDOerror(context, Slave, Index, SubIndex, etohl(aSDOp->ldata[0]));
+               }
+               else
+               {
+                  ecx_packeterror(context, Slave, Index, SubIndex, 1); /* Unexpected frame returned */
+               }
+               wkc = 0;
+            }
+         }
+      }
+   }
+   else
+   {
+      framedatasize = psize;
+      NotLast = FALSE;
+      if (framedatasize > maxdata)
+      {
+         framedatasize = maxdata;  /*  segmented transfer needed  */
+         NotLast = TRUE;
+      }
+      SDOp->MbxHeader.length = htoes(0x0a + framedatasize);
+      SDOp->MbxHeader.address = htoes(0x0000);
+      SDOp->MbxHeader.priority = 0x00;
+      /* get new mailbox counter, used for session handle */
+      cnt = ec_nextmbxcnt(context->slavelist[Slave].mbx_cnt);
+      context->slavelist[Slave].mbx_cnt = cnt;
+      SDOp->MbxHeader.mbxtype = ECT_MBXT_COE + (cnt << 4); /* CoE */
+      SDOp->CANOpen = htoes(0x000 + (ECT_COES_SDOREQ << 12)); /* number 9bits service upper 4 bits */
+      if (CA)
+      {
+         SDOp->Command = ECT_SDO_DOWN_INIT_CA; /* Complete Access, normal SDO init download transfer */
+      }
+      else
+      {
+         SDOp->Command = ECT_SDO_DOWN_INIT; /* normal SDO init download transfer */
+      }
+      SDOp->Index = htoes(Index);
+      SDOp->SubIndex = SubIndex;
+      if (CA && (SubIndex > 1))
+      {
+         SDOp->SubIndex = 1;
+      }
+      SDOp->ldata[0] = htoel(psize);
+      hp = p;
+      /* copy parameter data to mailbox */
+      memcpy(&SDOp->ldata[1], hp, framedatasize);
+      hp += framedatasize;
+      psize -= framedatasize;
+      /* send mailbox SDO download request to slave */
+      wkc = ecx_mbxsend(context, Slave, (ec_mbxbuft *)&MbxOut, EC_TIMEOUTTXM);
+      if (wkc > 0)
+      {
+         ec_clearmbx(&MbxIn);
+         /* read slave response */
+         wkc = ecx_mbxreceive(context, Slave, (ec_mbxbuft *)&MbxIn, Timeout);
+         if (wkc > 0)
+         {
+            /* response should be CoE, SDO response, correct index and subindex */
+            if (((aSDOp->MbxHeader.mbxtype & 0x0f) == ECT_MBXT_COE) &&
+                ((etohs(aSDOp->CANOpen) >> 12) == ECT_COES_SDORES) &&
+                 (aSDOp->Index == SDOp->Index) &&
+                 (aSDOp->SubIndex == SDOp->SubIndex))
+            {
+               /* all ok */
+               maxdata += 7;
+               toggle = 0;
+               /* repeat while segments left */
+               while (NotLast)
+               {
+                  SDOp = (ec_SDOt *)&MbxOut;
+                  framedatasize = psize;
+                  NotLast = FALSE;
+                  SDOp->Command = 0x01; /* last segment */
+                  if (framedatasize > maxdata)
+                  {
+                     framedatasize = maxdata;  /*  more segments needed  */
+                     NotLast = TRUE;
+                     SDOp->Command = 0x00; /* segments follow */
+                  }
+                  if (!NotLast && (framedatasize < 7))
+                  {
+                     SDOp->MbxHeader.length = htoes(0x0a); /* minimum size */
+                     SDOp->Command = 0x01 + ((7 - framedatasize) << 1); /* last segment reduced octets */
+                  }
+                  else
+                  {
+                     SDOp->MbxHeader.length = htoes(framedatasize + 3); /* data + 2 CoE + 1 SDO */
+                  }
+                  SDOp->MbxHeader.address = htoes(0x0000);
+                  SDOp->MbxHeader.priority = 0x00;
+                  /* get new mailbox counter value */
+                  cnt = ec_nextmbxcnt(context->slavelist[Slave].mbx_cnt);
+                  context->slavelist[Slave].mbx_cnt = cnt;
+                  SDOp->MbxHeader.mbxtype = ECT_MBXT_COE + (cnt << 4); /* CoE */
+                  SDOp->CANOpen = htoes(0x000 + (ECT_COES_SDOREQ << 12)); /* number 9bits service upper 4 bits (SDO request) */
+                  SDOp->Command = SDOp->Command + toggle; /* add toggle bit to command byte */
+                  /* copy parameter data to mailbox */
+                  memcpy(&SDOp->Index, hp, framedatasize);
+                  /* update parameter buffer pointer */
+                  hp += framedatasize;
+                  psize -= framedatasize;
+                  /* send SDO download request */
+                  wkc = ecx_mbxsend(context, Slave, (ec_mbxbuft *)&MbxOut, EC_TIMEOUTTXM);
+                  if (wkc > 0)
+                  {
+                     ec_clearmbx(&MbxIn);
+                     /* read slave response */
+                     wkc = ecx_mbxreceive(context, Slave, (ec_mbxbuft *)&MbxIn, Timeout);
+                     if (wkc > 0)
+                     {
+                        if (((aSDOp->MbxHeader.mbxtype & 0x0f) == ECT_MBXT_COE) &&
+                            ((etohs(aSDOp->CANOpen) >> 12) == ECT_COES_SDORES) &&
+                            ((aSDOp->Command & 0xe0) == 0x20))
+                        {
+                                   /* all OK, nothing to do */
+                        }
+                        else
+                        {
+                           if (aSDOp->Command == ECT_SDO_ABORT) /* SDO abort frame received */
+                           {
+                              ecx_SDOerror(context, Slave, Index, SubIndex, etohl(aSDOp->ldata[0]));
+                           }
+                           else
+                           {
+                              ecx_packeterror(context, Slave, Index, SubIndex, 1); /* Unexpected frame returned */
+                           }
+                           wkc = 0;
+                           NotLast = FALSE;
+                        }
+                     }
+                  }
+                  toggle = toggle ^ 0x10; /* toggle bit for segment request */
+               }
+            }
+            /* unexpected response from slave */
+            else
+            {
+               if (aSDOp->Command == ECT_SDO_ABORT) /* SDO abort frame received */
+               {
+                  ecx_SDOerror(context, Slave, Index, SubIndex, etohl(aSDOp->ldata[0]));
+               }
+               else
+               {
+                  ecx_packeterror(context, Slave, Index, SubIndex, 1); /* Unexpected frame returned */
+               }
+               wkc = 0;
+            }
+         }
+      }
+   }
+
+   return wkc;
+}
+
+/** CoE RxPDO write, blocking.
+ *
+ * A RxPDO download request is issued.
+ *
+ * @param[in]  context       = context struct
+ * @param[in]  Slave         = Slave number
+ * @param[in]  RxPDOnumber   = Related RxPDO number
+ * @param[in]  psize         = Size in bytes of PDO buffer.
+ * @param[out] p             = Pointer to PDO buffer
+ * @return Workcounter from last slave response
+ */
+int ecx_RxPDO(ecx_contextt *context, uint16 Slave, uint16 RxPDOnumber, int psize, void *p)
+{
+   ec_SDOt *SDOp;
+   int wkc, maxdata;
+   ec_mbxbuft MbxIn, MbxOut;
+   uint8 cnt;
+   uint16 framedatasize;
+
+   ec_clearmbx(&MbxIn);
+   /* Empty slave out mailbox if something is in. Timeout set to 0 */
+   wkc = ecx_mbxreceive(context, Slave, (ec_mbxbuft *)&MbxIn, 0);
+   ec_clearmbx(&MbxOut);
+   SDOp = (ec_SDOt *)&MbxOut;
+   maxdata = context->slavelist[Slave].mbx_l - 0x08; /* data section=mailbox size - 6 mbx - 2 CoE */
+   framedatasize = psize;
+   if (framedatasize > maxdata)
+   {
+      framedatasize = maxdata;  /*  limit transfer */
+   }
+   SDOp->MbxHeader.length = htoes(0x02 + framedatasize);
+   SDOp->MbxHeader.address = htoes(0x0000);
+   SDOp->MbxHeader.priority = 0x00;
+   /* get new mailbox counter, used for session handle */
+   cnt = ec_nextmbxcnt(context->slavelist[Slave].mbx_cnt);
+   context->slavelist[Slave].mbx_cnt = cnt;
+   SDOp->MbxHeader.mbxtype = ECT_MBXT_COE + (cnt << 4); /* CoE */
+   SDOp->CANOpen = htoes((RxPDOnumber & 0x01ff) + (ECT_COES_RXPDO << 12)); /* number 9bits service upper 4 bits */
+   /* copy PDO data to mailbox */
+   memcpy(&SDOp->Command, p, framedatasize);
+   /* send mailbox RxPDO request to slave */
+   wkc = ecx_mbxsend(context, Slave, (ec_mbxbuft *)&MbxOut, EC_TIMEOUTTXM);
+
+   return wkc;
+}
+
+/** CoE TxPDO read remote request, blocking.
+ *
+ * A RxPDO download request is issued.
+ *
+ * @param[in]  context       = context struct
+ * @param[in]  slave         = Slave number
+ * @param[in]  TxPDOnumber   = Related TxPDO number
+ * @param[in,out] psize      = Size in bytes of PDO buffer, returns bytes read from PDO.
+ * @param[out] p             = Pointer to PDO buffer
+ * @param[in]  timeout       = Timeout in us, standard is EC_TIMEOUTRXM
+ * @return Workcounter from last slave response
+ */
+int ecx_TxPDO(ecx_contextt *context, uint16 slave, uint16 TxPDOnumber , int *psize, void *p, int timeout)
+{
+   ec_SDOt *SDOp, *aSDOp;
+   int wkc;
+   ec_mbxbuft MbxIn, MbxOut;
+   uint8 cnt;
+   uint16 framedatasize;
+
+   ec_clearmbx(&MbxIn);
+   /* Empty slave out mailbox if something is in. Timeout set to 0 */
+   wkc = ecx_mbxreceive(context, slave, (ec_mbxbuft *)&MbxIn, 0);
+   ec_clearmbx(&MbxOut);
+   aSDOp = (ec_SDOt *)&MbxIn;
+   SDOp = (ec_SDOt *)&MbxOut;
+   SDOp->MbxHeader.length = htoes(0x02);
+   SDOp->MbxHeader.address = htoes(0x0000);
+   SDOp->MbxHeader.priority = 0x00;
+   /* get new mailbox counter, used for session handle */
+   cnt = ec_nextmbxcnt(context->slavelist[slave].mbx_cnt);
+   context->slavelist[slave].mbx_cnt = cnt;
+   SDOp->MbxHeader.mbxtype = ECT_MBXT_COE + (cnt << 4); /* CoE */
+   SDOp->CANOpen = htoes((TxPDOnumber & 0x01ff) + (ECT_COES_TXPDO_RR << 12)); /* number 9bits service upper 4 bits */
+   wkc = ecx_mbxsend(context, slave, (ec_mbxbuft *)&MbxOut, EC_TIMEOUTTXM);
+   if (wkc > 0)
+   {
+      /* 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 CoE, TxPDO */
+         if (((aSDOp->MbxHeader.mbxtype & 0x0f) == ECT_MBXT_COE) &&
+             ((etohs(aSDOp->CANOpen) >> 12) == ECT_COES_TXPDO))
+         {
+            /* TxPDO response */
+            framedatasize = (aSDOp->MbxHeader.length - 2);
+            if (*psize >= framedatasize) /* parameter buffer big enough ? */
+            {
+               /* copy parameter in parameter buffer */
+               memcpy(p, &aSDOp->Command, framedatasize);
+               /* return the real parameter size */
+               *psize = framedatasize;
+            }
+            /* parameter buffer too small */
+            else
+            {
+               wkc = 0;
+               ecx_packeterror(context, slave, 0, 0, 3); /*  data container too small for type */
+            }
+         }
+         /* other slave response */
+         else
+         {
+            if ((aSDOp->Command) == ECT_SDO_ABORT) /* SDO abort frame received */
+            {
+               ecx_SDOerror(context, slave, 0, 0, etohl(aSDOp->ldata[0]));
+            }
+            else
+            {
+               ecx_packeterror(context, slave, 0, 0, 1); /* Unexpected frame returned */
+            }
+            wkc = 0;
+         }
+      }
+   }
+
+   return wkc;
+}
+
+/** Read PDO assign structure
+ * @param[in]  context       = context struct
+ * @param[in]  Slave         = Slave number
+ * @param[in]  PDOassign     = PDO assign object
+ * @return total bitlength of PDO assign
+ */
+int ecx_readPDOassign(ecx_contextt *context, uint16 Slave, uint16 PDOassign)
+{
+   uint16 idxloop, nidx, subidxloop, rdat, idx, subidx;
+   uint8 subcnt;
+   int wkc, bsize = 0, rdl;
+   int32 rdat2;
+
+   rdl = sizeof(rdat); rdat = 0;
+   /* read PDO assign subindex 0 ( = number of PDO's) */
+   wkc = ecx_SDOread(context, Slave, PDOassign, 0x00, FALSE, &rdl, &rdat, EC_TIMEOUTRXM);
+   rdat = etohs(rdat);
+   /* positive result from slave ? */
+   if ((wkc > 0) && (rdat > 0))
+   {
+      /* number of available sub indexes */
+      nidx = rdat;
+      bsize = 0;
+      /* read all PDO's */
+      for (idxloop = 1; idxloop <= nidx; idxloop++)
+      {
+         rdl = sizeof(rdat); rdat = 0;
+         /* read PDO assign */
+         wkc = ecx_SDOread(context, Slave, PDOassign, (uint8)idxloop, FALSE, &rdl, &rdat, EC_TIMEOUTRXM);
+         /* result is index of PDO */
+         idx = etohs(rdat);
+         if (idx > 0)
+         {
+            rdl = sizeof(subcnt); subcnt = 0;
+            /* read number of subindexes of PDO */
+            wkc = ecx_SDOread(context, Slave,idx, 0x00, FALSE, &rdl, &subcnt, EC_TIMEOUTRXM);
+            subidx = subcnt;
+            /* for each subindex */
+            for (subidxloop = 1; subidxloop <= subidx; subidxloop++)
+            {
+               rdl = sizeof(rdat2); rdat2 = 0;
+               /* read SDO that is mapped in PDO */
+               wkc = ecx_SDOread(context, Slave, idx, (uint8)subidxloop, FALSE, &rdl, &rdat2, EC_TIMEOUTRXM);
+               rdat2 = etohl(rdat2);
+               /* extract bitlength of SDO */
+               if (LO_BYTE(rdat2) < 0xff)
+               {
+                  bsize += LO_BYTE(rdat2);
+               }
+               else
+               {
+                  rdl = sizeof(rdat); rdat = htoes(0xff);
+                  /* read Object Entry in Object database */
+//                  wkc = ec_readOEsingle(idx, (uint8)SubCount, pODlist, pOElist);
+                  bsize += etohs(rdat);
+               }
+            }
+         }
+      }
+   }
+   /* return total found bitlength (PDO) */
+   return bsize;
+}
+
+/** Read PDO assign structure in Complete Access mode
+ * @param[in]  context       = context struct
+ * @param[in]  Slave         = Slave number
+ * @param[in]  Thread_n      = Calling thread index
+ * @param[in]  PDOassign     = PDO assign object
+ * @return total bitlength of PDO assign
+ */
+int ecx_readPDOassignCA(ecx_contextt *context, uint16 Slave, int Thread_n,
+      uint16 PDOassign)
+{
+   uint16 idxloop, nidx, subidxloop, idx, subidx;
+   int wkc, bsize = 0, rdl;
+
+   /* find maximum size of PDOassign buffer */
+   rdl = sizeof(ec_PDOassignt);
+   context->PDOassign[Thread_n].n=0;
+   /* read rxPDOassign in CA mode, all subindexes are read in one struct */
+   wkc = ecx_SDOread(context, Slave, PDOassign, 0x00, TRUE, &rdl,
+         &(context->PDOassign[Thread_n]), EC_TIMEOUTRXM);
+   /* positive result from slave ? */
+   if ((wkc > 0) && (context->PDOassign[Thread_n].n > 0))
+   {
+      nidx = context->PDOassign[Thread_n].n;
+      bsize = 0;
+      /* for each PDO do */
+      for (idxloop = 1; idxloop <= nidx; idxloop++)
+      {
+         /* get index from PDOassign struct */
+         idx = etohs(context->PDOassign[Thread_n].index[idxloop - 1]);
+         if (idx > 0)
+         {
+            rdl = sizeof(ec_PDOdesct); context->PDOdesc[Thread_n].n = 0;
+            /* read SDO's that are mapped in PDO, CA mode */
+            wkc = ecx_SDOread(context, Slave,idx, 0x00, TRUE, &rdl,
+                  &(context->PDOdesc[Thread_n]), EC_TIMEOUTRXM);
+            subidx = context->PDOdesc[Thread_n].n;
+            /* extract all bitlengths of SDO's */
+            for (subidxloop = 1; subidxloop <= subidx; subidxloop++)
+            {
+               bsize += LO_BYTE(etohl(context->PDOdesc[Thread_n].PDO[subidxloop -1]));
+            }
+         }
+      }
+   }
+
+   /* return total found bitlength (PDO) */
+   return bsize;
+}
+
+/** CoE read PDO mapping.
+ *
+ * CANopen has standard indexes defined for PDO mapping. This function
+ * tries to read them and collect a full input and output mapping size
+ * of designated slave.
+ *
+ * Principal structure in slave:\n
+ * 1C00:00 is number of SM defined\n
+ * 1C00:01 SM0 type -> 1C10\n
+ * 1C00:02 SM1 type -> 1C11\n
+ * 1C00:03 SM2 type -> 1C12\n
+ * 1C00:04 SM3 type -> 1C13\n
+ * Type 0 = unused, 1 = mailbox in, 2 = mailbox out,
+ * 3 = outputs (RxPDO), 4 = inputs (TxPDO).
+ *
+ * 1C12:00 is number of PDO's defined for SM2\n
+ * 1C12:01 PDO assign SDO #1 -> f.e. 1A00\n
+ * 1C12:02 PDO assign SDO #2 -> f.e. 1A04\
+ *
+ * 1A00:00 is number of object defined for this PDO\n
+ * 1A00:01 object mapping #1, f.e. 60100710 (SDO 6010 SI 07 bitlength 0x10)
+ *
+ * @param[in]  context = context struct
+ * @param[in]  Slave   = Slave number
+ * @param[out] Osize   = Size in bits of output mapping (rxPDO) found
+ * @param[out] Isize   = Size in bits of input mapping (txPDO) found
+ * @return >0 if mapping successful.
+ */
+int ecx_readPDOmap(ecx_contextt *context, uint16 Slave, int *Osize, int *Isize)
+{
+   int wkc, rdl;
+   int retVal = 0;
+   uint8 nSM, iSM, tSM;
+   int Tsize;
+   uint8 SMt_bug_add;
+
+   *Isize = 0;
+   *Osize = 0;
+   SMt_bug_add = 0;
+   rdl = sizeof(nSM); nSM = 0;
+   /* read SyncManager Communication Type object count */
+   wkc = ecx_SDOread(context, Slave, ECT_SDO_SMCOMMTYPE, 0x00, FALSE, &rdl, &nSM, EC_TIMEOUTRXM);
+   /* positive result from slave ? */
+   if ((wkc > 0) && (nSM > 2))
+   {
+      /* limit to maximum number of SM defined, if true the slave can't be configured */
+      if (nSM > EC_MAXSM)
+         nSM = EC_MAXSM;
+      /* iterate for every SM type defined */
+      for (iSM = 2 ; iSM < nSM ; iSM++)
+      {
+         rdl = sizeof(tSM); tSM = 0;
+         /* read SyncManager Communication Type */
+         wkc = ecx_SDOread(context, Slave, ECT_SDO_SMCOMMTYPE, iSM + 1, FALSE, &rdl, &tSM, EC_TIMEOUTRXM);
+         if (wkc > 0)
+         {
+// start slave bug prevention code, remove if possible
+            if((iSM == 2) && (tSM == 2)) // SM2 has type 2 == mailbox out, this is a bug in the slave!
+            {
+               SMt_bug_add = 1; // try to correct, this works if the types are 0 1 2 3 and should be 1 2 3 4
+            }
+            if(tSM)
+            {
+               tSM += SMt_bug_add; // only add if SMt > 0
+            }
+            if((iSM == 2) && (tSM == 0)) // SM2 has type 0, this is a bug in the slave!
+            {
+               tSM = 3;
+            }
+            if((iSM == 3) && (tSM == 0)) // SM3 has type 0, this is a bug in the slave!
+            {
+               tSM = 4;
+            }
+// end slave bug prevention code
+
+            context->slavelist[Slave].SMtype[iSM] = tSM;
+            /* check if SM is unused -> clear enable flag */
+            if (tSM == 0)
+            {
+               context->slavelist[Slave].SM[iSM].SMflags =
+                  htoel( etohl(context->slavelist[Slave].SM[iSM].SMflags) & EC_SMENABLEMASK);
+            }
+            if ((tSM == 3) || (tSM == 4))
+            {
+               /* read the assign PDO */
+               Tsize = ecx_readPDOassign(context, Slave, ECT_SDO_PDOASSIGN + iSM );
+               /* if a mapping is found */
+               if (Tsize)
+               {
+                  context->slavelist[Slave].SM[iSM].SMlength = htoes((Tsize + 7) / 8);
+                  if (tSM == 3)
+                  {
+                     /* we are doing outputs */
+                     *Osize += Tsize;
+                  }
+                  else
+                  {
+                     /* we are doing inputs */
+                     *Isize += Tsize;
+                  }
+               }
+            }
+         }
+      }
+   }
+
+   /* found some I/O bits ? */
+   if ((*Isize > 0) || (*Osize > 0))
+   {
+      retVal = 1;
+   }
+
+   return retVal;
+}
+
+/** CoE read PDO mapping in Complete Access mode (CA).
+ *
+ * CANopen has standard indexes defined for PDO mapping. This function
+ * tries to read them and collect a full input and output mapping size
+ * of designated slave. Slave has to support CA, otherwise use ec_readPDOmap().
+ *
+ * @param[in]  context  = context struct
+ * @param[in]  Slave    = Slave number
+ * @param[in]  Thread_n = Calling thread index
+ * @param[out] Osize    = Size in bits of output mapping (rxPDO) found
+ * @param[out] Isize    = Size in bits of input mapping (txPDO) found
+ * @return >0 if mapping successful.
+ */
+int ecx_readPDOmapCA(ecx_contextt *context, uint16 Slave, int Thread_n, int *Osize, int *Isize)
+{
+   int wkc, rdl;
+   int retVal = 0;
+   uint8 nSM, iSM, tSM;
+   int Tsize;
+   uint8 SMt_bug_add;
+
+   *Isize = 0;
+   *Osize = 0;
+   SMt_bug_add = 0;
+   rdl = sizeof(ec_SMcommtypet);
+   context->SMcommtype[Thread_n].n = 0;
+   /* read SyncManager Communication Type object count Complete Access*/
+   wkc = ecx_SDOread(context, Slave, ECT_SDO_SMCOMMTYPE, 0x00, TRUE, &rdl,
+         &(context->SMcommtype[Thread_n]), EC_TIMEOUTRXM);
+   /* positive result from slave ? */
+   if ((wkc > 0) && (context->SMcommtype[Thread_n].n > 2))
+   {
+      nSM = context->SMcommtype[Thread_n].n;
+      /* limit to maximum number of SM defined, if true the slave can't be configured */
+      if (nSM > EC_MAXSM)
+      {
+         nSM = EC_MAXSM;
+         ecx_packeterror(context, Slave, 0, 0, 10); /* #SM larger than EC_MAXSM */
+      }
+      /* iterate for every SM type defined */
+      for (iSM = 2 ; iSM < nSM ; iSM++)
+      {
+         tSM = context->SMcommtype[Thread_n].SMtype[iSM];
+
+// start slave bug prevention code, remove if possible
+         if((iSM == 2) && (tSM == 2)) // SM2 has type 2 == mailbox out, this is a bug in the slave!
+         {
+            SMt_bug_add = 1; // try to correct, this works if the types are 0 1 2 3 and should be 1 2 3 4
+         }
+         if(tSM)
+         {
+            tSM += SMt_bug_add; // only add if SMt > 0
+         }
+// end slave bug prevention code
+
+         context->slavelist[Slave].SMtype[iSM] = tSM;
+         /* check if SM is unused -> clear enable flag */
+         if (tSM == 0)
+         {
+            context->slavelist[Slave].SM[iSM].SMflags =
+               htoel( etohl(context->slavelist[Slave].SM[iSM].SMflags) & EC_SMENABLEMASK);
+         }
+         if ((tSM == 3) || (tSM == 4))
+         {
+            /* read the assign PDO */
+            Tsize = ecx_readPDOassignCA(context, Slave, Thread_n,
+                  ECT_SDO_PDOASSIGN + iSM );
+            /* if a mapping is found */
+            if (Tsize)
+            {
+               context->slavelist[Slave].SM[iSM].SMlength = htoes((Tsize + 7) / 8);
+               if (tSM == 3)
+               {
+                  /* we are doing outputs */
+                  *Osize += Tsize;
+               }
+               else
+               {
+                  /* we are doing inputs */
+                  *Isize += Tsize;
+               }
+            }
+         }
+      }
+   }
+
+   /* found some I/O bits ? */
+   if ((*Isize > 0) || (*Osize > 0))
+   {
+      retVal = 1;
+   }
+   return retVal;
+}
+
+/** CoE read Object Description List.
+ *
+ * @param[in]  context  = context struct
+ * @param[in]  Slave    = Slave number.
+ * @param[out] pODlist  = resulting Object Description list.
+ * @return Workcounter of slave response.
+ */
+int ecx_readODlist(ecx_contextt *context, uint16 Slave, ec_ODlistt *pODlist)
+{
+   ec_SDOservicet *SDOp, *aSDOp;
+   ec_mbxbuft MbxIn, MbxOut;
+   int wkc;
+   uint16 x, n, i, sp, offset;
+   boolean stop;
+   uint8 cnt;
+   boolean First;
+
+   pODlist->Slave = Slave;
+   pODlist->Entries = 0;
+   ec_clearmbx(&MbxIn);
+   /* clear pending out mailbox in slave if available. Timeout is set to 0 */
+   wkc = ecx_mbxreceive(context, Slave, &MbxIn, 0);
+   ec_clearmbx(&MbxOut);
+   aSDOp = (ec_SDOservicet*)&MbxIn;
+   SDOp = (ec_SDOservicet*)&MbxOut;
+   SDOp->MbxHeader.length = htoes(0x0008);
+   SDOp->MbxHeader.address = htoes(0x0000);
+   SDOp->MbxHeader.priority = 0x00;
+   /* Get new mailbox counter value */
+   cnt = ec_nextmbxcnt(context->slavelist[Slave].mbx_cnt);
+   context->slavelist[Slave].mbx_cnt = cnt;
+   SDOp->MbxHeader.mbxtype = ECT_MBXT_COE + (cnt << 4); /* CoE */
+   SDOp->CANOpen = htoes(0x000 + (ECT_COES_SDOINFO << 12)); /* number 9bits service upper 4 bits */
+   SDOp->Opcode = ECT_GET_ODLIST_REQ; /* get object description list request */
+   SDOp->Reserved = 0;
+   SDOp->Fragments = 0; /* fragments left */
+   SDOp->wdata[0] = htoes(0x01); /* all objects */
+   /* send get object description list request to slave */
+   wkc = ecx_mbxsend(context, Slave, &MbxOut, EC_TIMEOUTTXM);
+   /* mailbox placed in slave ? */
+   if (wkc > 0)
+   {
+      x = 0;
+      sp = 0;
+      First = TRUE;
+      offset = 1; /* offset to skip info header in first frame, otherwise set to 0 */
+      do
+      {
+         stop = TRUE; /* assume this is last iteration */
+         ec_clearmbx(&MbxIn);
+         /* read slave response */
+         wkc = ecx_mbxreceive(context, Slave, &MbxIn, EC_TIMEOUTRXM);
+         /* got response ? */
+         if (wkc > 0)
+         {
+            /* response should be CoE and "get object description list response" */
+            if (((aSDOp->MbxHeader.mbxtype & 0x0f) == ECT_MBXT_COE) &&
+                ((aSDOp->Opcode & 0x7f) == ECT_GET_ODLIST_RES))
+            {
+               if (First)
+               {
+                  /* extract number of indexes from mailbox data size */
+                  n = (etohs(aSDOp->MbxHeader.length) - (6 + 2)) / 2;
+               }
+               else
+               {
+                  /* extract number of indexes from mailbox data size */
+                  n = (etohs(aSDOp->MbxHeader.length) - 6) / 2;
+               }
+               /* check if indexes fit in buffer structure */
+               if ((sp + n) > EC_MAXODLIST)
+               {
+                  n = EC_MAXODLIST + 1 - sp;
+                  ecx_SDOinfoerror(context, Slave, 0, 0, 0xf000000); /* Too many entries for master buffer */
+                  stop = TRUE;
+               }
+               /* trim to maximum number of ODlist entries defined */
+               if ((pODlist->Entries + n) > EC_MAXODLIST)
+               {
+                  n = EC_MAXODLIST - pODlist->Entries;
+               }
+               pODlist->Entries += n;
+               /* extract indexes one by one */
+               for (i = 0; i < n; i++)
+               {
+                  pODlist->Index[sp + i] = etohs(aSDOp->wdata[i + offset]);
+               }
+               sp += n;
+               /* check if more fragments will follow */
+               if (aSDOp->Fragments > 0)
+               {
+                  stop = FALSE;
+               }
+               First = FALSE;
+               offset = 0;
+            }
+            /* got unexpected response from slave */
+            else
+            {
+               if ((aSDOp->Opcode &  0x7f) == ECT_SDOINFO_ERROR) /* SDO info error received */
+               {
+                  ecx_SDOinfoerror(context, Slave, 0, 0, etohl(aSDOp->ldata[0]));
+                  stop = TRUE;
+               }
+               else
+               {
+                  ecx_packeterror(context, Slave, 0, 0, 1); /* Unexpected frame returned */
+               }
+               wkc = 0;
+               x += 20;
+            }
+         }
+         x++;
+      }
+      while ((x <= 128) && !stop);
+   }
+   return wkc;
+}
+
+/** CoE read Object Description. Adds textual description to object indexes.
+ *
+ * @param[in]  context       = context struct
+ * @param[in] Item           = Item number in ODlist.
+ * @param[in,out] pODlist    = referencing Object Description list.
+ * @return Workcounter of slave response.
+ */
+int ecx_readODdescription(ecx_contextt *context, uint16 Item, ec_ODlistt *pODlist)
+{
+   ec_SDOservicet *SDOp, *aSDOp;
+   int wkc;
+   uint16  n, Slave;
+   ec_mbxbuft MbxIn, MbxOut;
+   uint8 cnt;
+
+   Slave = pODlist->Slave;
+   pODlist->DataType[Item] = 0;
+   pODlist->ObjectCode[Item] = 0;
+   pODlist->MaxSub[Item] = 0;
+   pODlist->Name[Item][0] = 0;
+   ec_clearmbx(&MbxIn);
+   /* clear pending out mailbox in slave if available. Timeout is set to 0 */
+   wkc = ecx_mbxreceive(context, Slave, &MbxIn, 0);
+   ec_clearmbx(&MbxOut);
+   aSDOp = (ec_SDOservicet*)&MbxIn;
+   SDOp = (ec_SDOservicet*)&MbxOut;
+   SDOp->MbxHeader.length = htoes(0x0008);
+   SDOp->MbxHeader.address = htoes(0x0000);
+   SDOp->MbxHeader.priority = 0x00;
+   /* Get new mailbox counter value */
+   cnt = ec_nextmbxcnt(context->slavelist[Slave].mbx_cnt);
+   context->slavelist[Slave].mbx_cnt = cnt;
+   SDOp->MbxHeader.mbxtype = ECT_MBXT_COE + (cnt << 4); /* CoE */
+   SDOp->CANOpen = htoes(0x000 + (ECT_COES_SDOINFO << 12)); /* number 9bits service upper 4 bits */
+   SDOp->Opcode = ECT_GET_OD_REQ; /* get object description request */
+   SDOp->Reserved = 0;
+   SDOp->Fragments = 0; /* fragments left */
+   SDOp->wdata[0] = htoes(pODlist->Index[Item]); /* Data of Index */
+   /* send get object description request to slave */
+   wkc = ecx_mbxsend(context, Slave, &MbxOut, EC_TIMEOUTTXM);
+   /* mailbox placed in slave ? */
+   if (wkc > 0)
+   {
+      ec_clearmbx(&MbxIn);
+      /* read slave response */
+      wkc = ecx_mbxreceive(context, Slave, &MbxIn, EC_TIMEOUTRXM);
+      /* got response ? */
+      if (wkc > 0)
+      {
+         if (((aSDOp->MbxHeader.mbxtype & 0x0f) == ECT_MBXT_COE) &&
+             ((aSDOp->Opcode & 0x7f) == ECT_GET_OD_RES))
+         {
+            n = (etohs(aSDOp->MbxHeader.length) - 12); /* length of string(name of object) */
+            if (n > EC_MAXNAME)
+            {
+               n = EC_MAXNAME; /* max chars */
+            }
+            pODlist->DataType[Item] = etohs(aSDOp->wdata[1]);
+            pODlist->ObjectCode[Item] = aSDOp->bdata[5];
+            pODlist->MaxSub[Item] = aSDOp->bdata[4];
+
+            strncpy(pODlist->Name[Item] , (char *)&aSDOp->bdata[6], n);
+            pODlist->Name[Item][n] = 0x00; /* String terminator */
+         }
+         /* got unexpected response from slave */
+         else
+         {
+            if (((aSDOp->Opcode & 0x7f) == ECT_SDOINFO_ERROR)) /* SDO info error received */
+            {
+               ecx_SDOinfoerror(context, Slave,pODlist->Index[Item], 0, etohl(aSDOp->ldata[0]));
+            }
+            else
+            {
+               ecx_packeterror(context, Slave,pODlist->Index[Item], 0, 1); /* Unexpected frame returned */
+            }
+            wkc = 0;
+         }
+      }
+   }
+
+   return wkc;
+}
+
+/** CoE read SDO service object entry, single subindex.
+ * Used in ec_readOE().
+ *
+ * @param[in]  context       = context struct
+ * @param[in] Item           = Item in ODlist.
+ * @param[in] SubI           = Subindex of item in ODlist.
+ * @param[in] pODlist        = Object description list for reference.
+ * @param[out] pOElist       = resulting object entry structure.
+ * @return Workcounter of slave response.
+ */
+int ecx_readOEsingle(ecx_contextt *context, uint16 Item, uint8 SubI, ec_ODlistt *pODlist, ec_OElistt *pOElist)
+{
+   ec_SDOservicet *SDOp, *aSDOp;
+   int wkc;
+   uint16 Index, Slave;
+   int16 n;
+   ec_mbxbuft MbxIn, MbxOut;
+   uint8 cnt;
+
+   wkc = 0;
+   Slave = pODlist->Slave;
+   Index = pODlist->Index[Item];
+   ec_clearmbx(&MbxIn);
+   /* clear pending out mailbox in slave if available. Timeout is set to 0 */
+   wkc = ecx_mbxreceive(context, Slave, &MbxIn, 0);
+   ec_clearmbx(&MbxOut);
+   aSDOp = (ec_SDOservicet*)&MbxIn;
+   SDOp = (ec_SDOservicet*)&MbxOut;
+   SDOp->MbxHeader.length = htoes(0x000a);
+   SDOp->MbxHeader.address = htoes(0x0000);
+   SDOp->MbxHeader.priority = 0x00;
+   /* Get new mailbox counter value */
+   cnt = ec_nextmbxcnt(context->slavelist[Slave].mbx_cnt);
+   context->slavelist[Slave].mbx_cnt = cnt;
+   SDOp->MbxHeader.mbxtype = ECT_MBXT_COE + (cnt << 4); /* CoE */
+   SDOp->CANOpen = htoes(0x000 + (ECT_COES_SDOINFO << 12)); /* number 9bits service upper 4 bits */
+   SDOp->Opcode = ECT_GET_OE_REQ; /* get object entry description request */
+   SDOp->Reserved = 0;
+   SDOp->Fragments = 0;      /* fragments left */
+   SDOp->wdata[0] = htoes(Index);      /* Index */
+   SDOp->bdata[2] = SubI;       /* SubIndex */
+   SDOp->bdata[3] = 1 + 2 + 4; /* get access rights, object category, PDO */
+   /* send get object entry description request to slave */
+   wkc = ecx_mbxsend(context, Slave, &MbxOut, EC_TIMEOUTTXM);
+   /* mailbox placed in slave ? */
+   if (wkc > 0)
+   {
+      ec_clearmbx(&MbxIn);
+      /* read slave response */
+      wkc = ecx_mbxreceive(context, Slave, &MbxIn, EC_TIMEOUTRXM);
+      /* got response ? */
+      if (wkc > 0)
+      {
+         if (((aSDOp->MbxHeader.mbxtype & 0x0f) == ECT_MBXT_COE) &&
+             ((aSDOp->Opcode &  0x7f) == ECT_GET_OE_RES))
+         {
+            pOElist->Entries++;
+            n = (etohs(aSDOp->MbxHeader.length) - 16); /* length of string(name of object) */
+            if (n > EC_MAXNAME)
+            {
+               n = EC_MAXNAME; /* max string length */
+            }
+            if (n < 0 )
+            {
+               n = 0;
+            }
+            pOElist->ValueInfo[SubI] = aSDOp->bdata[3];
+            pOElist->DataType[SubI] = etohs(aSDOp->wdata[2]);
+            pOElist->BitLength[SubI] = etohs(aSDOp->wdata[3]);
+            pOElist->ObjAccess[SubI] = etohs(aSDOp->wdata[4]);
+
+            strncpy(pOElist->Name[SubI] , (char *)&aSDOp->wdata[5], n);
+            pOElist->Name[SubI][n] = 0x00; /* string terminator */
+         }
+         /* got unexpected response from slave */
+         else
+         {
+            if (((aSDOp->Opcode & 0x7f) == ECT_SDOINFO_ERROR)) /* SDO info error received */
+            {
+               ecx_SDOinfoerror(context, Slave, Index, SubI, etohl(aSDOp->ldata[0]));
+            }
+            else
+            {
+               ecx_packeterror(context, Slave, Index, SubI, 1); /* Unexpected frame returned */
+            }
+            wkc = 0;
+         }
+      }
+   }
+
+   return wkc;
+}
+
+/** CoE read SDO service object entry.
+ *
+ * @param[in] context        = context struct
+ * @param[in] Item           = Item in ODlist.
+ * @param[in] pODlist        = Object description list for reference.
+ * @param[out] pOElist       = resulting object entry structure.
+ * @return Workcounter of slave response.
+ */
+int ecx_readOE(ecx_contextt *context, uint16 Item, ec_ODlistt *pODlist, ec_OElistt *pOElist)
+{
+   uint16 SubCount;
+   int wkc;
+   uint8 SubI;
+
+   wkc = 0;
+   pOElist->Entries = 0;
+   SubI = pODlist->MaxSub[Item];
+   /* for each entry found in ODlist */
+   for (SubCount = 0; SubCount <= SubI; SubCount++)
+   {
+      /* read subindex of entry */
+      wkc = ecx_readOEsingle(context, Item, (uint8)SubCount, pODlist, pOElist);
+   }
+
+   return wkc;
+}
+
+#ifdef EC_VER1
+/** Report SDO error.
+ *
+ * @param[in]  Slave      = Slave number
+ * @param[in]  Index      = Index that generated error
+ * @param[in]  SubIdx     = Subindex that generated error
+ * @param[in]  AbortCode  = Abortcode, see EtherCAT documentation for list
+ * @see ecx_SDOerror
+ */
+void ec_SDOerror(uint16 Slave, uint16 Index, uint8 SubIdx, int32 AbortCode)
+{
+   ecx_SDOerror(&ecx_context, Slave, Index, SubIdx, AbortCode);
+}
+
+/** CoE SDO read, blocking. Single subindex or Complete Access.
+ *
+ * Only a "normal" upload request is issued. If the requested parameter is <= 4bytes
+ * then a "expedited" response is returned, otherwise a "normal" response. If a "normal"
+ * response is larger than the mailbox size then the response is segmented. The function
+ * will combine all segments and copy them to the parameter buffer.
+ *
+ * @param[in]  slave      = Slave number
+ * @param[in]  index      = Index to read
+ * @param[in]  subindex   = Subindex to read, must be 0 or 1 if CA is used.
+ * @param[in]  CA         = FALSE = single subindex. TRUE = Complete Access, all subindexes read.
+ * @param[in,out] psize   = Size in bytes of parameter buffer, returns bytes read from SDO.
+ * @param[out] p          = Pointer to parameter buffer
+ * @param[in]  timeout    = Timeout in us, standard is EC_TIMEOUTRXM
+ * @return Workcounter from last slave response
+ * @see ecx_SDOread
+ */
+int ec_SDOread(uint16 slave, uint16 index, uint8 subindex,
+               boolean CA, int *psize, void *p, int timeout)
+{
+   return ecx_SDOread(&ecx_context, slave, index, subindex, CA, psize, p, timeout);
+}
+
+/** CoE SDO write, blocking. Single subindex or Complete Access.
+ *
+ * A "normal" download request is issued, unless we have
+ * small data, then a "expedited" transfer is used. If the parameter is larger than
+ * the mailbox size then the download is segmented. The function will split the
+ * parameter data in segments and send them to the slave one by one.
+ *
+ * @param[in]  Slave      = Slave number
+ * @param[in]  Index      = Index to write
+ * @param[in]  SubIndex   = Subindex to write, must be 0 or 1 if CA is used.
+ * @param[in]  CA         = FALSE = single subindex. TRUE = Complete Access, all subindexes written.
+ * @param[in]  psize      = Size in bytes of parameter buffer.
+ * @param[out] p          = Pointer to parameter buffer
+ * @param[in]  Timeout    = Timeout in us, standard is EC_TIMEOUTRXM
+ * @return Workcounter from last slave response
+ * @see ecx_SDOwrite
+ */
+int ec_SDOwrite(uint16 Slave, uint16 Index, uint8 SubIndex,
+                boolean CA, int psize, void *p, int Timeout)
+{
+   return ecx_SDOwrite(&ecx_context, Slave, Index, SubIndex, CA, psize, p, Timeout);
+}
+
+/** CoE RxPDO write, blocking.
+ *
+ * A RxPDO download request is issued.
+ *
+ * @param[in]  Slave         = Slave number
+ * @param[in]  RxPDOnumber   = Related RxPDO number
+ * @param[in]  psize         = Size in bytes of PDO buffer.
+ * @param[out] p             = Pointer to PDO buffer
+ * @return Workcounter from last slave response
+ * @see ecx_RxPDO
+ */
+int ec_RxPDO(uint16 Slave, uint16 RxPDOnumber, int psize, void *p)
+{
+   return ecx_RxPDO(&ecx_context, Slave, RxPDOnumber, psize, p);
+}
+
+/** CoE TxPDO read remote request, blocking.
+ *
+ * A RxPDO download request is issued.
+ *
+ * @param[in]  slave         = Slave number
+ * @param[in]  TxPDOnumber   = Related TxPDO number
+ * @param[in,out] psize      = Size in bytes of PDO buffer, returns bytes read from PDO.
+ * @param[out] p             = Pointer to PDO buffer
+ * @param[in]  timeout       = Timeout in us, standard is EC_TIMEOUTRXM
+ * @return Workcounter from last slave response
+ * @see ecx_TxPDO
+ */
+int ec_TxPDO(uint16 slave, uint16 TxPDOnumber , int *psize, void *p, int timeout)
+{
+   return ecx_TxPDO(&ecx_context, slave, TxPDOnumber, psize, p, timeout);
+}
+
+/** Read PDO assign structure
+ * @param[in]  Slave         = Slave number
+ * @param[in]  PDOassign     = PDO assign object
+ * @return total bitlength of PDO assign
+ */
+int ec_readPDOassign(uint16 Slave, uint16 PDOassign)
+{
+   return ecx_readPDOassign(&ecx_context, Slave, PDOassign);
+}
+
+/** Read PDO assign structure in Complete Access mode
+ * @param[in]  Slave         = Slave number
+ * @param[in]  PDOassign     = PDO assign object
+ * @param[in]  Thread_n      = Calling thread index
+ * @return total bitlength of PDO assign
+ * @see ecx_readPDOmap
+ */
+int ec_readPDOassignCA(uint16 Slave, uint16 PDOassign, int Thread_n)
+{
+   return ecx_readPDOassignCA(&ecx_context, Slave, Thread_n, PDOassign);
+}
+
+/** CoE read PDO mapping.
+ *
+ * CANopen has standard indexes defined for PDO mapping. This function
+ * tries to read them and collect a full input and output mapping size
+ * of designated slave.
+ *
+ * For details, see #ecx_readPDOmap
+ *
+ * @param[in] Slave    = Slave number
+ * @param[out] Osize   = Size in bits of output mapping (rxPDO) found
+ * @param[out] Isize   = Size in bits of input mapping (txPDO) found
+ * @return >0 if mapping succesful.
+ */
+int ec_readPDOmap(uint16 Slave, int *Osize, int *Isize)
+{
+   return ecx_readPDOmap(&ecx_context, Slave, Osize, Isize);
+}
+
+/** CoE read PDO mapping in Complete Access mode (CA).
+ *
+ * CANopen has standard indexes defined for PDO mapping. This function
+ * tries to read them and collect a full input and output mapping size
+ * of designated slave. Slave has to support CA, otherwise use ec_readPDOmap().
+ *
+ * @param[in] Slave    = Slave number
+ * @param[in] Thread_n = Calling thread index
+ * @param[out] Osize   = Size in bits of output mapping (rxPDO) found
+ * @param[out] Isize   = Size in bits of input mapping (txPDO) found
+ * @return >0 if mapping succesful.
+ * @see ecx_readPDOmap ec_readPDOmapCA
+ */
+int ec_readPDOmapCA(uint16 Slave, int Thread_n, int *Osize, int *Isize)
+{
+   return ecx_readPDOmapCA(&ecx_context, Slave, Thread_n, Osize, Isize);
+}
+
+/** CoE read Object Description List.
+ *
+ * @param[in] Slave      = Slave number.
+ * @param[out] pODlist  = resulting Object Description list.
+ * @return Workcounter of slave response.
+ * @see ecx_readODlist
+ */
+int ec_readODlist(uint16 Slave, ec_ODlistt *pODlist)
+{
+   return ecx_readODlist(&ecx_context, Slave, pODlist);
+}
+
+/** CoE read Object Description. Adds textual description to object indexes.
+ *
+ * @param[in] Item           = Item number in ODlist.
+ * @param[in,out] pODlist    = referencing Object Description list.
+ * @return Workcounter of slave response.
+ * @see ecx_readODdescription
+ */
+int ec_readODdescription(uint16 Item, ec_ODlistt *pODlist)
+{
+   return ecx_readODdescription(&ecx_context, Item, pODlist);
+}
+
+int ec_readOEsingle(uint16 Item, uint8 SubI, ec_ODlistt *pODlist, ec_OElistt *pOElist)
+{
+   return ecx_readOEsingle(&ecx_context, Item, SubI, pODlist, pOElist);
+}
+
+/** CoE read SDO service object entry.
+ *
+ * @param[in] Item           = Item in ODlist.
+ * @param[in] pODlist        = Object description list for reference.
+ * @param[out] pOElist       = resulting object entry structure.
+ * @return Workcounter of slave response.
+ * @see ecx_readOE
+ */
+int ec_readOE(uint16 Item, ec_ODlistt *pODlist, ec_OElistt *pOElist)
+{
+   return ecx_readOE(&ecx_context, Item, pODlist, pOElist);
+}
+#endif
diff -r 000000000000 -r 543d6784d4cc SOEM/ethercatcoe.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SOEM/ethercatcoe.h	Tue Jun 11 10:29:09 2019 +0000
@@ -0,0 +1,95 @@
+/*
+ * Licensed under the GNU General Public License version 2 with exceptions. See
+ * LICENSE file in the project root for full license information
+ */
+
+/** \file
+ * \brief
+ * Headerfile for ethercatcoe.c
+ */
+
+#ifndef _ethercatcoe_
+#define _ethercatcoe_
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/** max entries in Object Description list */
+#define EC_MAXODLIST   1024
+
+/** max entries in Object Entry list */
+#define EC_MAXOELIST   256
+
+/* Storage for object description list */
+typedef struct
+{
+   /** slave number */
+   uint16  Slave;
+   /** number of entries in list */
+   uint16  Entries;
+   /** array of indexes */
+   uint16  Index[EC_MAXODLIST];
+   /** array of datatypes, see EtherCAT specification */
+   uint16  DataType[EC_MAXODLIST];
+   /** array of object codes, see EtherCAT specification */
+   uint8   ObjectCode[EC_MAXODLIST];
+   /** number of subindexes for each index */
+   uint8   MaxSub[EC_MAXODLIST];
+   /** textual description of each index */
+   char    Name[EC_MAXODLIST][EC_MAXNAME+1];
+} ec_ODlistt;
+
+/* storage for object list entry information */
+typedef struct
+{
+   /** number of entries in list */
+   uint16 Entries;
+   /** array of value infos, see EtherCAT specification */
+   uint8  ValueInfo[EC_MAXOELIST];
+   /** array of value infos, see EtherCAT specification */
+   uint16 DataType[EC_MAXOELIST];
+   /** array of bit lengths, see EtherCAT specification */
+   uint16 BitLength[EC_MAXOELIST];
+   /** array of object access bits, see EtherCAT specification */
+   uint16 ObjAccess[EC_MAXOELIST];
+   /** textual description of each index */
+   char   Name[EC_MAXOELIST][EC_MAXNAME+1];
+} ec_OElistt;
+
+#ifdef EC_VER1
+void ec_SDOerror(uint16 Slave, uint16 Index, uint8 SubIdx, int32 AbortCode);
+int ec_SDOread(uint16 slave, uint16 index, uint8 subindex,
+                      boolean CA, int *psize, void *p, int timeout);
+int ec_SDOwrite(uint16 Slave, uint16 Index, uint8 SubIndex,
+    boolean CA, int psize, void *p, int Timeout);
+int ec_RxPDO(uint16 Slave, uint16 RxPDOnumber , int psize, void *p);
+int ec_TxPDO(uint16 slave, uint16 TxPDOnumber , int *psize, void *p, int timeout);
+int ec_readPDOmap(uint16 Slave, int *Osize, int *Isize);
+int ec_readPDOmapCA(uint16 Slave, int Thread_n, int *Osize, int *Isize);
+int ec_readODlist(uint16 Slave, ec_ODlistt *pODlist);
+int ec_readODdescription(uint16 Item, ec_ODlistt *pODlist);
+int ec_readOEsingle(uint16 Item, uint8 SubI, ec_ODlistt *pODlist, ec_OElistt *pOElist);
+int ec_readOE(uint16 Item, ec_ODlistt *pODlist, ec_OElistt *pOElist);
+#endif
+
+void ecx_SDOerror(ecx_contextt *context, uint16 Slave, uint16 Index, uint8 SubIdx, int32 AbortCode);
+int ecx_SDOread(ecx_contextt *context, uint16 slave, uint16 index, uint8 subindex,
+                      boolean CA, int *psize, void *p, int timeout);
+int ecx_SDOwrite(ecx_contextt *context, uint16 Slave, uint16 Index, uint8 SubIndex,
+    boolean CA, int psize, void *p, int Timeout);
+int ecx_RxPDO(ecx_contextt *context, uint16 Slave, uint16 RxPDOnumber , int psize, void *p);
+int ecx_TxPDO(ecx_contextt *context, uint16 slave, uint16 TxPDOnumber , int *psize, void *p, int timeout);
+int ecx_readPDOmap(ecx_contextt *context, uint16 Slave, int *Osize, int *Isize);
+int ecx_readPDOmapCA(ecx_contextt *context, uint16 Slave, int Thread_n, int *Osize, int *Isize);
+int ecx_readODlist(ecx_contextt *context, uint16 Slave, ec_ODlistt *pODlist);
+int ecx_readODdescription(ecx_contextt *context, uint16 Item, ec_ODlistt *pODlist);
+int ecx_readOEsingle(ecx_contextt *context, uint16 Item, uint8 SubI, ec_ODlistt *pODlist, ec_OElistt *pOElist);
+int ecx_readOE(ecx_contextt *context, uint16 Item, ec_ODlistt *pODlist, ec_OElistt *pOElist);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff -r 000000000000 -r 543d6784d4cc SOEM/ethercatconfig.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SOEM/ethercatconfig.c	Tue Jun 11 10:29:09 2019 +0000
@@ -0,0 +1,1688 @@
+/*
+ * Licensed under the GNU General Public License version 2 with exceptions. See
+ * LICENSE file in the project root for full license information
+ */
+
+/** \file
+ * \brief
+ * Configuration module for EtherCAT master.
+ *
+ * After successful initialisation with ec_init() or ec_init_redundant()
+ * the slaves can be auto configured with this module.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include "osal.h"
+#include "oshw.h"
+#include "ethercattype.h"
+#include "ethercatbase.h"
+#include "ethercatmain.h"
+#include "ethercatcoe.h"
+#include "ethercatsoe.h"
+#include "ethercatconfig.h"
+
+
+typedef struct
+{
+   int thread_n;
+   int running;
+   ecx_contextt *context;
+   uint16 slave;
+} ecx_mapt_t;
+
+ecx_mapt_t ecx_mapt[EC_MAX_MAPT];
+#if EC_MAX_MAPT > 1
+OSAL_THREAD_HANDLE ecx_threadh[EC_MAX_MAPT];
+#endif
+
+#ifdef EC_VER1
+/** Slave configuration structure */
+typedef const struct
+{
+   /** Manufacturer code of slave */
+   uint32           man;
+   /** ID of slave */
+   uint32           id;
+   /** Readable name */
+   char             name[EC_MAXNAME + 1];
+   /** Data type */
+   uint8            Dtype;
+   /** Input bits */
+   uint16            Ibits;
+   /** Output bits */
+   uint16           Obits;
+   /** SyncManager 2 address */
+   uint16           SM2a;
+   /** SyncManager 2 flags */
+   uint32           SM2f;
+   /** SyncManager 3 address */
+   uint16           SM3a;
+   /** SyncManager 3 flags */
+   uint32           SM3f;
+   /** FMMU 0 activation */
+   uint8            FM0ac;
+   /** FMMU 1 activation */
+   uint8            FM1ac;
+} ec_configlist_t;
+
+#include "ethercatconfiglist.h"
+#endif
+
+/** standard SM0 flags configuration for mailbox slaves */
+#define EC_DEFAULTMBXSM0  0x00010026
+/** standard SM1 flags configuration for mailbox slaves */
+#define EC_DEFAULTMBXSM1  0x00010022
+/** standard SM0 flags configuration for digital output slaves */
+#define EC_DEFAULTDOSM0   0x00010044
+
+#ifdef EC_VER1
+/** Find slave in standard configuration list ec_configlist[]
+ *
+ * @param[in] man      = manufacturer
+ * @param[in] id       = ID
+ * @return index in ec_configlist[] when found, otherwise 0
+ */
+int ec_findconfig( uint32 man, uint32 id)
+{
+   int i = 0;
+
+   do
+   {
+      i++;
+   } while ( (ec_configlist[i].man != EC_CONFIGEND) &&
+           ((ec_configlist[i].man != man) || (ec_configlist[i].id != id)) );
+   if (ec_configlist[i].man == EC_CONFIGEND)
+   {
+      i = 0;
+   }
+   return i;
+}
+#endif
+
+void ecx_init_context(ecx_contextt *context)
+{
+   int lp;
+   *(context->slavecount) = 0;
+   /* clean ec_slave array */
+   memset(context->slavelist, 0x00, sizeof(ec_slavet) * context->maxslave);
+   memset(context->grouplist, 0x00, sizeof(ec_groupt) * context->maxgroup);
+   /* clear slave eeprom cache, does not actually read any eeprom */
+   ecx_siigetbyte(context, 0, EC_MAXEEPBUF);
+   for(lp = 0; lp < context->maxgroup; lp++)
+   {
+      /* default start address per group entry */
+      context->grouplist[lp].logstartaddr = lp << EC_LOGGROUPOFFSET;
+   }
+}
+
+int ecx_detect_slaves(ecx_contextt *context)
+{
+   uint8  b;
+   uint16 w;
+   int    wkc;
+
+   /* make special pre-init register writes to enable MAC[1] local administered bit *
+    * setting for old netX100 slaves */
+   b = 0x00;
+   ecx_BWR(context->port, 0x0000, ECT_REG_DLALIAS, sizeof(b), &b, EC_TIMEOUTRET3);     /* Ignore Alias register */
+   b = EC_STATE_INIT | EC_STATE_ACK;
+   ecx_BWR(context->port, 0x0000, ECT_REG_ALCTL, sizeof(b), &b, EC_TIMEOUTRET3);       /* Reset all slaves to Init */
+   /* netX100 should now be happy */
+   ecx_BWR(context->port, 0x0000, ECT_REG_ALCTL, sizeof(b), &b, EC_TIMEOUTRET3);       /* Reset all slaves to Init */
+   wkc = ecx_BRD(context->port, 0x0000, ECT_REG_TYPE, sizeof(w), &w, EC_TIMEOUTSAFE);  /* detect number of slaves */
+   if (wkc > 0)
+   {
+      /* this is strictly "less than" since the master is "slave 0" */
+      if (wkc < EC_MAXSLAVE)
+      {
+         *(context->slavecount) = wkc;
+      }
+      else
+      {
+         EC_PRINT("Error: too many slaves on network: num_slaves=%d, EC_MAXSLAVE=%d\n",
+               wkc, EC_MAXSLAVE);
+         return -2;
+      }
+   }
+   return wkc;
+}
+
+static void ecx_set_slaves_to_default(ecx_contextt *context)
+{
+   uint8 b;
+   uint16 w;
+   uint8 zbuf[64];
+   memset(&zbuf, 0x00, sizeof(zbuf));
+   b = 0x00;
+   ecx_BWR(context->port, 0x0000, ECT_REG_DLPORT      , sizeof(b) , &b, EC_TIMEOUTRET3);     /* deact loop manual */
+   w = htoes(0x0004);
+   ecx_BWR(context->port, 0x0000, ECT_REG_IRQMASK     , sizeof(w) , &w, EC_TIMEOUTRET3);     /* set IRQ mask */
+   ecx_BWR(context->port, 0x0000, ECT_REG_RXERR       , 8         , &zbuf, EC_TIMEOUTRET3);  /* reset CRC counters */
+   ecx_BWR(context->port, 0x0000, ECT_REG_FMMU0       , 16 * 3    , &zbuf, EC_TIMEOUTRET3);  /* reset FMMU's */
+   ecx_BWR(context->port, 0x0000, ECT_REG_SM0         , 8 * 4     , &zbuf, EC_TIMEOUTRET3);  /* reset SyncM */
+   b = 0x00;
+   ecx_BWR(context->port, 0x0000, ECT_REG_DCSYNCACT   , sizeof(b) , &b, EC_TIMEOUTRET3);     /* reset activation register */
+   ecx_BWR(context->port, 0x0000, ECT_REG_DCSYSTIME   , 4         , &zbuf, EC_TIMEOUTRET3);  /* reset system time+ofs */
+   w = htoes(0x1000);
+   ecx_BWR(context->port, 0x0000, ECT_REG_DCSPEEDCNT  , sizeof(w) , &w, EC_TIMEOUTRET3);     /* DC speedstart */
+   w = htoes(0x0c00);
+   ecx_BWR(context->port, 0x0000, ECT_REG_DCTIMEFILT  , sizeof(w) , &w, EC_TIMEOUTRET3);     /* DC filt expr */
+   b = 0x00;
+   ecx_BWR(context->port, 0x0000, ECT_REG_DLALIAS     , sizeof(b) , &b, EC_TIMEOUTRET3);     /* Ignore Alias register */
+   b = EC_STATE_INIT | EC_STATE_ACK;
+   ecx_BWR(context->port, 0x0000, ECT_REG_ALCTL       , sizeof(b) , &b, EC_TIMEOUTRET3);     /* Reset all slaves to Init */
+   b = 2;
+   ecx_BWR(context->port, 0x0000, ECT_REG_EEPCFG      , sizeof(b) , &b, EC_TIMEOUTRET3);     /* force Eeprom from PDI */
+   b = 0;
+   ecx_BWR(context->port, 0x0000, ECT_REG_EEPCFG      , sizeof(b) , &b, EC_TIMEOUTRET3);     /* set Eeprom to master */
+}
+
+#ifdef EC_VER1
+static int ecx_config_from_table(ecx_contextt *context, uint16 slave)
+{
+   int cindex;
+   ec_slavet *csl;
+
+   csl = &(context->slavelist[slave]);
+   cindex = ec_findconfig( csl->eep_man, csl->eep_id );
+   csl->configindex= cindex;
+   /* slave found in configuration table ? */
+   if (cindex)
+   {
+      csl->Dtype = ec_configlist[cindex].Dtype;
+      strcpy(csl->name ,ec_configlist[cindex].name);
+      csl->Ibits = ec_configlist[cindex].Ibits;
+      csl->Obits = ec_configlist[cindex].Obits;
+      if (csl->Obits)
+      {
+         csl->FMMU0func = 1;
+      }
+      if (csl->Ibits)
+      {
+         csl->FMMU1func = 2;
+      }
+      csl->FMMU[0].FMMUactive = ec_configlist[cindex].FM0ac;
+      csl->FMMU[1].FMMUactive = ec_configlist[cindex].FM1ac;
+      csl->SM[2].StartAddr = htoes(ec_configlist[cindex].SM2a);
+      csl->SM[2].SMflags = htoel(ec_configlist[cindex].SM2f);
+      /* simple (no mailbox) output slave found ? */
+      if (csl->Obits && !csl->SM[2].StartAddr)
+      {
+         csl->SM[0].StartAddr = htoes(0x0f00);
+         csl->SM[0].SMlength = htoes((csl->Obits + 7) / 8);
+         csl->SM[0].SMflags = htoel(EC_DEFAULTDOSM0);
+         csl->FMMU[0].FMMUactive = 1;
+         csl->FMMU[0].FMMUtype = 2;
+         csl->SMtype[0] = 3;
+      }
+      /* complex output slave */
+      else
+      {
+         csl->SM[2].SMlength = htoes((csl->Obits + 7) / 8);
+         csl->SMtype[2] = 3;
+      }
+      csl->SM[3].StartAddr = htoes(ec_configlist[cindex].SM3a);
+      csl->SM[3].SMflags = htoel(ec_configlist[cindex].SM3f);
+      /* simple (no mailbox) input slave found ? */
+      if (csl->Ibits && !csl->SM[3].StartAddr)
+      {
+         csl->SM[1].StartAddr = htoes(0x1000);
+         csl->SM[1].SMlength = htoes((csl->Ibits + 7) / 8);
+         csl->SM[1].SMflags = htoel(0x00000000);
+         csl->FMMU[1].FMMUactive = 1;
+         csl->FMMU[1].FMMUtype = 1;
+         csl->SMtype[1] = 4;
+      }
+      /* complex input slave */
+      else
+      {
+         csl->SM[3].SMlength = htoes((csl->Ibits + 7) / 8);
+         csl->SMtype[3] = 4;
+      }
+   }
+   return cindex;
+}
+#else
+static int ecx_config_from_table(ecx_contextt *context, uint16 slave)
+{
+   (void)context;
+   (void)slave;
+   return 0;
+}
+#endif
+
+/* If slave has SII and same slave ID done before, use previous data.
+ * This is safe because SII is constant for same slave ID.
+ */
+static int ecx_lookup_prev_sii(ecx_contextt *context, uint16 slave)
+{
+   int i, nSM;
+   if ((slave > 1) && (*(context->slavecount) > 0))
+   {
+      i = 1;
+      while(((context->slavelist[i].eep_man != context->slavelist[slave].eep_man) ||
+             (context->slavelist[i].eep_id  != context->slavelist[slave].eep_id ) ||
+             (context->slavelist[i].eep_rev != context->slavelist[slave].eep_rev)) &&
+            (i < slave))
+      {
+         i++;
+      }
+      if(i < slave)
+      {
+         context->slavelist[slave].CoEdetails = context->slavelist[i].CoEdetails;
+         context->slavelist[slave].FoEdetails = context->slavelist[i].FoEdetails;
+         context->slavelist[slave].EoEdetails = context->slavelist[i].EoEdetails;
+         context->slavelist[slave].SoEdetails = context->slavelist[i].SoEdetails;
+         if(context->slavelist[i].blockLRW > 0)
+         {
+            context->slavelist[slave].blockLRW = 1;
+            context->slavelist[0].blockLRW++;
+         }
+         context->slavelist[slave].Ebuscurrent = context->slavelist[i].Ebuscurrent;
+         context->slavelist[0].Ebuscurrent += context->slavelist[slave].Ebuscurrent;
+         memcpy(context->slavelist[slave].name, context->slavelist[i].name, EC_MAXNAME + 1);
+         for( nSM=0 ; nSM < EC_MAXSM ; nSM++ )
+         {
+            context->slavelist[slave].SM[nSM].StartAddr = context->slavelist[i].SM[nSM].StartAddr;
+            context->slavelist[slave].SM[nSM].SMlength  = context->slavelist[i].SM[nSM].SMlength;
+            context->slavelist[slave].SM[nSM].SMflags   = context->slavelist[i].SM[nSM].SMflags;
+         }
+         context->slavelist[slave].FMMU0func = context->slavelist[i].FMMU0func;
+         context->slavelist[slave].FMMU1func = context->slavelist[i].FMMU1func;
+         context->slavelist[slave].FMMU2func = context->slavelist[i].FMMU2func;
+         context->slavelist[slave].FMMU3func = context->slavelist[i].FMMU3func;
+         EC_PRINT("Copy SII slave %d from %d.\n", slave, i);
+         return 1;
+      }
+   }
+   return 0;
+}
+
+/** Enumerate and init all slaves.
+ *
+ * @param[in] context      = context struct
+ * @param[in] usetable     = TRUE when using configtable to init slaves, FALSE otherwise
+ * @return Workcounter of slave discover datagram = number of slaves found
+ */
+int ecx_config_init(ecx_contextt *context, uint8 usetable)
+{
+   uint16 slave, ADPh, configadr, ssigen;
+   uint16 topology, estat;
+   int16 topoc, slavec, aliasadr;
+   uint8 b,h;
+   uint8 SMc;
+   uint32 eedat;
+   int wkc, cindex, nSM;
+   uint16 val16;
+
+   EC_PRINT("ec_config_init %d\n",usetable);
+   ecx_init_context(context);
+   wkc = ecx_detect_slaves(context);
+   if (wkc > 0)
+   {
+      ecx_set_slaves_to_default(context);
+      for (slave = 1; slave <= *(context->slavecount); slave++)
+      {
+         ADPh = (uint16)(1 - slave);
+         val16 = ecx_APRDw(context->port, ADPh, ECT_REG_PDICTL, EC_TIMEOUTRET3); /* read interface type of slave */
+         context->slavelist[slave].Itype = etohs(val16);
+         /* a node offset is used to improve readability of network frames */
+         /* this has no impact on the number of addressable slaves (auto wrap around) */
+         ecx_APWRw(context->port, ADPh, ECT_REG_STADR, htoes(slave + EC_NODEOFFSET) , EC_TIMEOUTRET3); /* set node address of slave */
+         if (slave == 1)
+         {
+            b = 1; /* kill non ecat frames for first slave */
+         }
+         else
+         {
+            b = 0; /* pass all frames for following slaves */
+         }
+         ecx_APWRw(context->port, ADPh, ECT_REG_DLCTL, htoes(b), EC_TIMEOUTRET3); /* set non ecat frame behaviour */
+         configadr = ecx_APRDw(context->port, ADPh, ECT_REG_STADR, EC_TIMEOUTRET3);
+         configadr = etohs(configadr);
+         context->slavelist[slave].configadr = configadr;
+         ecx_FPRD(context->port, configadr, ECT_REG_ALIAS, sizeof(aliasadr), &aliasadr, EC_TIMEOUTRET3);
+         context->slavelist[slave].aliasadr = etohs(aliasadr);
+         ecx_FPRD(context->port, configadr, ECT_REG_EEPSTAT, sizeof(estat), &estat, EC_TIMEOUTRET3);
+         estat = etohs(estat);
+         if (estat & EC_ESTAT_R64) /* check if slave can read 8 byte chunks */
+         {
+            context->slavelist[slave].eep_8byte = 1;
+         }
+         ecx_readeeprom1(context, slave, ECT_SII_MANUF); /* Manuf */
+      }
+      for (slave = 1; slave <= *(context->slavecount); slave++)
+      {
+         eedat = ecx_readeeprom2(context, slave, EC_TIMEOUTEEP); /* Manuf */
+         context->slavelist[slave].eep_man = etohl(eedat);
+         ecx_readeeprom1(context, slave, ECT_SII_ID); /* ID */
+      }
+      for (slave = 1; slave <= *(context->slavecount); slave++)
+      {
+         eedat = ecx_readeeprom2(context, slave, EC_TIMEOUTEEP); /* ID */
+         context->slavelist[slave].eep_id = etohl(eedat);
+         ecx_readeeprom1(context, slave, ECT_SII_REV); /* revision */
+      }
+      for (slave = 1; slave <= *(context->slavecount); slave++)
+      {
+         eedat = ecx_readeeprom2(context, slave, EC_TIMEOUTEEP); /* revision */
+         context->slavelist[slave].eep_rev = etohl(eedat);
+         ecx_readeeprom1(context, slave, ECT_SII_RXMBXADR); /* write mailbox address + mailboxsize */
+      }
+      for (slave = 1; slave <= *(context->slavecount); slave++)
+      {
+         eedat = ecx_readeeprom2(context, slave, EC_TIMEOUTEEP); /* write mailbox address and mailboxsize */
+         context->slavelist[slave].mbx_wo = (uint16)LO_WORD(etohl(eedat));
+         context->slavelist[slave].mbx_l = (uint16)HI_WORD(etohl(eedat));
+         if (context->slavelist[slave].mbx_l > 0)
+         {
+            ecx_readeeprom1(context, slave, ECT_SII_TXMBXADR); /* read mailbox offset */
+         }
+      }
+      for (slave = 1; slave <= *(context->slavecount); slave++)
+      {
+         if (context->slavelist[slave].mbx_l > 0)
+         {
+            eedat = ecx_readeeprom2(context, slave, EC_TIMEOUTEEP); /* read mailbox offset */
+            context->slavelist[slave].mbx_ro = (uint16)LO_WORD(etohl(eedat)); /* read mailbox offset */
+            context->slavelist[slave].mbx_rl = (uint16)HI_WORD(etohl(eedat)); /*read mailbox length */
+            if (context->slavelist[slave].mbx_rl == 0)
+            {
+               context->slavelist[slave].mbx_rl = context->slavelist[slave].mbx_l;
+            }
+            ecx_readeeprom1(context, slave, ECT_SII_MBXPROTO);
+         }
+         configadr = context->slavelist[slave].configadr;
+         val16 = ecx_FPRDw(context->port, configadr, ECT_REG_ESCSUP, EC_TIMEOUTRET3);
+         if ((etohs(val16) & 0x04) > 0)  /* Support DC? */
+         {
+            context->slavelist[slave].hasdc = TRUE;
+         }
+         else
+         {
+            context->slavelist[slave].hasdc = FALSE;
+         }
+         topology = ecx_FPRDw(context->port, configadr, ECT_REG_DLSTAT, EC_TIMEOUTRET3); /* extract topology from DL status */
+         topology = etohs(topology);
+         h = 0;
+         b = 0;
+         if ((topology & 0x0300) == 0x0200) /* port0 open and communication established */
+         {
+            h++;
+            b |= 0x01;
+         }
+         if ((topology & 0x0c00) == 0x0800) /* port1 open and communication established */
+         {
+            h++;
+            b |= 0x02;
+         }
+         if ((topology & 0x3000) == 0x2000) /* port2 open and communication established */
+         {
+            h++;
+            b |= 0x04;
+         }
+         if ((topology & 0xc000) == 0x8000) /* port3 open and communication established */
+         {
+            h++;
+            b |= 0x08;
+         }
+         /* ptype = Physical type*/
+         val16 = ecx_FPRDw(context->port, configadr, ECT_REG_PORTDES, EC_TIMEOUTRET3);
+         context->slavelist[slave].ptype = LO_BYTE(etohs(val16));
+         context->slavelist[slave].topology = h;
+         context->slavelist[slave].activeports = b;
+         /* 0=no links, not possible             */
+         /* 1=1 link  , end of line              */
+         /* 2=2 links , one before and one after */
+         /* 3=3 links , split point              */
+         /* 4=4 links , cross point              */
+         /* search for parent */
+         context->slavelist[slave].parent = 0; /* parent is master */
+         if (slave > 1)
+         {
+            topoc = 0;
+            slavec = slave - 1;
+            do
+            {
+               topology = context->slavelist[slavec].topology;
+               if (topology == 1)
+               {
+                  topoc--; /* endpoint found */
+               }
+               if (topology == 3)
+               {
+                  topoc++; /* split found */
+               }
+               if (topology == 4)
+               {
+                  topoc += 2; /* cross found */
+               }
+               if (((topoc >= 0) && (topology > 1)) ||
+                   (slavec == 1)) /* parent found */
+               {
+                  context->slavelist[slave].parent = slavec;
+                  slavec = 1;
+               }
+               slavec--;
+            }
+            while (slavec > 0);
+         }
+         (void)ecx_statecheck(context, slave, EC_STATE_INIT,  EC_TIMEOUTSTATE); //* check state change Init */
+
+         /* set default mailbox configuration if slave has mailbox */
+         if (context->slavelist[slave].mbx_l>0)
+         {
+            context->slavelist[slave].SMtype[0] = 1;
+            context->slavelist[slave].SMtype[1] = 2;
+            context->slavelist[slave].SMtype[2] = 3;
+            context->slavelist[slave].SMtype[3] = 4;
+            context->slavelist[slave].SM[0].StartAddr = htoes(context->slavelist[slave].mbx_wo);
+            context->slavelist[slave].SM[0].SMlength = htoes(context->slavelist[slave].mbx_l);
+            context->slavelist[slave].SM[0].SMflags = htoel(EC_DEFAULTMBXSM0);
+            context->slavelist[slave].SM[1].StartAddr = htoes(context->slavelist[slave].mbx_ro);
+            context->slavelist[slave].SM[1].SMlength = htoes(context->slavelist[slave].mbx_rl);
+            context->slavelist[slave].SM[1].SMflags = htoel(EC_DEFAULTMBXSM1);
+            eedat = ecx_readeeprom2(context, slave, EC_TIMEOUTEEP);
+            context->slavelist[slave].mbx_proto = etohl(eedat);
+         }
+         cindex = 0;
+         /* use configuration table ? */
+         if (usetable == 1)
+         {
+            cindex = ecx_config_from_table(context, slave);
+         }
+         /* slave not in configuration table, find out via SII */
+         if (!cindex && !ecx_lookup_prev_sii(context, slave))
+         {
+            ssigen = ecx_siifind(context, slave, ECT_SII_GENERAL);
+            /* SII general section */
+            if (ssigen)
+            {
+               context->slavelist[slave].CoEdetails = ecx_siigetbyte(context, slave, ssigen + 0x07);
+               context->slavelist[slave].FoEdetails = ecx_siigetbyte(context, slave, ssigen + 0x08);
+               context->slavelist[slave].EoEdetails = ecx_siigetbyte(context, slave, ssigen + 0x09);
+               context->slavelist[slave].SoEdetails = ecx_siigetbyte(context, slave, ssigen + 0x0a);
+               if((ecx_siigetbyte(context, slave, ssigen + 0x0d) & 0x02) > 0)
+               {
+                  context->slavelist[slave].blockLRW = 1;
+                  context->slavelist[0].blockLRW++;
+               }
+               context->slavelist[slave].Ebuscurrent = ecx_siigetbyte(context, slave, ssigen + 0x0e);
+               context->slavelist[slave].Ebuscurrent += ecx_siigetbyte(context, slave, ssigen + 0x0f) << 8;
+               context->slavelist[0].Ebuscurrent += context->slavelist[slave].Ebuscurrent;
+            }
+            /* SII strings section */
+            if (ecx_siifind(context, slave, ECT_SII_STRING) > 0)
+            {
+               ecx_siistring(context, context->slavelist[slave].name, slave, 1);
+            }
+            /* no name for slave found, use constructed name */
+            else
+            {
+               sprintf(context->slavelist[slave].name, "? M:%8.8x I:%8.8x",
+                       (unsigned int)context->slavelist[slave].eep_man,
+                       (unsigned int)context->slavelist[slave].eep_id);
+            }
+            /* SII SM section */
+            nSM = ecx_siiSM(context, slave, context->eepSM);
+            if (nSM>0)
+            {
+               context->slavelist[slave].SM[0].StartAddr = htoes(context->eepSM->PhStart);
+               context->slavelist[slave].SM[0].SMlength = htoes(context->eepSM->Plength);
+               context->slavelist[slave].SM[0].SMflags =
+                  htoel((context->eepSM->Creg) + (context->eepSM->Activate << 16));
+               SMc = 1;
+               while ((SMc < EC_MAXSM) &&  ecx_siiSMnext(context, slave, context->eepSM, SMc))
+               {
+                  context->slavelist[slave].SM[SMc].StartAddr = htoes(context->eepSM->PhStart);
+                  context->slavelist[slave].SM[SMc].SMlength = htoes(context->eepSM->Plength);
+                  context->slavelist[slave].SM[SMc].SMflags =
+                     htoel((context->eepSM->Creg) + (context->eepSM->Activate << 16));
+                  SMc++;
+               }
+            }
+            /* SII FMMU section */
+            if (ecx_siiFMMU(context, slave, context->eepFMMU))
+            {
+               if (context->eepFMMU->FMMU0 !=0xff)
+               {
+                  context->slavelist[slave].FMMU0func = context->eepFMMU->FMMU0;
+               }
+               if (context->eepFMMU->FMMU1 !=0xff)
+               {
+                  context->slavelist[slave].FMMU1func = context->eepFMMU->FMMU1;
+               }
+               if (context->eepFMMU->FMMU2 !=0xff)
+               {
+                  context->slavelist[slave].FMMU2func = context->eepFMMU->FMMU2;
+               }
+               if (context->eepFMMU->FMMU3 !=0xff)
+               {
+                  context->slavelist[slave].FMMU3func = context->eepFMMU->FMMU3;
+               }
+            }
+         }
+
+         if (context->slavelist[slave].mbx_l > 0)
+         {
+            if (context->slavelist[slave].SM[0].StartAddr == 0x0000) /* should never happen */
+            {
+               EC_PRINT("Slave %d has no proper mailbox in configuration, try default.\n", slave);
+               context->slavelist[slave].SM[0].StartAddr = htoes(0x1000);
+               context->slavelist[slave].SM[0].SMlength = htoes(0x0080);
+               context->slavelist[slave].SM[0].SMflags = htoel(EC_DEFAULTMBXSM0);
+               context->slavelist[slave].SMtype[0] = 1;
+            }
+            if (context->slavelist[slave].SM[1].StartAddr == 0x0000) /* should never happen */
+            {
+               EC_PRINT("Slave %d has no proper mailbox out configuration, try default.\n", slave);
+               context->slavelist[slave].SM[1].StartAddr = htoes(0x1080);
+               context->slavelist[slave].SM[1].SMlength = htoes(0x0080);
+               context->slavelist[slave].SM[1].SMflags = htoel(EC_DEFAULTMBXSM1);
+               context->slavelist[slave].SMtype[1] = 2;
+            }
+            /* program SM0 mailbox in and SM1 mailbox out for slave */
+            /* writing both SM in one datagram will solve timing issue in old NETX */
+            ecx_FPWR(context->port, configadr, ECT_REG_SM0, sizeof(ec_smt) * 2,
+               &(context->slavelist[slave].SM[0]), EC_TIMEOUTRET3);
+         }
+         /* some slaves need eeprom available to PDI in init->preop transition */
+         ecx_eeprom2pdi(context, slave);
+         /* request pre_op for slave */
+         ecx_FPWRw(context->port, configadr, ECT_REG_ALCTL, htoes(EC_STATE_PRE_OP | EC_STATE_ACK) , EC_TIMEOUTRET3); /* set preop status */
+      }
+   }
+   return wkc;
+}
+
+/* If slave has SII mapping and same slave ID done before, use previous mapping.
+ * This is safe because SII mapping is constant for same slave ID.
+ */
+static int ecx_lookup_mapping(ecx_contextt *context, uint16 slave, int *Osize, int *Isize)
+{
+   int i, nSM;
+   if ((slave > 1) && (*(context->slavecount) > 0))
+   {
+      i = 1;
+      while(((context->slavelist[i].eep_man != context->slavelist[slave].eep_man) ||
+             (context->slavelist[i].eep_id  != context->slavelist[slave].eep_id ) ||
+             (context->slavelist[i].eep_rev != context->slavelist[slave].eep_rev)) &&
+            (i < slave))
+      {
+         i++;
+      }
+      if(i < slave)
+      {
+         for( nSM=0 ; nSM < EC_MAXSM ; nSM++ )
+         {
+            context->slavelist[slave].SM[nSM].SMlength = context->slavelist[i].SM[nSM].SMlength;
+            context->slavelist[slave].SMtype[nSM] = context->slavelist[i].SMtype[nSM];
+         }
+         *Osize = context->slavelist[i].Obits;
+         *Isize = context->slavelist[i].Ibits;
+         context->slavelist[slave].Obits = *Osize;
+         context->slavelist[slave].Ibits = *Isize;
+         EC_PRINT("Copy mapping slave %d from %d.\n", slave, i);
+         return 1;
+      }
+   }
+   return 0;
+}
+
+static int ecx_map_coe_soe(ecx_contextt *context, uint16 slave, int thread_n)
+{
+   int Isize, Osize;
+   int rval;
+
+   ecx_statecheck(context, slave, EC_STATE_PRE_OP, EC_TIMEOUTSTATE); /* check state change pre-op */
+
+   EC_PRINT(" >Slave %d, configadr %x, state %2.2x\n",
+            slave, context->slavelist[slave].configadr, context->slavelist[slave].state);
+
+   /* execute special slave configuration hook Pre-Op to Safe-OP */
+   if(context->slavelist[slave].PO2SOconfig) /* only if registered */
+   {
+      context->slavelist[slave].PO2SOconfig(slave);
+   }
+   /* if slave not found in configlist find IO mapping in slave self */
+   if (!context->slavelist[slave].configindex)
+   {
+      Isize = 0;
+      Osize = 0;
+      if (context->slavelist[slave].mbx_proto & ECT_MBXPROT_COE) /* has CoE */
+      {
+         rval = 0;
+         if (context->slavelist[slave].CoEdetails & ECT_COEDET_SDOCA) /* has Complete Access */
+         {
+            /* read PDO mapping via CoE and use Complete Access */
+            rval = ecx_readPDOmapCA(context, slave, thread_n, &Osize, &Isize);
+         }
+         if (!rval) /* CA not available or not succeeded */
+         {
+            /* read PDO mapping via CoE */
+            rval = ecx_readPDOmap(context, slave, &Osize, &Isize);
+         }
+         EC_PRINT("  CoE Osize:%d Isize:%d\n", Osize, Isize);
+      }
+      if ((!Isize && !Osize) && (context->slavelist[slave].mbx_proto & ECT_MBXPROT_SOE)) /* has SoE */
+      {
+         /* read AT / MDT mapping via SoE */
+         rval = ecx_readIDNmap(context, slave, &Osize, &Isize);
+         context->slavelist[slave].SM[2].SMlength = htoes((Osize + 7) / 8);
+         context->slavelist[slave].SM[3].SMlength = htoes((Isize + 7) / 8);
+         EC_PRINT("  SoE Osize:%d Isize:%d\n", Osize, Isize);
+      }
+      context->slavelist[slave].Obits = Osize;
+      context->slavelist[slave].Ibits = Isize;
+   }
+
+   return 1;
+}
+
+static int ecx_map_sii(ecx_contextt *context, uint16 slave)
+{
+   int Isize, Osize;
+   int nSM;
+   ec_eepromPDOt eepPDO;
+
+   Osize = context->slavelist[slave].Obits;
+   Isize = context->slavelist[slave].Ibits;
+
+   if (!Isize && !Osize) /* find PDO in previous slave with same ID */
+   {
+      (void)ecx_lookup_mapping(context, slave, &Osize, &Isize);
+   }
+   if (!Isize && !Osize) /* find PDO mapping by SII */
+   {
+      memset(&eepPDO, 0, sizeof(eepPDO));
+      Isize = (int)ecx_siiPDO(context, slave, &eepPDO, 0);
+      EC_PRINT("  SII Isize:%d\n", Isize);
+      for( nSM=0 ; nSM < EC_MAXSM ; nSM++ )
+      {
+         if (eepPDO.SMbitsize[nSM] > 0)
+         {
+            context->slavelist[slave].SM[nSM].SMlength =  htoes((eepPDO.SMbitsize[nSM] + 7) / 8);
+            context->slavelist[slave].SMtype[nSM] = 4;
+            EC_PRINT("    SM%d length %d\n", nSM, eepPDO.SMbitsize[nSM]);
+         }
+      }
+      Osize = (int)ecx_siiPDO(context, slave, &eepPDO, 1);
+      EC_PRINT("  SII Osize:%d\n", Osize);
+      for( nSM=0 ; nSM < EC_MAXSM ; nSM++ )
+      {
+         if (eepPDO.SMbitsize[nSM] > 0)
+         {
+            context->slavelist[slave].SM[nSM].SMlength =  htoes((eepPDO.SMbitsize[nSM] + 7) / 8);
+            context->slavelist[slave].SMtype[nSM] = 3;
+            EC_PRINT("    SM%d length %d\n", nSM, eepPDO.SMbitsize[nSM]);
+         }
+      }
+   }
+   context->slavelist[slave].Obits = Osize;
+   context->slavelist[slave].Ibits = Isize;
+   EC_PRINT("     ISIZE:%d %d OSIZE:%d\n",
+      context->slavelist[slave].Ibits, Isize,context->slavelist[slave].Obits);
+
+   return 1;
+}
+
+static int ecx_map_sm(ecx_contextt *context, uint16 slave)
+{
+   uint16 configadr;
+   int nSM;
+
+   configadr = context->slavelist[slave].configadr;
+
+   EC_PRINT("  SM programming\n");
+   if (!context->slavelist[slave].mbx_l && context->slavelist[slave].SM[0].StartAddr)
+   {
+      ecx_FPWR(context->port, configadr, ECT_REG_SM0,
+         sizeof(ec_smt), &(context->slavelist[slave].SM[0]), EC_TIMEOUTRET3);
+      EC_PRINT("    SM0 Type:%d StartAddr:%4.4x Flags:%8.8x\n",
+          context->slavelist[slave].SMtype[0],
+          context->slavelist[slave].SM[0].StartAddr,
+          context->slavelist[slave].SM[0].SMflags);
+   }
+   if (!context->slavelist[slave].mbx_l && context->slavelist[slave].SM[1].StartAddr)
+   {
+      ecx_FPWR(context->port, configadr, ECT_REG_SM1,
+         sizeof(ec_smt), &context->slavelist[slave].SM[1], EC_TIMEOUTRET3);
+      EC_PRINT("    SM1 Type:%d StartAddr:%4.4x Flags:%8.8x\n",
+          context->slavelist[slave].SMtype[1],
+          context->slavelist[slave].SM[1].StartAddr,
+          context->slavelist[slave].SM[1].SMflags);
+   }
+   /* program SM2 to SMx */
+   for( nSM = 2 ; nSM < EC_MAXSM ; nSM++ )
+   {
+      if (context->slavelist[slave].SM[nSM].StartAddr)
+      {
+         /* check if SM length is zero -> clear enable flag */
+         if( context->slavelist[slave].SM[nSM].SMlength == 0)
+         {
+            context->slavelist[slave].SM[nSM].SMflags =
+               htoel( etohl(context->slavelist[slave].SM[nSM].SMflags) & EC_SMENABLEMASK);
+         }
+         /* if SM length is non zero always set enable flag */
+         else
+         {
+            context->slavelist[slave].SM[nSM].SMflags =
+               htoel( etohl(context->slavelist[slave].SM[nSM].SMflags) | ~EC_SMENABLEMASK);
+         }
+         ecx_FPWR(context->port, configadr, (uint16)(ECT_REG_SM0 + (nSM * sizeof(ec_smt))),
+            sizeof(ec_smt), &context->slavelist[slave].SM[nSM], EC_TIMEOUTRET3);
+         EC_PRINT("    SM%d Type:%d StartAddr:%4.4x Flags:%8.8x\n", nSM,
+             context->slavelist[slave].SMtype[nSM],
+             context->slavelist[slave].SM[nSM].StartAddr,
+             context->slavelist[slave].SM[nSM].SMflags);
+      }
+   }
+   if (context->slavelist[slave].Ibits > 7)
+   {
+      context->slavelist[slave].Ibytes = (context->slavelist[slave].Ibits + 7) / 8;
+   }
+   if (context->slavelist[slave].Obits > 7)
+   {
+      context->slavelist[slave].Obytes = (context->slavelist[slave].Obits + 7) / 8;
+   }
+
+   return 1;
+}
+
+#if EC_MAX_MAPT > 1
+OSAL_THREAD_FUNC ecx_mapper_thread(void *param)
+{
+   ecx_mapt_t *maptp;
+   maptp = param;
+   ecx_map_coe_soe(maptp->context, maptp->slave, maptp->thread_n);
+   maptp->running = 0;
+}
+
+static int ecx_find_mapt(void)
+{
+   int p;
+   p = 0;
+   while((p < EC_MAX_MAPT) && ecx_mapt[p].running)
+   {
+      p++;
+   }
+   if(p < EC_MAX_MAPT)
+   {
+      return p;
+   }
+   else
+   {
+      return -1;
+   }
+}
+#endif
+
+static int ecx_get_threadcount(void)
+{
+   int thrc, thrn;
+   thrc = 0;
+   for(thrn = 0 ; thrn < EC_MAX_MAPT ; thrn++)
+   {
+      thrc += ecx_mapt[thrn].running;
+   }
+   return thrc;
+}
+
+static void ecx_config_find_mappings(ecx_contextt *context, uint8 group)
+{
+   int thrn, thrc;
+   uint16 slave;
+
+   for (thrn = 0; thrn < EC_MAX_MAPT; thrn++)
+   {
+      ecx_mapt[thrn].running = 0;
+   }
+   /* find CoE and SoE mapping of slaves in multiple threads */
+   for (slave = 1; slave <= *(context->slavecount); slave++)
+   {
+      if (!group || (group == context->slavelist[slave].group))
+      {
+#if EC_MAX_MAPT > 1
+            /* multi-threaded version */
+            while ((thrn = ecx_find_mapt()) < 0)
+            {
+               osal_usleep(1000);
+            }
+            ecx_mapt[thrn].context = context;
+            ecx_mapt[thrn].slave = slave;
+            ecx_mapt[thrn].thread_n = thrn;
+            ecx_mapt[thrn].running = 1;
+            osal_thread_create(&(ecx_threadh[thrn]), 128000,
+               &ecx_mapper_thread, &(ecx_mapt[thrn]));
+#else
+            /* serialised version */
+            ecx_map_coe_soe(context, slave, 0);
+#endif
+      }
+   }
+   /* wait for all threads to finish */
+   do
+   {
+      thrc = ecx_get_threadcount();
+      if (thrc)
+      {
+         osal_usleep(1000);
+      }
+   } while (thrc);
+   /* find SII mapping of slave and program SM */
+   for (slave = 1; slave <= *(context->slavecount); slave++)
+   {
+      if (!group || (group == context->slavelist[slave].group))
+      {
+         ecx_map_sii(context, slave);
+         ecx_map_sm(context, slave);
+      }
+   }
+}
+
+static void ecx_config_create_input_mappings(ecx_contextt *context, void *pIOmap, 
+   uint8 group, int16 slave, uint32 * LogAddr, uint8 * BitPos)
+{
+   int BitCount = 0;
+   int ByteCount = 0;
+   int FMMUsize = 0;
+   int FMMUdone = 0;
+   uint8 SMc = 0;
+   uint16 EndAddr;
+   uint16 SMlength;
+   uint16 configadr;
+   uint8 FMMUc;
+
+   EC_PRINT(" =Slave %d, INPUT MAPPING\n", slave);
+
+   configadr = context->slavelist[slave].configadr;
+   FMMUc = context->slavelist[slave].FMMUunused;
+   if (context->slavelist[slave].Obits) /* find free FMMU */
+   {
+      while (context->slavelist[slave].FMMU[FMMUc].LogStart)
+      {
+         FMMUc++;
+      }
+   }
+   /* search for SM that contribute to the input mapping */
+   while ((SMc < (EC_MAXSM - 1)) && (FMMUdone < ((context->slavelist[slave].Ibits + 7) / 8)))
+   {
+      EC_PRINT("    FMMU %d\n", FMMUc);
+      while ((SMc < (EC_MAXSM - 1)) && (context->slavelist[slave].SMtype[SMc] != 4))
+      {
+         SMc++;
+      }
+      EC_PRINT("      SM%d\n", SMc);
+      context->slavelist[slave].FMMU[FMMUc].PhysStart =
+         context->slavelist[slave].SM[SMc].StartAddr;
+      SMlength = etohs(context->slavelist[slave].SM[SMc].SMlength);
+      ByteCount += SMlength;
+      BitCount += SMlength * 8;
+      EndAddr = etohs(context->slavelist[slave].SM[SMc].StartAddr) + SMlength;
+      while ((BitCount < context->slavelist[slave].Ibits) && (SMc < (EC_MAXSM - 1))) /* more SM for input */
+      {
+         SMc++;
+         while ((SMc < (EC_MAXSM - 1)) && (context->slavelist[slave].SMtype[SMc] != 4))
+         {
+            SMc++;
+         }
+         /* if addresses from more SM connect use one FMMU otherwise break up in multiple FMMU */
+         if (etohs(context->slavelist[slave].SM[SMc].StartAddr) > EndAddr)
+         {
+            break;
+         }
+         EC_PRINT("      SM%d\n", SMc);
+         SMlength = etohs(context->slavelist[slave].SM[SMc].SMlength);
+         ByteCount += SMlength;
+         BitCount += SMlength * 8;
+         EndAddr = etohs(context->slavelist[slave].SM[SMc].StartAddr) + SMlength;
+      }
+
+      /* bit oriented slave */
+      if (!context->slavelist[slave].Ibytes)
+      {
+         context->slavelist[slave].FMMU[FMMUc].LogStart = htoel(*LogAddr);
+         context->slavelist[slave].FMMU[FMMUc].LogStartbit = *BitPos;
+         *BitPos += context->slavelist[slave].Ibits - 1;
+         if (*BitPos > 7)
+         {
+            *LogAddr += 1;
+            *BitPos -= 8;
+         }
+         FMMUsize = *LogAddr - etohl(context->slavelist[slave].FMMU[FMMUc].LogStart) + 1;
+         context->slavelist[slave].FMMU[FMMUc].LogLength = htoes(FMMUsize);
+         context->slavelist[slave].FMMU[FMMUc].LogEndbit = *BitPos;
+         *BitPos += 1;
+         if (*BitPos > 7)
+         {
+            *LogAddr += 1;
+            *BitPos -= 8;
+         }
+      }
+      /* byte oriented slave */
+      else
+      {
+         if (*BitPos)
+         {
+            *LogAddr += 1;
+            *BitPos = 0;
+         }
+         context->slavelist[slave].FMMU[FMMUc].LogStart = htoel(*LogAddr);
+         context->slavelist[slave].FMMU[FMMUc].LogStartbit = *BitPos;
+         *BitPos = 7;
+         FMMUsize = ByteCount;
+         if ((FMMUsize + FMMUdone)> (int)context->slavelist[slave].Ibytes)
+         {
+            FMMUsize = context->slavelist[slave].Ibytes - FMMUdone;
+         }
+         *LogAddr += FMMUsize;
+         context->slavelist[slave].FMMU[FMMUc].LogLength = htoes(FMMUsize);
+         context->slavelist[slave].FMMU[FMMUc].LogEndbit = *BitPos;
+         *BitPos = 0;
+      }
+      FMMUdone += FMMUsize;
+      if (context->slavelist[slave].FMMU[FMMUc].LogLength)
+      {
+         context->slavelist[slave].FMMU[FMMUc].PhysStartBit = 0;
+         context->slavelist[slave].FMMU[FMMUc].FMMUtype = 1;
+         context->slavelist[slave].FMMU[FMMUc].FMMUactive = 1;
+         /* program FMMU for input */
+         ecx_FPWR(context->port, configadr, ECT_REG_FMMU0 + (sizeof(ec_fmmut) * FMMUc),
+            sizeof(ec_fmmut), &(context->slavelist[slave].FMMU[FMMUc]), EC_TIMEOUTRET3);
+         /* add one for an input FMMU */
+         context->grouplist[group].inputsWKC++;
+      }
+      if (!context->slavelist[slave].inputs)
+      {
+         if (group)
+         {
+            context->slavelist[slave].inputs =
+               (uint8 *)(pIOmap) + 
+               etohl(context->slavelist[slave].FMMU[FMMUc].LogStart) - 
+               context->grouplist[group].logstartaddr;
+         }
+         else
+         {
+            context->slavelist[slave].inputs =
+               (uint8 *)(pIOmap) +
+               etohl(context->slavelist[slave].FMMU[FMMUc].LogStart);
+         }
+         context->slavelist[slave].Istartbit =
+            context->slavelist[slave].FMMU[FMMUc].LogStartbit;
+         EC_PRINT("    Inputs %p startbit %d\n",
+            context->slavelist[slave].inputs,
+            context->slavelist[slave].Istartbit);
+      }
+      FMMUc++;
+   }
+   context->slavelist[slave].FMMUunused = FMMUc;
+}
+
+static void ecx_config_create_output_mappings(ecx_contextt *context, void *pIOmap, 
+   uint8 group, int16 slave, uint32 * LogAddr, uint8 * BitPos)
+{
+   int BitCount = 0;
+   int ByteCount = 0;
+   int FMMUsize = 0;
+   int FMMUdone = 0;
+   uint8 SMc = 0;
+   uint16 EndAddr;
+   uint16 SMlength;
+   uint16 configadr;
+   uint8 FMMUc;
+
+   EC_PRINT("  OUTPUT MAPPING\n");
+
+   FMMUc = context->slavelist[slave].FMMUunused;
+   configadr = context->slavelist[slave].configadr;
+
+   /* search for SM that contribute to the output mapping */
+   while ((SMc < (EC_MAXSM - 1)) && (FMMUdone < ((context->slavelist[slave].Obits + 7) / 8)))
+   {
+      EC_PRINT("    FMMU %d\n", FMMUc);
+      while ((SMc < (EC_MAXSM - 1)) && (context->slavelist[slave].SMtype[SMc] != 3))
+      {
+         SMc++;
+      }
+      EC_PRINT("      SM%d\n", SMc);
+      context->slavelist[slave].FMMU[FMMUc].PhysStart =
+         context->slavelist[slave].SM[SMc].StartAddr;
+      SMlength = etohs(context->slavelist[slave].SM[SMc].SMlength);
+      ByteCount += SMlength;
+      BitCount += SMlength * 8;
+      EndAddr = etohs(context->slavelist[slave].SM[SMc].StartAddr) + SMlength;
+      while ((BitCount < context->slavelist[slave].Obits) && (SMc < (EC_MAXSM - 1))) /* more SM for output */
+      {
+         SMc++;
+         while ((SMc < (EC_MAXSM - 1)) && (context->slavelist[slave].SMtype[SMc] != 3))
+         {
+            SMc++;
+         }
+         /* if addresses from more SM connect use one FMMU otherwise break up in multiple FMMU */
+         if (etohs(context->slavelist[slave].SM[SMc].StartAddr) > EndAddr)
+         {
+            break;
+         }
+         EC_PRINT("      SM%d\n", SMc);
+         SMlength = etohs(context->slavelist[slave].SM[SMc].SMlength);
+         ByteCount += SMlength;
+         BitCount += SMlength * 8;
+         EndAddr = etohs(context->slavelist[slave].SM[SMc].StartAddr) + SMlength;
+      }
+
+      /* bit oriented slave */
+      if (!context->slavelist[slave].Obytes)
+      {
+         context->slavelist[slave].FMMU[FMMUc].LogStart = htoel(*LogAddr);
+         context->slavelist[slave].FMMU[FMMUc].LogStartbit = *BitPos;
+         *BitPos += context->slavelist[slave].Obits - 1;
+         if (*BitPos > 7)
+         {
+            *LogAddr += 1;
+            *BitPos -= 8;
+         }
+         FMMUsize = *LogAddr - etohl(context->slavelist[slave].FMMU[FMMUc].LogStart) + 1;
+         context->slavelist[slave].FMMU[FMMUc].LogLength = htoes(FMMUsize);
+         context->slavelist[slave].FMMU[FMMUc].LogEndbit = *BitPos;
+         *BitPos += 1;
+         if (*BitPos > 7)
+         {
+            *LogAddr += 1;
+            *BitPos -= 8;
+         }
+      }
+      /* byte oriented slave */
+      else
+      {
+         if (*BitPos)
+         {
+            *LogAddr += 1;
+            *BitPos = 0;
+         }
+         context->slavelist[slave].FMMU[FMMUc].LogStart = htoel(*LogAddr);
+         context->slavelist[slave].FMMU[FMMUc].LogStartbit = *BitPos;
+         *BitPos = 7;
+         FMMUsize = ByteCount;
+         if ((FMMUsize + FMMUdone)> (int)context->slavelist[slave].Obytes)
+         {
+            FMMUsize = context->slavelist[slave].Obytes - FMMUdone;
+         }
+         *LogAddr += FMMUsize;
+         context->slavelist[slave].FMMU[FMMUc].LogLength = htoes(FMMUsize);
+         context->slavelist[slave].FMMU[FMMUc].LogEndbit = *BitPos;
+         *BitPos = 0;
+      }
+      FMMUdone += FMMUsize;
+      context->slavelist[slave].FMMU[FMMUc].PhysStartBit = 0;
+      context->slavelist[slave].FMMU[FMMUc].FMMUtype = 2;
+      context->slavelist[slave].FMMU[FMMUc].FMMUactive = 1;
+      /* program FMMU for output */
+      ecx_FPWR(context->port, configadr, ECT_REG_FMMU0 + (sizeof(ec_fmmut) * FMMUc),
+         sizeof(ec_fmmut), &(context->slavelist[slave].FMMU[FMMUc]), EC_TIMEOUTRET3);
+      context->grouplist[group].outputsWKC++;
+      if (!context->slavelist[slave].outputs)
+      {
+         if (group)
+         {
+            context->slavelist[slave].outputs =
+               (uint8 *)(pIOmap) + 
+               etohl(context->slavelist[slave].FMMU[FMMUc].LogStart) - 
+               context->grouplist[group].logstartaddr;
+         }
+         else
+         {
+            context->slavelist[slave].outputs =
+               (uint8 *)(pIOmap) + 
+               etohl(context->slavelist[slave].FMMU[FMMUc].LogStart);
+         }
+         context->slavelist[slave].Ostartbit =
+            context->slavelist[slave].FMMU[FMMUc].LogStartbit;
+         EC_PRINT("    slave %d Outputs %p startbit %d\n",
+            slave,
+            context->slavelist[slave].outputs,
+            context->slavelist[slave].Ostartbit);
+      }
+      FMMUc++;
+   }
+   context->slavelist[slave].FMMUunused = FMMUc;
+}
+
+/** Map all PDOs in one group of slaves to IOmap with Outputs/Inputs
+* in sequential order (legacy SOEM way).
+*
+ *
+ * @param[in]  context    = context struct
+ * @param[out] pIOmap     = pointer to IOmap
+ * @param[in]  group      = group to map, 0 = all groups
+ * @return IOmap size
+ */
+int ecx_config_map_group(ecx_contextt *context, void *pIOmap, uint8 group)
+{
+   uint16 slave, configadr;
+   uint8 BitPos;
+   uint32 LogAddr = 0;
+   uint32 oLogAddr = 0;
+   uint32 diff;
+   uint16 currentsegment = 0;
+   uint32 segmentsize = 0;
+
+   if ((*(context->slavecount) > 0) && (group < context->maxgroup))
+   {
+      EC_PRINT("ec_config_map_group IOmap:%p group:%d\n", pIOmap, group);
+      LogAddr = context->grouplist[group].logstartaddr;
+      oLogAddr = LogAddr;
+      BitPos = 0;
+      context->grouplist[group].nsegments = 0;
+      context->grouplist[group].outputsWKC = 0;
+      context->grouplist[group].inputsWKC = 0;
+
+      /* Find mappings and program syncmanagers */
+      ecx_config_find_mappings(context, group);
+
+      /* do output mapping of slave and program FMMUs */
+      for (slave = 1; slave <= *(context->slavecount); slave++)
+      {
+         configadr = context->slavelist[slave].configadr;
+
+         if (!group || (group == context->slavelist[slave].group))
+         {
+            /* create output mapping */
+            if (context->slavelist[slave].Obits)
+            {
+               ecx_config_create_output_mappings (context, pIOmap, group, slave, &LogAddr, &BitPos);
+               diff = LogAddr - oLogAddr;
+               oLogAddr = LogAddr;
+               if ((segmentsize + diff) > (EC_MAXLRWDATA - EC_FIRSTDCDATAGRAM))
+               {
+                  context->grouplist[group].IOsegment[currentsegment] = segmentsize;
+                  if (currentsegment < (EC_MAXIOSEGMENTS - 1))
+                  {
+                     currentsegment++;
+                     segmentsize = diff;
+                  }
+               }
+               else
+               {
+                  segmentsize += diff;
+               }
+            }
+         }
+      }
+      if (BitPos)
+      {
+         LogAddr++;
+         oLogAddr = LogAddr;
+         BitPos = 0;
+         if ((segmentsize + 1) > (EC_MAXLRWDATA - EC_FIRSTDCDATAGRAM))
+         {
+            context->grouplist[group].IOsegment[currentsegment] = segmentsize;
+            if (currentsegment < (EC_MAXIOSEGMENTS - 1))
+            {
+               currentsegment++;
+               segmentsize = 1;
+            }
+         }
+         else
+         {
+            segmentsize += 1;
+         }
+      }
+      context->grouplist[group].outputs = pIOmap;
+      context->grouplist[group].Obytes = LogAddr - context->grouplist[group].logstartaddr;
+      context->grouplist[group].nsegments = currentsegment + 1;
+      context->grouplist[group].Isegment = currentsegment;
+      context->grouplist[group].Ioffset = segmentsize;
+      if (!group)
+      {
+         context->slavelist[0].outputs = pIOmap;
+         context->slavelist[0].Obytes = LogAddr - 
+            context->grouplist[group].logstartaddr; /* store output bytes in master record */
+      }
+
+      /* do input mapping of slave and program FMMUs */
+      for (slave = 1; slave <= *(context->slavecount); slave++)
+      {
+         configadr = context->slavelist[slave].configadr;
+         if (!group || (group == context->slavelist[slave].group))
+         {
+            /* create input mapping */
+            if (context->slavelist[slave].Ibits)
+            {
+ 
+               ecx_config_create_input_mappings(context, pIOmap, group, slave, &LogAddr, &BitPos);
+               diff = LogAddr - oLogAddr;
+               oLogAddr = LogAddr;
+               if ((segmentsize + diff) > (EC_MAXLRWDATA - EC_FIRSTDCDATAGRAM))
+               {
+                  context->grouplist[group].IOsegment[currentsegment] = segmentsize;
+                  if (currentsegment < (EC_MAXIOSEGMENTS - 1))
+                  {
+                     currentsegment++;
+                     segmentsize = diff;
+                  }
+               }
+               else
+               {
+                  segmentsize += diff;
+               }
+            }
+
+            ecx_eeprom2pdi(context, slave); /* set Eeprom control to PDI */
+            ecx_FPWRw(context->port, configadr, ECT_REG_ALCTL, htoes(EC_STATE_SAFE_OP) , EC_TIMEOUTRET3); /* set safeop status */
+
+            if (context->slavelist[slave].blockLRW)
+            {
+               context->grouplist[group].blockLRW++;
+            }
+            context->grouplist[group].Ebuscurrent += context->slavelist[slave].Ebuscurrent;
+         }
+      }
+      if (BitPos)
+      {
+         LogAddr++;
+         oLogAddr = LogAddr;
+         BitPos = 0;
+         if ((segmentsize + 1) > (EC_MAXLRWDATA - EC_FIRSTDCDATAGRAM))
+         {
+            context->grouplist[group].IOsegment[currentsegment] = segmentsize;
+            if (currentsegment < (EC_MAXIOSEGMENTS - 1))
+            {
+               currentsegment++;
+               segmentsize = 1;
+            }
+         }
+         else
+         {
+            segmentsize += 1;
+         }
+      }
+      context->grouplist[group].IOsegment[currentsegment] = segmentsize;
+      context->grouplist[group].nsegments = currentsegment + 1;
+      context->grouplist[group].inputs = (uint8 *)(pIOmap) + context->grouplist[group].Obytes;
+      context->grouplist[group].Ibytes = LogAddr - 
+         context->grouplist[group].logstartaddr - 
+         context->grouplist[group].Obytes;
+      if (!group)
+      {
+         context->slavelist[0].inputs = (uint8 *)(pIOmap) + context->slavelist[0].Obytes;
+         context->slavelist[0].Ibytes = LogAddr - 
+            context->grouplist[group].logstartaddr - 
+            context->slavelist[0].Obytes; /* store input bytes in master record */
+      }
+
+      EC_PRINT("IOmapSize %d\n", LogAddr - context->grouplist[group].logstartaddr);
+
+      return (LogAddr - context->grouplist[group].logstartaddr);
+   }
+
+   return 0;
+}
+
+/** Map all PDOs in one group of slaves to IOmap with Outputs/Inputs
+ * overlapping. NOTE: Must use this for TI ESC when using LRW.
+ *
+ * @param[in]  context    = context struct
+ * @param[out] pIOmap     = pointer to IOmap
+ * @param[in]  group      = group to map, 0 = all groups
+ * @return IOmap size
+ */
+int ecx_config_overlap_map_group(ecx_contextt *context, void *pIOmap, uint8 group)
+{
+   uint16 slave, configadr;
+   uint8 BitPos;
+   uint32 mLogAddr = 0;
+   uint32 siLogAddr = 0;
+   uint32 soLogAddr = 0;
+   uint32 tempLogAddr;
+   uint32 diff;
+   uint16 currentsegment = 0;
+   uint32 segmentsize = 0;
+
+   if ((*(context->slavecount) > 0) && (group < context->maxgroup))
+   {
+      EC_PRINT("ec_config_map_group IOmap:%p group:%d\n", pIOmap, group);
+      mLogAddr = context->grouplist[group].logstartaddr;
+      siLogAddr = mLogAddr;
+      soLogAddr = mLogAddr;
+      BitPos = 0;
+      context->grouplist[group].nsegments = 0;
+      context->grouplist[group].outputsWKC = 0;
+      context->grouplist[group].inputsWKC = 0;
+
+      /* Find mappings and program syncmanagers */
+      ecx_config_find_mappings(context, group);
+      
+      /* do IO mapping of slave and program FMMUs */
+      for (slave = 1; slave <= *(context->slavecount); slave++)
+      {
+         configadr = context->slavelist[slave].configadr;
+         siLogAddr = soLogAddr = mLogAddr;
+
+         if (!group || (group == context->slavelist[slave].group))
+         {
+            /* create output mapping */
+            if (context->slavelist[slave].Obits)
+            {
+               
+               ecx_config_create_output_mappings(context, pIOmap, group, 
+                  slave, &soLogAddr, &BitPos);
+               if (BitPos)
+               {
+                  soLogAddr++;
+                  BitPos = 0;
+               }
+            }
+
+            /* create input mapping */
+            if (context->slavelist[slave].Ibits)
+            {
+               ecx_config_create_input_mappings(context, pIOmap, group, 
+                  slave, &siLogAddr, &BitPos);
+               if (BitPos)
+               {
+                  siLogAddr++;
+                  BitPos = 0;
+               }
+            }
+
+            tempLogAddr = (siLogAddr > soLogAddr) ?  siLogAddr : soLogAddr;
+            diff = tempLogAddr - mLogAddr;
+            mLogAddr = tempLogAddr;
+
+            if ((segmentsize + diff) > (EC_MAXLRWDATA - EC_FIRSTDCDATAGRAM))
+            {
+               context->grouplist[group].IOsegment[currentsegment] = segmentsize;
+               if (currentsegment < (EC_MAXIOSEGMENTS - 1))
+               {
+                  currentsegment++;
+                  segmentsize = diff;
+               }
+            }
+            else
+            {
+               segmentsize += diff;
+            }
+
+            ecx_eeprom2pdi(context, slave); /* set Eeprom control to PDI */
+            ecx_FPWRw(context->port, configadr, ECT_REG_ALCTL, htoes(EC_STATE_SAFE_OP), EC_TIMEOUTRET3); /* set safeop status */
+
+            if (context->slavelist[slave].blockLRW)
+            {
+               context->grouplist[group].blockLRW++;
+            }
+            context->grouplist[group].Ebuscurrent += context->slavelist[slave].Ebuscurrent;
+
+         }
+      }
+
+      context->grouplist[group].IOsegment[currentsegment] = segmentsize;
+      context->grouplist[group].nsegments = currentsegment + 1;
+      context->grouplist[group].Isegment = 0;
+      context->grouplist[group].Ioffset = 0;
+
+      context->grouplist[group].Obytes = soLogAddr - context->grouplist[group].logstartaddr;
+      context->grouplist[group].Ibytes = siLogAddr - context->grouplist[group].logstartaddr;
+      context->grouplist[group].outputs = pIOmap;
+      context->grouplist[group].inputs = (uint8 *)pIOmap + context->grouplist[group].Obytes;
+
+      /* Move calculated inputs with OBytes offset*/
+      for (slave = 1; slave <= *(context->slavecount); slave++)
+      {
+         context->slavelist[slave].inputs += context->grouplist[group].Obytes;
+      }
+
+      if (!group)
+      {
+         /* store output bytes in master record */
+         context->slavelist[0].outputs = pIOmap;
+         context->slavelist[0].Obytes = soLogAddr - context->grouplist[group].logstartaddr; 
+         context->slavelist[0].inputs = (uint8 *)pIOmap + context->slavelist[0].Obytes;
+         context->slavelist[0].Ibytes = siLogAddr - context->grouplist[group].logstartaddr;
+      }
+
+      EC_PRINT("IOmapSize %d\n", context->grouplist[group].Obytes + context->grouplist[group].Ibytes);
+
+      return (context->grouplist[group].Obytes + context->grouplist[group].Ibytes);
+   }
+
+   return 0;
+}
+
+
+/** Recover slave.
+ *
+ * @param[in] context = context struct
+ * @param[in] slave   = slave to recover
+ * @param[in] timeout = local timeout f.e. EC_TIMEOUTRET3
+ * @return >0 if successful
+ */
+int ecx_recover_slave(ecx_contextt *context, uint16 slave, int timeout)
+{
+   int rval;
+   int wkc;
+   uint16 ADPh, configadr, readadr;
+
+   rval = 0;
+   configadr = context->slavelist[slave].configadr;
+   ADPh = (uint16)(1 - slave);
+   /* check if we found another slave than the requested */
+   readadr = 0xfffe;
+   wkc = ecx_APRD(context->port, ADPh, ECT_REG_STADR, sizeof(readadr), &readadr, timeout);
+   /* correct slave found, finished */
+   if(readadr == configadr)
+   {
+       return 1;
+   }
+   /* only try if no config address*/
+   if( (wkc > 0) && (readadr == 0))
+   {
+      /* clear possible slaves at EC_TEMPNODE */
+      ecx_FPWRw(context->port, EC_TEMPNODE, ECT_REG_STADR, htoes(0) , 0);
+      /* set temporary node address of slave */
+      if(ecx_APWRw(context->port, ADPh, ECT_REG_STADR, htoes(EC_TEMPNODE) , timeout) <= 0)
+      {
+         ecx_FPWRw(context->port, EC_TEMPNODE, ECT_REG_STADR, htoes(0) , 0);
+         return 0; /* slave fails to respond */
+      }
+
+      context->slavelist[slave].configadr = EC_TEMPNODE; /* temporary config address */
+      ecx_eeprom2master(context, slave); /* set Eeprom control to master */
+
+      /* check if slave is the same as configured before */
+      if ((ecx_FPRDw(context->port, EC_TEMPNODE, ECT_REG_ALIAS, timeout) ==
+             htoes(context->slavelist[slave].aliasadr)) &&
+          (ecx_readeeprom(context, slave, ECT_SII_ID, EC_TIMEOUTEEP) ==
+             htoel(context->slavelist[slave].eep_id)) &&
+          (ecx_readeeprom(context, slave, ECT_SII_MANUF, EC_TIMEOUTEEP) ==
+             htoel(context->slavelist[slave].eep_man)) &&
+          (ecx_readeeprom(context, slave, ECT_SII_REV, EC_TIMEOUTEEP) ==
+             htoel(context->slavelist[slave].eep_rev)))
+      {
+         rval = ecx_FPWRw(context->port, EC_TEMPNODE, ECT_REG_STADR, htoes(configadr) , timeout);
+         context->slavelist[slave].configadr = configadr;
+      }
+      else
+      {
+         /* slave is not the expected one, remove config address*/
+         ecx_FPWRw(context->port, EC_TEMPNODE, ECT_REG_STADR, htoes(0) , timeout);
+         context->slavelist[slave].configadr = configadr;
+      }
+   }
+
+   return rval;
+}
+
+/** Reconfigure slave.
+ *
+ * @param[in] context = context struct
+ * @param[in] slave   = slave to reconfigure
+ * @param[in] timeout = local timeout f.e. EC_TIMEOUTRET3
+ * @return Slave state
+ */
+int ecx_reconfig_slave(ecx_contextt *context, uint16 slave, int timeout)
+{
+   int state, nSM, FMMUc;
+   uint16 configadr;
+
+   configadr = context->slavelist[slave].configadr;
+   if (ecx_FPWRw(context->port, configadr, ECT_REG_ALCTL, htoes(EC_STATE_INIT) , timeout) <= 0)
+   {
+      return 0;
+   }
+   state = 0;
+   ecx_eeprom2pdi(context, slave); /* set Eeprom control to PDI */
+   /* check state change init */
+   state = ecx_statecheck(context, slave, EC_STATE_INIT, EC_TIMEOUTSTATE);
+   if(state == EC_STATE_INIT)
+   {
+      /* program all enabled SM */
+      for( nSM = 0 ; nSM < EC_MAXSM ; nSM++ )
+      {
+         if (context->slavelist[slave].SM[nSM].StartAddr)
+         {
+            ecx_FPWR(context->port, configadr, (uint16)(ECT_REG_SM0 + (nSM * sizeof(ec_smt))),
+               sizeof(ec_smt), &context->slavelist[slave].SM[nSM], timeout);
+         }
+      }
+      ecx_FPWRw(context->port, configadr, ECT_REG_ALCTL, htoes(EC_STATE_PRE_OP) , timeout);
+      state = ecx_statecheck(context, slave, EC_STATE_PRE_OP, EC_TIMEOUTSTATE); /* check state change pre-op */
+      if( state == EC_STATE_PRE_OP)
+      {
+         /* execute special slave configuration hook Pre-Op to Safe-OP */
+         if(context->slavelist[slave].PO2SOconfig) /* only if registered */
+         {
+            context->slavelist[slave].PO2SOconfig(slave);
+         }
+         ecx_FPWRw(context->port, configadr, ECT_REG_ALCTL, htoes(EC_STATE_SAFE_OP) , timeout); /* set safeop status */
+         state = ecx_statecheck(context, slave, EC_STATE_SAFE_OP, EC_TIMEOUTSTATE); /* check state change safe-op */
+         /* program configured FMMU */
+         for( FMMUc = 0 ; FMMUc < context->slavelist[slave].FMMUunused ; FMMUc++ )
+         {
+            ecx_FPWR(context->port, configadr, (uint16)(ECT_REG_FMMU0 + (sizeof(ec_fmmut) * FMMUc)),
+               sizeof(ec_fmmut), &context->slavelist[slave].FMMU[FMMUc], timeout);
+         }
+      }
+   }
+
+   return state;
+}
+
+#ifdef EC_VER1
+/** Enumerate and init all slaves.
+ *
+ * @param[in] usetable     = TRUE when using configtable to init slaves, FALSE otherwise
+ * @return Workcounter of slave discover datagram = number of slaves found
+ * @see ecx_config_init
+ */
+int ec_config_init(uint8 usetable)
+{
+   return ecx_config_init(&ecx_context, usetable);
+}
+
+/** Map all PDOs in one group of slaves to IOmap with Outputs/Inputs
+ * in sequential order (legacy SOEM way).
+ *
+ * @param[out] pIOmap     = pointer to IOmap
+ * @param[in]  group      = group to map, 0 = all groups
+ * @return IOmap size
+ * @see ecx_config_map_group
+ */
+int ec_config_map_group(void *pIOmap, uint8 group)
+{
+   return ecx_config_map_group(&ecx_context, pIOmap, group);
+}
+
+/** Map all PDOs in one group of slaves to IOmap with Outputs/Inputs
+* overlapping. NOTE: Must use this for TI ESC when using LRW.
+*
+* @param[out] pIOmap     = pointer to IOmap
+* @param[in]  group      = group to map, 0 = all groups
+* @return IOmap size
+* @see ecx_config_overlap_map_group
+*/
+int ec_config_overlap_map_group(void *pIOmap, uint8 group)
+{
+   return ecx_config_overlap_map_group(&ecx_context, pIOmap, group);
+}
+
+/** Map all PDOs from slaves to IOmap with Outputs/Inputs
+ * in sequential order (legacy SOEM way).
+ *
+ * @param[out] pIOmap     = pointer to IOmap
+ * @return IOmap size
+ */
+int ec_config_map(void *pIOmap)
+{
+   return ec_config_map_group(pIOmap, 0);
+}
+
+/** Map all PDOs from slaves to IOmap with Outputs/Inputs
+* overlapping. NOTE: Must use this for TI ESC when using LRW.
+*
+* @param[out] pIOmap     = pointer to IOmap
+* @return IOmap size
+*/
+int ec_config_overlap_map(void *pIOmap)
+{
+   return ec_config_overlap_map_group(pIOmap, 0);
+}
+
+/** Enumerate / map and init all slaves.
+ *
+ * @param[in] usetable    = TRUE when using configtable to init slaves, FALSE otherwise
+ * @param[out] pIOmap     = pointer to IOmap
+ * @return Workcounter of slave discover datagram = number of slaves found
+ */
+int ec_config(uint8 usetable, void *pIOmap)
+{
+   int wkc;
+   wkc = ec_config_init(usetable);
+   if (wkc)
+   {
+      ec_config_map(pIOmap);
+   }
+   return wkc;
+}
+
+/** Enumerate / map and init all slaves.
+*
+* @param[in] usetable    = TRUE when using configtable to init slaves, FALSE otherwise
+* @param[out] pIOmap     = pointer to IOmap
+* @return Workcounter of slave discover datagram = number of slaves found
+*/
+int ec_config_overlap(uint8 usetable, void *pIOmap)
+{
+   int wkc;
+   wkc = ec_config_init(usetable);
+   if (wkc)
+   {
+      ec_config_overlap_map(pIOmap);
+   }
+   return wkc;
+}
+
+/** Recover slave.
+ *
+ * @param[in] slave   = slave to recover
+ * @param[in] timeout = local timeout f.e. EC_TIMEOUTRET3
+ * @return >0 if successful
+ * @see ecx_recover_slave
+ */
+int ec_recover_slave(uint16 slave, int timeout)
+{
+   return ecx_recover_slave(&ecx_context, slave, timeout);
+}
+
+/** Reconfigure slave.
+ *
+ * @param[in] slave   = slave to reconfigure
+ * @param[in] timeout = local timeout f.e. EC_TIMEOUTRET3
+ * @return Slave state
+ * @see ecx_reconfig_slave
+ */
+int ec_reconfig_slave(uint16 slave, int timeout)
+{
+   return ecx_reconfig_slave(&ecx_context, slave, timeout);
+}
+#endif
diff -r 000000000000 -r 543d6784d4cc SOEM/ethercatconfig.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SOEM/ethercatconfig.h	Tue Jun 11 10:29:09 2019 +0000
@@ -0,0 +1,44 @@
+/*
+ * Licensed under the GNU General Public License version 2 with exceptions. See
+ * LICENSE file in the project root for full license information
+ */
+
+/** \file
+ * \brief
+ * Headerfile for ethercatconfig.c
+ */
+
+#ifndef _ethercatconfig_
+#define _ethercatconfig_
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#define EC_NODEOFFSET      0x1000
+#define EC_TEMPNODE        0xffff
+
+#ifdef EC_VER1
+int ec_config_init(uint8 usetable);
+int ec_config_map(void *pIOmap);
+int ec_config_overlap_map(void *pIOmap);
+int ec_config_map_group(void *pIOmap, uint8 group);
+int ec_config_overlap_map_group(void *pIOmap, uint8 group);
+int ec_config(uint8 usetable, void *pIOmap);
+int ec_config_overlap(uint8 usetable, void *pIOmap);
+int ec_recover_slave(uint16 slave, int timeout);
+int ec_reconfig_slave(uint16 slave, int timeout);
+#endif
+
+int ecx_config_init(ecx_contextt *context, uint8 usetable);
+int ecx_config_map_group(ecx_contextt *context, void *pIOmap, uint8 group);
+int ecx_config_overlap_map_group(ecx_contextt *context, void *pIOmap, uint8 group);
+int ecx_recover_slave(ecx_contextt *context, uint16 slave, int timeout);
+int ecx_reconfig_slave(ecx_contextt *context, uint16 slave, int timeout);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff -r 000000000000 -r 543d6784d4cc SOEM/ethercatconfiglist.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SOEM/ethercatconfiglist.h	Tue Jun 11 10:29:09 2019 +0000
@@ -0,0 +1,66 @@
+/*
+ * Licensed under the GNU General Public License version 2 with exceptions. See
+ * LICENSE file in the project root for full license information
+ */
+
+/** \file
+ * \brief
+ * DEPRECATED Configuration list of known EtherCAT slave devices.
+ *
+ * If a slave is found in this list it is configured according to the parameters
+ * in the list. Otherwise the configuration info is read directly from the slave
+ * EEPROM (SII or Slave Information Interface).
+ */
+
+#ifndef _ethercatconfiglist_
+#define _ethercatconfiglist_
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/*
+   explanation of dev:
+    1: static device with no IO mapping ie EK1100
+    2: input device no mailbox ie simple IO device
+    3: output device no mailbox
+    4: input device with mailbox configuration
+    5: output device with mailbox configuration
+    6: input/output device no mailbox
+    7: input.output device with mailbox configuration
+*/
+#define EC_CONFIGEND 0xffffffff
+
+ec_configlist_t ec_configlist[] = {
+      {/*Man=*/0x00000000,/*ID=*/0x00000000,/*Name=*/""          ,/*dtype=*/0,/*Ibits=*/ 0,/*Obits=*/ 0,/*SM2a*/     0,/*SM2f*/         0,/*SM3a*/     0,/*SM3f*/         0,/*FM0ac*/0,/*FM1ac*/0},
+      {/*Man=*/0x00000002,/*ID=*/0x044c2c52,/*Name=*/"EK1100"    ,/*dtype=*/1,/*Ibits=*/ 0,/*Obits=*/ 0,/*SM2a*/     0,/*SM2f*/         0,/*SM3a*/     0,/*SM3f*/         0,/*FM0ac*/0,/*FM1ac*/0},
+      {/*Man=*/0x00000002,/*ID=*/0x03ea3052,/*Name=*/"EL1002"    ,/*dtype=*/2,/*Ibits=*/ 2,/*Obits=*/ 0,/*SM2a*/     0,/*SM2f*/         0,/*SM3a*/     0,/*SM3f*/         0,/*FM0ac*/0,/*FM1ac*/0},
+      {/*Man=*/0x00000002,/*ID=*/0x03ec3052,/*Name=*/"EL1004"    ,/*dtype=*/2,/*Ibits=*/ 4,/*Obits=*/ 0,/*SM2a*/     0,/*SM2f*/         0,/*SM3a*/     0,/*SM3f*/         0,/*FM0ac*/0,/*FM1ac*/0},
+      {/*Man=*/0x00000002,/*ID=*/0x03f43052,/*Name=*/"EL1012"    ,/*dtype=*/2,/*Ibits=*/ 2,/*Obits=*/ 0,/*SM2a*/     0,/*SM2f*/         0,/*SM3a*/     0,/*SM3f*/         0,/*FM0ac*/0,/*FM1ac*/0},
+      {/*Man=*/0x00000002,/*ID=*/0x03f63052,/*Name=*/"EL1014"    ,/*dtype=*/2,/*Ibits=*/ 4,/*Obits=*/ 0,/*SM2a*/     0,/*SM2f*/         0,/*SM3a*/     0,/*SM3f*/         0,/*FM0ac*/0,/*FM1ac*/0},
+      {/*Man=*/0x00000002,/*ID=*/0x03fa3052,/*Name=*/"EL1018"    ,/*dtype=*/2,/*Ibits=*/ 8,/*Obits=*/ 0,/*SM2a*/     0,/*SM2f*/         0,/*SM3a*/     0,/*SM3f*/         0,/*FM0ac*/0,/*FM1ac*/0},
+      {/*Man=*/0x00000002,/*ID=*/0x07d23052,/*Name=*/"EL2002"    ,/*dtype=*/3,/*Ibits=*/ 0,/*Obits=*/ 2,/*SM2a*/     0,/*SM2f*/         0,/*SM3a*/     0,/*SM3f*/         0,/*FM0ac*/0,/*FM1ac*/0},
+      {/*Man=*/0x00000002,/*ID=*/0x07d43052,/*Name=*/"EL2004"    ,/*dtype=*/3,/*Ibits=*/ 0,/*Obits=*/ 4,/*SM2a*/     0,/*SM2f*/         0,/*SM3a*/     0,/*SM3f*/         0,/*FM0ac*/0,/*FM1ac*/0},
+      {/*Man=*/0x00000002,/*ID=*/0x07d83052,/*Name=*/"EL2008"    ,/*dtype=*/3,/*Ibits=*/ 0,/*Obits=*/ 8,/*SM2a*/     0,/*SM2f*/         0,/*SM3a*/     0,/*SM3f*/         0,/*FM0ac*/0,/*FM1ac*/0},
+      {/*Man=*/0x00000002,/*ID=*/0x07f03052,/*Name=*/"EL2032"    ,/*dtype=*/6,/*Ibits=*/ 2,/*Obits=*/ 2,/*SM2a*/     0,/*SM2f*/         0,/*SM3a*/     0,/*SM3f*/         0,/*FM0ac*/0,/*FM1ac*/0},
+      {/*Man=*/0x00000002,/*ID=*/0x0c1e3052,/*Name=*/"EL3102"    ,/*dtype=*/4,/*Ibits=*/48,/*Obits=*/ 0,/*SM2a*/0x1000,/*SM2f*/0x00000024,/*SM3a*/0x1100,/*SM3f*/0x00010020,/*FM0ac*/0,/*FM1ac*/1},
+      {/*Man=*/0x00000002,/*ID=*/0x0c283052,/*Name=*/"EL3112"    ,/*dtype=*/4,/*Ibits=*/48,/*Obits=*/ 0,/*SM2a*/0x1000,/*SM2f*/0x00000024,/*SM3a*/0x1100,/*SM3f*/0x00010020,/*FM0ac*/0,/*FM1ac*/1},
+      {/*Man=*/0x00000002,/*ID=*/0x0c323052,/*Name=*/"EL3122"    ,/*dtype=*/4,/*Ibits=*/48,/*Obits=*/ 0,/*SM2a*/0x1000,/*SM2f*/0x00000024,/*SM3a*/0x1100,/*SM3f*/0x00010020,/*FM0ac*/0,/*FM1ac*/1},
+      {/*Man=*/0x00000002,/*ID=*/0x0c463052,/*Name=*/"EL3142"    ,/*dtype=*/4,/*Ibits=*/48,/*Obits=*/ 0,/*SM2a*/0x1000,/*SM2f*/0x00000024,/*SM3a*/0x1100,/*SM3f*/0x00010020,/*FM0ac*/0,/*FM1ac*/1},
+      {/*Man=*/0x00000002,/*ID=*/0x0c503052,/*Name=*/"EL3152"    ,/*dtype=*/4,/*Ibits=*/48,/*Obits=*/ 0,/*SM2a*/0x1000,/*SM2f*/0x00000024,/*SM3a*/0x1100,/*SM3f*/0x00010020,/*FM0ac*/0,/*FM1ac*/1},
+      {/*Man=*/0x00000002,/*ID=*/0x0c5a3052,/*Name=*/"EL3162"    ,/*dtype=*/4,/*Ibits=*/48,/*Obits=*/ 0,/*SM2a*/0x1000,/*SM2f*/0x00000024,/*SM3a*/0x1100,/*SM3f*/0x00010020,/*FM0ac*/0,/*FM1ac*/1},
+      {/*Man=*/0x00000002,/*ID=*/0x0fc03052,/*Name=*/"EL4032"    ,/*dtype=*/5,/*Ibits=*/ 0,/*Obits=*/32,/*SM2a*/0x1100,/*SM2f*/0x00010024,/*SM3a*/0x1180,/*SM3f*/0x00000022,/*FM0ac*/1,/*FM1ac*/0},
+      {/*Man=*/0x00000002,/*ID=*/0x10063052,/*Name=*/"EL4102"    ,/*dtype=*/5,/*Ibits=*/ 0,/*Obits=*/32,/*SM2a*/0x1000,/*SM2f*/0x00010024,/*SM3a*/0x1100,/*SM3f*/0x00000022,/*FM0ac*/1,/*FM1ac*/0},
+      {/*Man=*/0x00000002,/*ID=*/0x10103052,/*Name=*/"EL4112"    ,/*dtype=*/5,/*Ibits=*/ 0,/*Obits=*/32,/*SM2a*/0x1000,/*SM2f*/0x00010024,/*SM3a*/0x1100,/*SM3f*/0x00000022,/*FM0ac*/1,/*FM1ac*/0},
+      {/*Man=*/0x00000002,/*ID=*/0x101a3052,/*Name=*/"EL4122"    ,/*dtype=*/5,/*Ibits=*/ 0,/*Obits=*/32,/*SM2a*/0x1000,/*SM2f*/0x00010024,/*SM3a*/0x1100,/*SM3f*/0x00000022,/*FM0ac*/1,/*FM1ac*/0},
+      {/*Man=*/0x00000002,/*ID=*/0x10243052,/*Name=*/"EL4132"    ,/*dtype=*/5,/*Ibits=*/ 0,/*Obits=*/32,/*SM2a*/0x1000,/*SM2f*/0x00010024,/*SM3a*/0x1100,/*SM3f*/0x00000022,/*FM0ac*/1,/*FM1ac*/0},
+      {/*Man=*/0x00000002,/*ID=*/0x13ed3052,/*Name=*/"EL5101"    ,/*dtype=*/7,/*Ibits=*/40,/*Obits=*/24,/*SM2a*/0x1000,/*SM2f*/0x00010024,/*SM3a*/0x1100,/*SM3f*/0x00010020,/*FM0ac*/1,/*FM1ac*/1},
+      {/*Man=*/EC_CONFIGEND,/*ID=*/0x00000000,/*Name=*/""        ,/*dtype=*/0,/*Ibits=*/ 0,/*Obits=*/ 0,/*SM2a*/     0,/*SM2f*/         0,/*SM3a*/     0,/*SM3f*/         0,/*FM0ac*/0,/*FM1ac*/0}
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff -r 000000000000 -r 543d6784d4cc SOEM/ethercatdc.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SOEM/ethercatdc.c	Tue Jun 11 10:29:09 2019 +0000
@@ -0,0 +1,448 @@
+/*
+ * Licensed under the GNU General Public License version 2 with exceptions. See
+ * LICENSE file in the project root for full license information
+ */
+
+/** \file
+ * \brief
+ * Distributed Clock EtherCAT functions.
+ *
+ */
+#include "oshw.h"
+#include "osal.h"
+#include "ethercattype.h"
+#include "ethercatbase.h"
+#include "ethercatmain.h"
+#include "ethercatdc.h"
+
+#define PORTM0 0x01
+#define PORTM1 0x02
+#define PORTM2 0x04
+#define PORTM3 0x08
+
+/** 1st sync pulse delay in ns here 100ms */
+#define SyncDelay       ((int32)100000000)
+
+/**
+ * Set DC of slave to fire sync0 at CyclTime interval with CyclShift offset.
+ *
+ * @param[in]  context        = context struct
+ * @param [in] slave            Slave number.
+ * @param [in] act              TRUE = active, FALSE = deactivated
+ * @param [in] CyclTime         Cycltime in ns.
+ * @param [in] CyclShift        CyclShift in ns.
+ */
+void ecx_dcsync0(ecx_contextt *context, uint16 slave, boolean act, uint32 CyclTime, int32 CyclShift)
+{
+   uint8 h, RA;
+   uint16 slaveh;
+   int64 t, t1;
+   int32 tc;
+
+   slaveh = context->slavelist[slave].configadr;
+   RA = 0;
+
+   /* stop cyclic operation, ready for next trigger */
+   (void)ecx_FPWR(context->port, slaveh, ECT_REG_DCSYNCACT, sizeof(RA), &RA, EC_TIMEOUTRET);
+   if (act)
+   {
+       RA = 1 + 2;    /* act cyclic operation and sync0, sync1 deactivated */
+   }
+   h = 0;
+   (void)ecx_FPWR(context->port, slaveh, ECT_REG_DCCUC, sizeof(h), &h, EC_TIMEOUTRET); /* write access to ethercat */
+   t1 = 0;
+   (void)ecx_FPRD(context->port, slaveh, ECT_REG_DCSYSTIME, sizeof(t1), &t1, EC_TIMEOUTRET); /* read local time of slave */
+   t1 = etohll(t1);
+
+   /* Calculate first trigger time, always a whole multiple of CyclTime rounded up
+   plus the shifttime (can be negative)
+   This insures best synchronization between slaves, slaves with the same CyclTime
+   will sync at the same moment (you can use CyclShift to shift the sync) */
+   if (CyclTime > 0)
+   {
+       t = ((t1 + SyncDelay) / CyclTime) * CyclTime + CyclTime + CyclShift;
+   }
+   else
+   {
+      t = t1 + SyncDelay + CyclShift;
+      /* first trigger at T1 + CyclTime + SyncDelay + CyclShift in ns */
+   }
+   t = htoell(t);
+   (void)ecx_FPWR(context->port, slaveh, ECT_REG_DCSTART0, sizeof(t), &t, EC_TIMEOUTRET); /* SYNC0 start time */
+   tc = htoel(CyclTime);
+   (void)ecx_FPWR(context->port, slaveh, ECT_REG_DCCYCLE0, sizeof(tc), &tc, EC_TIMEOUTRET); /* SYNC0 cycle time */
+   (void)ecx_FPWR(context->port, slaveh, ECT_REG_DCSYNCACT, sizeof(RA), &RA, EC_TIMEOUTRET); /* activate cyclic operation */
+
+    // update ec_slave state
+    context->slavelist[slave].DCactive = (uint8)act;
+    context->slavelist[slave].DCshift = CyclShift;
+    context->slavelist[slave].DCcycle = CyclTime;
+}
+
+/**
+ * Set DC of slave to fire sync0 and sync1 at CyclTime interval with CyclShift offset.
+ *
+ * @param[in]  context        = context struct
+ * @param [in] slave            Slave number.
+ * @param [in] act              TRUE = active, FALSE = deactivated
+ * @param [in] CyclTime0        Cycltime SYNC0 in ns.
+ * @param [in] CyclTime1        Cycltime SYNC1 in ns. This time is a delta time in relation to
+                                the SYNC0 fire. If CylcTime1 = 0 then SYNC1 fires a the same time
+                                as SYNC0.
+ * @param [in] CyclShift        CyclShift in ns.
+ */
+void ecx_dcsync01(ecx_contextt *context, uint16 slave, boolean act, uint32 CyclTime0, uint32 CyclTime1, int32 CyclShift)
+{
+   uint8 h, RA;
+   uint16 slaveh;
+   int64 t, t1;
+   int32 tc;
+   uint32 TrueCyclTime;
+
+   /* Sync1 can be used as a multiple of Sync0, use true cycle time */
+   TrueCyclTime = ((CyclTime1 / CyclTime0) + 1) * CyclTime0;
+
+   slaveh = context->slavelist[slave].configadr;
+   RA = 0;
+
+   /* stop cyclic operation, ready for next trigger */
+   (void)ecx_FPWR(context->port, slaveh, ECT_REG_DCSYNCACT, sizeof(RA), &RA, EC_TIMEOUTRET);
+   if (act)
+   {
+      RA = 1 + 2 + 4;    /* act cyclic operation and sync0 + sync1 */
+   }
+   h = 0;
+   (void)ecx_FPWR(context->port, slaveh, ECT_REG_DCCUC, sizeof(h), &h, EC_TIMEOUTRET); /* write access to ethercat */
+   t1 = 0;
+   (void)ecx_FPRD(context->port, slaveh, ECT_REG_DCSYSTIME, sizeof(t1), &t1, EC_TIMEOUTRET); /* read local time of slave */
+   t1 = etohll(t1);
+
+   /* Calculate first trigger time, always a whole multiple of TrueCyclTime rounded up
+   plus the shifttime (can be negative)
+   This insures best synchronization between slaves, slaves with the same CyclTime
+   will sync at the same moment (you can use CyclShift to shift the sync) */
+   if (CyclTime0 > 0)
+   {
+      t = ((t1 + SyncDelay) / TrueCyclTime) * TrueCyclTime + TrueCyclTime + CyclShift;
+   }
+   else
+   {
+      t = t1 + SyncDelay + CyclShift;
+      /* first trigger at T1 + CyclTime + SyncDelay + CyclShift in ns */
+   }
+   t = htoell(t);
+   (void)ecx_FPWR(context->port, slaveh, ECT_REG_DCSTART0, sizeof(t), &t, EC_TIMEOUTRET); /* SYNC0 start time */
+   tc = htoel(CyclTime0);
+   (void)ecx_FPWR(context->port, slaveh, ECT_REG_DCCYCLE0, sizeof(tc), &tc, EC_TIMEOUTRET); /* SYNC0 cycle time */
+   tc = htoel(CyclTime1);
+   (void)ecx_FPWR(context->port, slaveh, ECT_REG_DCCYCLE1, sizeof(tc), &tc, EC_TIMEOUTRET); /* SYNC1 cycle time */
+   (void)ecx_FPWR(context->port, slaveh, ECT_REG_DCSYNCACT, sizeof(RA), &RA, EC_TIMEOUTRET); /* activate cyclic operation */
+
+    // update ec_slave state
+    context->slavelist[slave].DCactive = (uint8)act;
+    context->slavelist[slave].DCshift = CyclShift;
+    context->slavelist[slave].DCcycle = CyclTime0;
+}
+
+/* latched port time of slave */
+static int32 ecx_porttime(ecx_contextt *context, uint16 slave, uint8 port)
+{
+   int32 ts;
+   switch (port)
+   {
+      case 0:
+         ts = context->slavelist[slave].DCrtA;
+         break;
+      case 1:
+         ts = context->slavelist[slave].DCrtB;
+         break;
+      case 2:
+         ts = context->slavelist[slave].DCrtC;
+         break;
+      case 3:
+         ts = context->slavelist[slave].DCrtD;
+         break;
+      default:
+         ts = 0;
+         break;
+   }
+   return ts;
+}
+
+/* calculate previous active port of a slave */
+static uint8 ecx_prevport(ecx_contextt *context, uint16 slave, uint8 port)
+{
+   uint8 pport = port;
+   uint8 aport = context->slavelist[slave].activeports;
+   switch(port)
+   {
+      case 0:
+         if(aport & PORTM2)
+            pport = 2;
+         else if (aport & PORTM1)
+            pport = 1;
+         else if (aport & PORTM3)
+            pport = 3;
+         break;
+      case 1:
+         if(aport & PORTM3)
+            pport = 3;
+         else if (aport & PORTM0)
+            pport = 0;
+         else if (aport & PORTM2)
+            pport = 2;
+         break;
+      case 2:
+         if(aport & PORTM1)
+            pport = 1;
+         else if (aport & PORTM3)
+            pport = 3;
+         else if (aport & PORTM0)
+            pport = 0;
+         break;
+      case 3:
+         if(aport & PORTM0)
+            pport = 0;
+         else if (aport & PORTM2)
+            pport = 2;
+         else if (aport & PORTM1)
+            pport = 1;
+         break;
+   }
+   return pport;
+}
+
+/* search unconsumed ports in parent, consume and return first open port */
+static uint8 ecx_parentport(ecx_contextt *context, uint16 parent)
+{
+   uint8 parentport = 0;
+   uint8 b;
+   /* search order is important, here 3 - 1 - 2 - 0 */
+   b = context->slavelist[parent].consumedports;
+   if (b & PORTM3)
+   {
+      parentport = 3;
+      b &= (uint8)~PORTM3;
+   }
+   else if (b & PORTM1)
+   {
+      parentport = 1;
+      b &= (uint8)~PORTM1;
+   }
+   else if (b & PORTM2)
+   {
+      parentport = 2;
+      b &= (uint8)~PORTM2;
+   }
+   else if (b & PORTM0)
+   {
+      parentport = 0;
+      b &= (uint8)~PORTM0;
+   }
+   context->slavelist[parent].consumedports = b;
+   return parentport;
+}
+
+/**
+ * Locate DC slaves, measure propagation delays.
+ *
+ * @param[in]  context        = context struct
+ * @return boolean if slaves are found with DC
+ */
+boolean ecx_configdc(ecx_contextt *context)
+{
+   uint16 i, slaveh, parent, child;
+   uint16 parenthold = 0;
+   uint16 prevDCslave = 0;
+   int32 ht, dt1, dt2, dt3;
+   int64 hrt;
+   uint8 entryport;
+   int8 nlist;
+   int8 plist[4];
+   int32 tlist[4];
+   ec_timet mastertime;
+   uint64 mastertime64;
+
+   context->slavelist[0].hasdc = FALSE;
+   context->grouplist[0].hasdc = FALSE;
+   ht = 0;
+
+   ecx_BWR(context->port, 0, ECT_REG_DCTIME0, sizeof(ht), &ht, EC_TIMEOUTRET);  /* latch DCrecvTimeA of all slaves */
+   mastertime = osal_current_time();
+   mastertime.sec -= 946684800UL;  /* EtherCAT uses 2000-01-01 as epoch start instead of 1970-01-01 */
+   mastertime64 = (((uint64)mastertime.sec * 1000000) + (uint64)mastertime.usec) * 1000;
+   for (i = 1; i <= *(context->slavecount); i++)
+   {
+      context->slavelist[i].consumedports = context->slavelist[i].activeports;
+      if (context->slavelist[i].hasdc)
+      {
+         if (!context->slavelist[0].hasdc)
+         {
+            context->slavelist[0].hasdc = TRUE;
+            context->slavelist[0].DCnext = i;
+            context->slavelist[i].DCprevious = 0;
+            context->grouplist[context->slavelist[i].group].hasdc = TRUE;
+            context->grouplist[context->slavelist[i].group].DCnext = i;
+         }
+         else
+         {
+            context->slavelist[prevDCslave].DCnext = i;
+            context->slavelist[i].DCprevious = prevDCslave;
+         }
+         /* this branch has DC slave so remove parenthold */
+         parenthold = 0;
+         prevDCslave = i;
+         slaveh = context->slavelist[i].configadr;
+         (void)ecx_FPRD(context->port, slaveh, ECT_REG_DCTIME0, sizeof(ht), &ht, EC_TIMEOUTRET);
+         context->slavelist[i].DCrtA = etohl(ht);
+         /* 64bit latched DCrecvTimeA of each specific slave */
+         (void)ecx_FPRD(context->port, slaveh, ECT_REG_DCSOF, sizeof(hrt), &hrt, EC_TIMEOUTRET);
+         /* use it as offset in order to set local time around 0 + mastertime */
+         hrt = htoell(-etohll(hrt) + mastertime64);
+         /* save it in the offset register */
+         (void)ecx_FPWR(context->port, slaveh, ECT_REG_DCSYSOFFSET, sizeof(hrt), &hrt, EC_TIMEOUTRET);
+         (void)ecx_FPRD(context->port, slaveh, ECT_REG_DCTIME1, sizeof(ht), &ht, EC_TIMEOUTRET);
+         context->slavelist[i].DCrtB = etohl(ht);
+         (void)ecx_FPRD(context->port, slaveh, ECT_REG_DCTIME2, sizeof(ht), &ht, EC_TIMEOUTRET);
+         context->slavelist[i].DCrtC = etohl(ht);
+         (void)ecx_FPRD(context->port, slaveh, ECT_REG_DCTIME3, sizeof(ht), &ht, EC_TIMEOUTRET);
+         context->slavelist[i].DCrtD = etohl(ht);
+
+         /* make list of active ports and their time stamps */
+         nlist = 0;
+         if (context->slavelist[i].activeports & PORTM0)
+         {
+            plist[nlist] = 0;
+            tlist[nlist] = context->slavelist[i].DCrtA;
+            nlist++;
+         }
+         if (context->slavelist[i].activeports & PORTM3)
+         {
+            plist[nlist] = 3;
+            tlist[nlist] = context->slavelist[i].DCrtD;
+            nlist++;
+         }
+         if (context->slavelist[i].activeports & PORTM1)
+         {
+            plist[nlist] = 1;
+            tlist[nlist] = context->slavelist[i].DCrtB;
+            nlist++;
+         }
+         if (context->slavelist[i].activeports & PORTM2)
+         {
+            plist[nlist] = 2;
+            tlist[nlist] = context->slavelist[i].DCrtC;
+            nlist++;
+         }
+         /* entryport is port with the lowest timestamp */
+         entryport = 0;
+         if((nlist > 1) && (tlist[1] < tlist[entryport]))
+         {
+            entryport = 1;
+         }
+         if((nlist > 2) && (tlist[2] < tlist[entryport]))
+         {
+            entryport = 2;
+         }
+         if((nlist > 3) && (tlist[3] < tlist[entryport]))
+         {
+            entryport = 3;
+         }
+         entryport = plist[entryport];
+         context->slavelist[i].entryport = entryport;
+         /* consume entryport from activeports */
+         context->slavelist[i].consumedports &= (uint8)~(1 << entryport);
+
+         /* finding DC parent of current */
+         parent = i;
+         do
+         {
+            child = parent;
+            parent = context->slavelist[parent].parent;
+         }
+         while (!((parent == 0) || (context->slavelist[parent].hasdc)));
+         /* only calculate propagation delay if slave is not the first */
+         if (parent > 0)
+         {
+            /* find port on parent this slave is connected to */
+            context->slavelist[i].parentport = ecx_parentport(context, parent);
+            if (context->slavelist[parent].topology == 1)
+            {
+               context->slavelist[i].parentport = context->slavelist[parent].entryport;
+            }
+
+            dt1 = 0;
+            dt2 = 0;
+            /* delta time of (parentport - 1) - parentport */
+            /* note: order of ports is 0 - 3 - 1 -2 */
+            /* non active ports are skipped */
+            dt3 = ecx_porttime(context, parent, context->slavelist[i].parentport) -
+                  ecx_porttime(context, parent,
+                    ecx_prevport(context, parent, context->slavelist[i].parentport));
+            /* current slave has children */
+            /* those children's delays need to be subtracted */
+            if (context->slavelist[i].topology > 1)
+            {
+               dt1 = ecx_porttime(context, i,
+                        ecx_prevport(context, i, context->slavelist[i].entryport)) -
+                     ecx_porttime(context, i, context->slavelist[i].entryport);
+            }
+            /* we are only interested in positive difference */
+            if (dt1 > dt3) dt1 = -dt1;
+            /* current slave is not the first child of parent */
+            /* previous child's delays need to be added */
+            if ((child - parent) > 1)
+            {
+               dt2 = ecx_porttime(context, parent,
+                        ecx_prevport(context, parent, context->slavelist[i].parentport)) -
+                     ecx_porttime(context, parent, context->slavelist[parent].entryport);
+            }
+            if (dt2 < 0) dt2 = -dt2;
+
+            /* calculate current slave delay from delta times */
+            /* assumption : forward delay equals return delay */
+            context->slavelist[i].pdelay = ((dt3 - dt1) / 2) + dt2 +
+               context->slavelist[parent].pdelay;
+            ht = htoel(context->slavelist[i].pdelay);
+            /* write propagation delay*/
+            (void)ecx_FPWR(context->port, slaveh, ECT_REG_DCSYSDELAY, sizeof(ht), &ht, EC_TIMEOUTRET);
+         }
+      }
+      else
+      {
+         context->slavelist[i].DCrtA = 0;
+         context->slavelist[i].DCrtB = 0;
+         context->slavelist[i].DCrtC = 0;
+         context->slavelist[i].DCrtD = 0;
+         parent = context->slavelist[i].parent;
+         /* if non DC slave found on first position on branch hold root parent */
+         if ( (parent > 0) && (context->slavelist[parent].topology > 2))
+            parenthold = parent;
+         /* if branch has no DC slaves consume port on root parent */
+         if ( parenthold && (context->slavelist[i].topology == 1))
+         {
+            ecx_parentport(context, parenthold);
+            parenthold = 0;
+         }
+      }
+   }
+
+   return context->slavelist[0].hasdc;
+}
+
+#ifdef EC_VER1
+void ec_dcsync0(uint16 slave, boolean act, uint32 CyclTime, int32 CyclShift)
+{
+   ecx_dcsync0(&ecx_context, slave, act, CyclTime, CyclShift);
+}
+
+void ec_dcsync01(uint16 slave, boolean act, uint32 CyclTime0, uint32 CyclTime1, int32 CyclShift)
+{
+   ecx_dcsync01(&ecx_context, slave, act, CyclTime0, CyclTime1, CyclShift);
+}
+
+boolean ec_configdc(void)
+{
+   return ecx_configdc(&ecx_context);
+}
+#endif
diff -r 000000000000 -r 543d6784d4cc SOEM/ethercatdc.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SOEM/ethercatdc.h	Tue Jun 11 10:29:09 2019 +0000
@@ -0,0 +1,33 @@
+/*
+ * Licensed under the GNU General Public License version 2 with exceptions. See
+ * LICENSE file in the project root for full license information
+ */
+
+/** \file
+ * \brief
+ * Headerfile for ethercatdc.c
+ */
+
+#ifndef _EC_ECATDC_H
+#define _EC_ECATDC_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#ifdef EC_VER1
+boolean ec_configdc();
+void ec_dcsync0(uint16 slave, boolean act, uint32 CyclTime, int32 CyclShift);
+void ec_dcsync01(uint16 slave, boolean act, uint32 CyclTime0, uint32 CyclTime1, int32 CyclShift);
+#endif
+
+boolean ecx_configdc(ecx_contextt *context);
+void ecx_dcsync0(ecx_contextt *context, uint16 slave, boolean act, uint32 CyclTime, int32 CyclShift);
+void ecx_dcsync01(ecx_contextt *context, uint16 slave, boolean act, uint32 CyclTime0, uint32 CyclTime1, int32 CyclShift);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _EC_ECATDC_H */
diff -r 000000000000 -r 543d6784d4cc SOEM/ethercateoe.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SOEM/ethercateoe.c	Tue Jun 11 10:29:09 2019 +0000
@@ -0,0 +1,640 @@
+/*
+ * Licensed under the GNU General Public License version 2 with exceptions. See
+ * LICENSE file in the project root for full license information
+ */
+
+/** \file
+ * \brief
+ * Ethernet over EtherCAT (EoE) module.
+ *
+ * Set / Get IP functions
+ * Blocking send/receive Ethernet Frame
+ * Read incoming EoE fragment to Ethernet Frame
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include "osal.h"
+#include "oshw.h"
+#include "ethercat.h"
+
+ /** EoE utility function to convert uint32 to eoe ip bytes.
+ * @param[in] ip       = ip in uint32
+ * @param[out] byte_ip = eoe ip 4th octet, 3ed octet, 2nd octet, 1st octet
+ */
+static void EOE_ip_uint32_to_byte(eoe_ip4_addr_t * ip, uint8_t * byte_ip)
+{
+   byte_ip[3] = eoe_ip4_addr1(ip); /* 1st octet */
+   byte_ip[2] = eoe_ip4_addr2(ip); /* 2nd octet */
+   byte_ip[1] = eoe_ip4_addr3(ip); /* 3ed octet */
+   byte_ip[0] = eoe_ip4_addr4(ip); /* 4th octet */
+}
+
+/** EoE utility function to convert eoe ip bytes to uint32.
+* @param[in] byte_ip = eoe ip 4th octet, 3ed octet, 2nd octet, 1st octet
+* @param[out] ip     = ip in uint32
+*/
+static void EOE_ip_byte_to_uint32(uint8_t * byte_ip, eoe_ip4_addr_t * ip)
+{
+   EOE_IP4_ADDR_TO_U32(ip,
+      byte_ip[3],  /* 1st octet */
+      byte_ip[2],  /* 2nd octet */
+      byte_ip[1],  /* 3ed octet */
+      byte_ip[0]); /* 4th octet */
+}
+
+/** EoE fragment data handler hook. Should not block.
+*
+* @param[in]  context = context struct
+* @param[in]  hook    = Pointer to hook function.
+* @return 1
+*/
+int ecx_EOEdefinehook(ecx_contextt *context, void *hook)
+{
+   context->EOEhook = hook;
+   return 1;
+}
+
+/** EoE EOE set IP, blocking. Waits for response from the slave.
+*
+* @param[in]  context    = Context struct
+* @param[in]  slave      = Slave number
+* @param[in]  port       = Port number on slave if applicable
+* @param[in]  ipparam    = IP parameter data to be sent
+* @param[in]  Timeout    = Timeout in us, standard is EC_TIMEOUTRXM
+* @return Workcounter from last slave response or returned result code 
+*/
+int ecx_EOEsetIp(ecx_contextt *context, uint16 slave, uint8 port, eoe_param_t * ipparam, int timeout)
+{
+   ec_EOEt *EOEp, *aEOEp;  
+   ec_mbxbuft MbxIn, MbxOut;  
+   uint16 frameinfo1, result;
+   uint8 cnt, data_offset;
+   uint8 flags = 0;
+   int wkc;
+
+   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);
+   aEOEp = (ec_EOEt *)&MbxIn;
+   EOEp = (ec_EOEt *)&MbxOut;  
+   EOEp->mbxheader.address = htoes(0x0000);
+   EOEp->mbxheader.priority = 0x00;
+   data_offset = EOE_PARAM_OFFSET;
+   
+   /* get new mailbox count value, used as session handle */
+   cnt = ec_nextmbxcnt(context->slavelist[slave].mbx_cnt);
+   context->slavelist[slave].mbx_cnt = cnt;
+
+   EOEp->mbxheader.mbxtype = ECT_MBXT_EOE + (cnt << 4); /* EoE */
+
+   EOEp->frameinfo1 = htoes(EOE_HDR_FRAME_TYPE_SET(EOE_INIT_REQ) | 
+      EOE_HDR_FRAME_PORT_SET(port) |
+      EOE_HDR_LAST_FRAGMENT);
+   EOEp->frameinfo2 = 0;
+  
+   /* The EoE frame will include "empty" IP/DNS entries, makes wireshark happy.
+    * Specification say they are optional, TwinCAT include empty entries.
+    */
+   if (ipparam->mac_set)
+   {
+      flags |= EOE_PARAM_MAC_INCLUDE;
+      memcpy(&EOEp->data[data_offset], ipparam->mac.addr, EOE_ETHADDR_LENGTH);
+   }
+   data_offset += EOE_ETHADDR_LENGTH;
+   if (ipparam->ip_set)
+   {
+      flags |= EOE_PARAM_IP_INCLUDE;
+      EOE_ip_uint32_to_byte(&ipparam->ip, &EOEp->data[data_offset]);
+   }
+   data_offset += 4;
+   if (ipparam->subnet_set)
+   {
+      flags |= EOE_PARAM_SUBNET_IP_INCLUDE;
+      EOE_ip_uint32_to_byte(&ipparam->subnet, &EOEp->data[data_offset]);
+   }
+   data_offset += 4;
+   if (ipparam->default_gateway_set)
+   {
+      flags |= EOE_PARAM_DEFAULT_GATEWAY_INCLUDE;
+      EOE_ip_uint32_to_byte(&ipparam->default_gateway, &EOEp->data[data_offset]);
+   }
+   data_offset += 4;
+   if (ipparam->dns_ip_set)
+   {
+      flags |= EOE_PARAM_DNS_IP_INCLUDE;
+      EOE_ip_uint32_to_byte(&ipparam->dns_ip, &EOEp->data[data_offset]);
+   }
+   data_offset += 4;
+   if (ipparam->dns_name_set)
+   {
+      flags |= EOE_PARAM_DNS_NAME_INCLUDE;
+      memcpy(&EOEp->data[data_offset], (void *)ipparam->dns_name, EOE_DNS_NAME_LENGTH);
+   }
+   data_offset += EOE_DNS_NAME_LENGTH;
+
+   EOEp->mbxheader.length = htoes(EOE_PARAM_OFFSET + data_offset);
+   EOEp->data[0] = flags;
+
+   /* send EoE request to slave */
+   wkc = ecx_mbxsend(context, slave, (ec_mbxbuft *)&MbxOut, EC_TIMEOUTTXM);
+
+   if (wkc > 0) /* succeeded to place mailbox in slave ? */
+   {
+      /* 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 ((aEOEp->mbxheader.mbxtype & 0x0f) == ECT_MBXT_EOE)
+         {
+            frameinfo1 = etohs(aEOEp->frameinfo1);
+            result = etohs(aEOEp->result);
+            if ((EOE_HDR_FRAME_TYPE_GET(frameinfo1) != EOE_INIT_RESP) ||
+                (result != EOE_RESULT_SUCCESS))
+            {
+               wkc = -result;
+            }
+         }
+         else
+         {
+            /* unexpected mailbox received */
+            wkc = -EC_ERR_TYPE_PACKET_ERROR;
+         }
+      }
+   }
+   return wkc;
+}
+
+/** EoE EOE get IP, blocking. Waits for response from the slave.
+*
+* @param[in]  context    = Context struct
+* @param[in]  slave      = Slave number
+* @param[in]  port       = Port number on slave if applicable
+* @param[out] ipparam    = IP parameter data retrived from slave
+* @param[in]  Timeout    = Timeout in us, standard is EC_TIMEOUTRXM
+* @return Workcounter from last slave response or returned result code
+*/
+int ecx_EOEgetIp(ecx_contextt *context, uint16 slave, uint8 port, eoe_param_t * ipparam, int timeout)
+{
+   ec_EOEt *EOEp, *aEOEp;
+   ec_mbxbuft MbxIn, MbxOut;
+   uint16 frameinfo1, eoedatasize;
+   uint8 cnt, data_offset;
+   uint8 flags = 0;
+   int wkc;
+
+   /* Empty slave out mailbox if something is in. Timout set to 0 */
+   wkc = ecx_mbxreceive(context, slave, (ec_mbxbuft *)&MbxIn, 0);
+   ec_clearmbx(&MbxOut);
+   aEOEp = (ec_EOEt *)&MbxIn;
+   EOEp = (ec_EOEt *)&MbxOut;
+   EOEp->mbxheader.address = htoes(0x0000);
+   EOEp->mbxheader.priority = 0x00;
+   data_offset = EOE_PARAM_OFFSET;
+
+   /* get new mailbox count value, used as session handle */
+   cnt = ec_nextmbxcnt(context->slavelist[slave].mbx_cnt);
+   context->slavelist[slave].mbx_cnt = cnt;
+
+   EOEp->mbxheader.mbxtype = ECT_MBXT_EOE + (cnt << 4); /* EoE */
+
+   EOEp->frameinfo1 = htoes(EOE_HDR_FRAME_TYPE_SET(EOE_GET_IP_PARAM_REQ) | 
+      EOE_HDR_FRAME_PORT_SET(port) |
+      EOE_HDR_LAST_FRAGMENT);
+   EOEp->frameinfo2 = 0;
+
+   EOEp->mbxheader.length = htoes(0x0004); 
+   EOEp->data[0] = flags;
+
+   /* send EoE request to slave */
+   wkc = ecx_mbxsend(context, slave, (ec_mbxbuft *)&MbxOut, EC_TIMEOUTTXM);
+   if (wkc > 0) /* succeeded to place mailbox in slave ? */
+   {
+      /* 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 ((aEOEp->mbxheader.mbxtype & 0x0f) == ECT_MBXT_EOE)
+         {
+            frameinfo1 = etohs(aEOEp->frameinfo1);
+            eoedatasize = etohs(aEOEp->mbxheader.length) - 0x0004;
+            if (EOE_HDR_FRAME_TYPE_GET(frameinfo1) != EOE_GET_IP_PARAM_RESP)
+            {
+               wkc = -EOE_RESULT_UNSUPPORTED_FRAME_TYPE;
+            }
+            else
+            {
+               /* The EoE frame will include "empty" IP/DNS entries,  makes 
+                * wireshark happy. Specification say they are optional, TwinCAT 
+                * include empty entries.
+                */
+               flags = aEOEp->data[0];
+               if (flags & EOE_PARAM_MAC_INCLUDE)
+               {
+                  memcpy(ipparam->mac.addr, 
+                     &aEOEp->data[data_offset], 
+                     EOE_ETHADDR_LENGTH);
+                  ipparam->mac_set = 1;
+               }
+               data_offset += EOE_ETHADDR_LENGTH;
+               if (flags & EOE_PARAM_IP_INCLUDE)
+               {
+                  EOE_ip_byte_to_uint32(&aEOEp->data[data_offset],
+                     &ipparam->ip);
+                  ipparam->ip_set = 1;
+               }
+               data_offset += 4;
+               if (flags & EOE_PARAM_SUBNET_IP_INCLUDE)
+               {
+                  EOE_ip_byte_to_uint32(&aEOEp->data[data_offset],
+                     &ipparam->subnet);
+                  ipparam->subnet_set = 1;
+               }
+               data_offset += 4;
+               if (flags & EOE_PARAM_DEFAULT_GATEWAY_INCLUDE)
+               {
+                  EOE_ip_byte_to_uint32(&aEOEp->data[data_offset],
+                     &ipparam->default_gateway);
+                  ipparam->default_gateway_set = 1;
+               }
+               data_offset += 4;
+               if (flags & EOE_PARAM_DNS_IP_INCLUDE)
+               {
+                  EOE_ip_byte_to_uint32(&aEOEp->data[data_offset],
+                     &ipparam->dns_ip);
+                  ipparam->dns_ip_set = 1;
+               }
+               data_offset += 4;
+               if (flags & EOE_PARAM_DNS_NAME_INCLUDE)
+               {
+                  uint16_t dns_len;
+                  if ((eoedatasize - data_offset) < EOE_DNS_NAME_LENGTH)
+                  {
+                     dns_len = (eoedatasize - data_offset);
+                  }
+                  else
+                  {
+                     dns_len = EOE_DNS_NAME_LENGTH;
+                  }     
+                  /* Assume ZERO terminated string */
+                  memcpy(ipparam->dns_name, &aEOEp->data[data_offset], dns_len);
+                  ipparam->dns_name_set = 1;
+               }
+               data_offset += EOE_DNS_NAME_LENGTH;
+               /* Something os not correct, flag the error */
+               if(data_offset > eoedatasize)
+               {
+                  wkc = -EC_ERR_TYPE_MBX_ERROR;
+               }
+            }
+         }
+         else
+         {
+            /* unexpected mailbox received */
+            wkc = -EC_ERR_TYPE_PACKET_ERROR;
+         }
+      }
+   }
+   return wkc;
+}
+
+/** EoE ethernet buffer write, blocking. 
+*
+* If the buffer is larger than the mailbox size then the buffer is sent in 
+* several fragments. The function will split the buf data in fragments and
+* send them to the slave one by one.
+*
+* @param[in]  context    = context struct
+* @param[in]  slave      = Slave number
+* @param[in]  port       = Port number on slave if applicable
+* @param[in]  psize      = Size in bytes of parameter buffer.
+* @param[in]  p          = Pointer to parameter buffer
+* @param[in]  Timeout    = Timeout in us, standard is EC_TIMEOUTRXM
+* @return Workcounter from last slave transmission
+*/
+int ecx_EOEsend(ecx_contextt *context, uint16 slave, uint8 port, int psize, void *p, int timeout)
+{
+   ec_EOEt *EOEp;
+   ec_mbxbuft MbxOut;
+   uint16 frameinfo1, frameinfo2;
+   uint16 txframesize, txframeoffset;
+   uint8 cnt, txfragmentno;  
+   boolean  NotLast;
+   int wkc, maxdata;
+   const uint8 * buf = p;
+   static uint8_t txframeno = 0;
+
+   ec_clearmbx(&MbxOut);
+   EOEp = (ec_EOEt *)&MbxOut;
+   EOEp->mbxheader.address = htoes(0x0000);
+   EOEp->mbxheader.priority = 0x00;
+   /* data section=mailbox size - 6 mbx - 4 EoEh */
+   maxdata = context->slavelist[slave].mbx_l - 0x0A; 
+   txframesize = psize;
+   txfragmentno = 0;
+   txframeoffset = 0;
+   NotLast = TRUE;
+
+   do
+   {
+      txframesize = psize - txframeoffset;
+      if (txframesize > maxdata)
+      {
+         /* Adjust to even 32-octect blocks */
+         txframesize = ((maxdata >> 5) << 5);
+      }
+
+      if (txframesize == (psize - txframeoffset))
+      {
+         frameinfo1 = (EOE_HDR_LAST_FRAGMENT_SET(1) | EOE_HDR_FRAME_PORT_SET(port));
+         NotLast = FALSE;
+      }
+      else
+      {
+         frameinfo1 = EOE_HDR_FRAME_PORT_SET(port);
+      }
+
+      frameinfo2 = EOE_HDR_FRAG_NO_SET(txfragmentno);
+      if (txfragmentno > 0)
+      {
+         frameinfo2 = frameinfo2 | (EOE_HDR_FRAME_OFFSET_SET((txframeoffset >> 5)));
+      }
+      else
+      {
+         frameinfo2 = frameinfo2 | (EOE_HDR_FRAME_OFFSET_SET(((psize + 31) >> 5)));
+         txframeno++;
+      }
+      frameinfo2 = frameinfo2 | EOE_HDR_FRAME_NO_SET(txframeno);
+
+      /* get new mailbox count value, used as session handle */
+      cnt = ec_nextmbxcnt(context->slavelist[slave].mbx_cnt);
+      context->slavelist[slave].mbx_cnt = cnt;
+
+      EOEp->mbxheader.length = htoes(4 + txframesize); /* no timestamp */
+      EOEp->mbxheader.mbxtype = ECT_MBXT_EOE + (cnt << 4); /* EoE */
+
+      EOEp->frameinfo1 = htoes(frameinfo1);
+      EOEp->frameinfo2 = htoes(frameinfo2);
+
+      memcpy(EOEp->data, &buf[txframeoffset], txframesize);
+
+      /* send EoE request to slave */
+      wkc = ecx_mbxsend(context, slave, (ec_mbxbuft *)&MbxOut, timeout);
+      if ((NotLast == TRUE)  && (wkc > 0))
+      {
+         txframeoffset += txframesize;
+         txfragmentno++;
+      }
+   } while ((NotLast == TRUE) && (wkc > 0));
+   
+   return wkc;
+}
+
+
+/** EoE ethernet buffer read, blocking.
+*
+* If the buffer is larger than the mailbox size then the buffer is received 
+* by several fragments. The function will assamble the fragments into
+* a complete Ethernet buffer.
+*
+* @param[in]     context = context struct
+* @param[in]     slave   = Slave number
+* @param[in]     port    = Port number on slave if applicable
+* @param[in/out] psize   = Size in bytes of parameter buffer.
+* @param[in]     p       = Pointer to parameter buffer
+* @param[in]     timeout = Timeout in us, standard is EC_TIMEOUTRXM
+* @return Workcounter from last slave response or error code
+*/
+int ecx_EOErecv(ecx_contextt *context, uint16 slave, uint8 port, int * psize, void *p, int timeout)
+{
+   ec_EOEt *aEOEp;
+   ec_mbxbuft MbxIn;
+   uint16 frameinfo1, frameinfo2, rxframesize, rxframeoffset, eoedatasize;
+   uint8 rxfragmentno, rxframeno;
+   boolean NotLast;
+   int wkc, buffersize;
+   uint8 * buf = p;
+   
+   ec_clearmbx(&MbxIn);
+   aEOEp = (ec_EOEt *)&MbxIn;
+   NotLast = TRUE;
+   buffersize = *psize;
+   rxfragmentno = 0;
+   
+   /* Hang for a while if nothing is in */
+   wkc = ecx_mbxreceive(context, slave, (ec_mbxbuft *)&MbxIn, timeout);
+
+   while ((wkc > 0) && (NotLast == TRUE))
+   {
+      /* slave response should be FoE */
+      if ((aEOEp->mbxheader.mbxtype & 0x0f) == ECT_MBXT_EOE)
+      {
+        
+         eoedatasize = etohs(aEOEp->mbxheader.length) - 0x00004;
+         frameinfo1 = etohs(aEOEp->frameinfo1);
+         frameinfo2 = etohs(aEOEp->frameinfo2);
+
+         if (rxfragmentno != EOE_HDR_FRAG_NO_GET(frameinfo2))
+         {
+            if (EOE_HDR_FRAG_NO_GET(frameinfo2) > 0)
+            {
+               wkc = -EC_ERR_TYPE_EOE_INVALID_RX_DATA;
+               /* Exit here*/
+               break;
+            }
+         }
+
+         if (rxfragmentno == 0)
+         {
+            rxframeoffset = 0;
+            rxframeno = EOE_HDR_FRAME_NO_GET(frameinfo2);
+            rxframesize = (EOE_HDR_FRAME_OFFSET_GET(frameinfo2) << 5);
+            if (rxframesize > buffersize)
+            {
+               wkc = -EC_ERR_TYPE_EOE_INVALID_RX_DATA;
+               /* Exit here*/
+               break;
+            }
+            if (port != EOE_HDR_FRAME_PORT_GET(frameinfo1))
+            {
+               wkc = -EC_ERR_TYPE_EOE_INVALID_RX_DATA;
+               /* Exit here*/
+               break;
+            }
+         }
+         else
+         {
+            if (rxframeno != EOE_HDR_FRAME_NO_GET(frameinfo2))
+            {
+               wkc = -EC_ERR_TYPE_EOE_INVALID_RX_DATA;
+               /* Exit here*/
+               break;
+            }
+            else if (rxframeoffset != (EOE_HDR_FRAME_OFFSET_GET(frameinfo2) << 5))
+            {
+               wkc = -EC_ERR_TYPE_EOE_INVALID_RX_DATA;
+               /* Exit here*/
+               break;
+            }
+         }
+
+         if ((rxframeoffset + eoedatasize) <= buffersize)
+         {
+            memcpy(&buf[rxframeoffset], aEOEp->data, eoedatasize);
+            rxframeoffset += eoedatasize;
+            rxfragmentno++;
+         }
+
+         if (EOE_HDR_LAST_FRAGMENT_GET(frameinfo1))
+         {
+            /* Remove timestamp */
+            if (EOE_HDR_TIME_APPEND_GET(frameinfo1))
+            {
+               rxframeoffset -= 4;
+            }
+            NotLast = FALSE;
+            *psize = rxframeoffset;
+         }
+         else
+         {
+            /* Hang for a while if nothing is in */
+            wkc = ecx_mbxreceive(context, slave, (ec_mbxbuft *)&MbxIn, timeout);
+         }
+      }
+      else
+      {
+         /* unexpected mailbox received */
+         wkc = -EC_ERR_TYPE_PACKET_ERROR;
+      }
+   }
+   return wkc;
+}
+
+/** EoE mailbox fragment read
+*
+* Will take the data in incoming mailbox buffer and copy to destination 
+* Ethernet frame buffer at given offset and update current fragment variables
+*
+* @param[in] MbxIn             = Received mailbox containing fragment data
+* @param[in/out] rxfragmentno  = Fragment number
+* @param[in/out] rxframesize   = Frame size
+* @param[in/out] rxframeoffset = Frame offset
+* @param[in/out] rxframeno     = Frame number
+* @param[in/out] psize         = Size in bytes of frame buffer.
+* @param[out] p                = Pointer to frame buffer
+* @return 0= if fragment OK, >0 if last fragment, <0 on error
+*/
+int ecx_EOEreadfragment(
+   ec_mbxbuft * MbxIn,
+   uint8 * rxfragmentno,
+   uint16 * rxframesize, 
+   uint16 * rxframeoffset, 
+   uint16 * rxframeno,
+   int * psize, 
+   void *p)
+{  
+   uint16 frameinfo1, frameinfo2,  eoedatasize;
+   int wkc;
+   ec_EOEt * aEOEp;
+   uint8 * buf;
+   
+   aEOEp = (ec_EOEt *)MbxIn;
+   buf = p;
+   wkc = 0;
+
+   /* slave response should be EoE */
+   if ((aEOEp->mbxheader.mbxtype & 0x0f) == ECT_MBXT_EOE)
+   {
+      eoedatasize = etohs(aEOEp->mbxheader.length) - 0x00004;
+      frameinfo1 = etohs(aEOEp->frameinfo1);
+      frameinfo2 = etohs(aEOEp->frameinfo2);
+
+      /* Retrive fragment number, is it what we expect? */
+      if (*rxfragmentno != EOE_HDR_FRAG_NO_GET(frameinfo2))
+      {
+         /* If expected fragment number is not 0, reset working variables */
+         if (*rxfragmentno != 0)
+         {
+            *rxfragmentno = 0;
+            *rxframesize = 0;
+            *rxframeoffset = 0;
+            *rxframeno = 0;
+         }
+
+         /* If incoming fragment number is not 0 we can't recover, exit */
+         if (EOE_HDR_FRAG_NO_GET(frameinfo2) > 0)
+         {
+            wkc = -EC_ERR_TYPE_EOE_INVALID_RX_DATA;
+            return wkc;
+         }
+      }
+
+      /* Is it a new frame?*/
+      if (*rxfragmentno == 0)
+      {
+         *rxframesize = (EOE_HDR_FRAME_OFFSET_GET(frameinfo2) << 5);
+         *rxframeoffset = 0;
+         *rxframeno = EOE_HDR_FRAME_NO_GET(frameinfo2);
+      }
+      else
+      {
+         /* If we're inside a frame, make sure it is the same */
+         if (*rxframeno != EOE_HDR_FRAME_NO_GET(frameinfo2))
+         {
+            *rxfragmentno = 0;
+            *rxframesize = 0;
+            *rxframeoffset = 0;
+            *rxframeno = 0;
+            wkc = -EC_ERR_TYPE_EOE_INVALID_RX_DATA;
+            return wkc;
+         }
+         else if (*rxframeoffset != (EOE_HDR_FRAME_OFFSET_GET(frameinfo2) << 5))
+         {
+            *rxfragmentno = 0;
+            *rxframesize = 0;
+            *rxframeoffset = 0;
+            *rxframeno = 0;
+            wkc = -EC_ERR_TYPE_EOE_INVALID_RX_DATA;
+            return wkc;
+         }
+      }
+
+      /* Make sure we're inside expected frame size */
+      if (((*rxframeoffset + eoedatasize) <= *rxframesize) && 
+         ((*rxframeoffset + eoedatasize) <= *psize))
+      {
+         memcpy(&buf[*rxframeoffset], aEOEp->data, eoedatasize);
+         *rxframeoffset += eoedatasize;
+         *rxfragmentno += 1;
+      }
+
+      /* Is it the last fragment */
+      if (EOE_HDR_LAST_FRAGMENT_GET(frameinfo1))
+      {
+         /* Remove timestamp */
+         if (EOE_HDR_TIME_APPEND_GET(frameinfo1))
+         {
+            *rxframeoffset -= 4;
+         }
+         *psize = *rxframeoffset;
+         *rxfragmentno = 0;
+         *rxframesize = 0;
+         *rxframeoffset = 0;
+         *rxframeno = 0;
+         wkc = 1;
+      }
+   }
+   else
+   {
+      /* unexpected mailbox received */
+      wkc = -EC_ERR_TYPE_PACKET_ERROR;
+   }
+   return wkc;
+}
diff -r 000000000000 -r 543d6784d4cc SOEM/ethercateoe.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SOEM/ethercateoe.h	Tue Jun 11 10:29:09 2019 +0000
@@ -0,0 +1,206 @@
+/*
+ * Licensed under the GNU General Public License version 2 with exceptions. See
+ * LICENSE file in the project root for full license information
+ */
+
+/** \file
+ * \brief
+ * Headerfile for ethercatfoe.c
+ */
+
+#ifndef _ethercateoe_
+#define _ethercateoe_
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <ethercattype.h> 
+
+/** DNS length according to ETG 1000.6 */
+#define EOE_DNS_NAME_LENGTH  32
+/** Ethernet address length not including VLAN */
+#define EOE_ETHADDR_LENGTH    6
+
+#define EOE_MAKEU32(a,b,c,d) (((uint32_t)((a) & 0xff) << 24) | \
+                            ((uint32_t)((b) & 0xff) << 16) | \
+                            ((uint32_t)((c) & 0xff) << 8)  | \
+                            (uint32_t)((d) & 0xff))
+
+#if !defined(EC_BIG_ENDIAN) && defined(EC_LITTLE_ENDIAN)
+
+#define EOE_HTONS(x) ((((x) & 0x00ffUL) << 8) | (((x) & 0xff00UL) >> 8))
+#define EOE_NTOHS(x) EOE_HTONS(x)
+#define EOE_HTONL(x) ((((x) & 0x000000ffUL) << 24) | \
+                     (((x) & 0x0000ff00UL) <<  8) | \
+                     (((x) & 0x00ff0000UL) >>  8) | \
+                     (((x) & 0xff000000UL) >> 24))
+#define EOE_NTOHL(x) EOE_HTONL(x)
+#else
+#define EOE_HTONS(x) (x)
+#define EOE_NTOHS(x) (x)
+#define EOE_HTONL(x) (x)
+#define EOE_NTOHL(x) (x)
+#endif /* !defined(EC_BIG_ENDIAN) && defined(EC_LITTLE_ENDIAN) */
+
+/** Get one byte from the 4-byte address */
+#define eoe_ip4_addr1(ipaddr) (((const uint8_t*)(&(ipaddr)->addr))[0])
+#define eoe_ip4_addr2(ipaddr) (((const uint8_t*)(&(ipaddr)->addr))[1])
+#define eoe_ip4_addr3(ipaddr) (((const uint8_t*)(&(ipaddr)->addr))[2])
+#define eoe_ip4_addr4(ipaddr) (((const uint8_t*)(&(ipaddr)->addr))[3])
+
+/** Set an IP address given by the four byte-parts */
+#define EOE_IP4_ADDR_TO_U32(ipaddr,a,b,c,d)  \
+   (ipaddr)->addr = EOE_HTONL(EOE_MAKEU32(a,b,c,d))
+
+/** Header frame info 1 */
+#define EOE_HDR_FRAME_TYPE_OFFSET      0
+#define EOE_HDR_FRAME_TYPE             (0xF << 0)
+#define EOE_HDR_FRAME_TYPE_SET(x)      (((x) & 0xF) << 0)
+#define EOE_HDR_FRAME_TYPE_GET(x)      (((x) >> 0) & 0xF)
+#define EOE_HDR_FRAME_PORT_OFFSET      4
+#define EOE_HDR_FRAME_PORT             (0xF << 4)
+#define EOE_HDR_FRAME_PORT_SET(x)      (((x) & 0xF) << 4)
+#define EOE_HDR_FRAME_PORT_GET(x)      (((x) >> 4) & 0xF)
+#define EOE_HDR_LAST_FRAGMENT_OFFSET   8
+#define EOE_HDR_LAST_FRAGMENT          (0x1 << 8)
+#define EOE_HDR_LAST_FRAGMENT_SET(x)   (((x) & 0x1) << 8)
+#define EOE_HDR_LAST_FRAGMENT_GET(x)   (((x) >> 8) & 0x1)
+#define EOE_HDR_TIME_APPEND_OFFSET     9
+#define EOE_HDR_TIME_APPEND            (0x1 << 9)
+#define EOE_HDR_TIME_APPEND_SET(x)     (((x) & 0x1) << 9)
+#define EOE_HDR_TIME_APPEND_GET(x)     (((x) >> 9) & 0x1)
+#define EOE_HDR_TIME_REQUEST_OFFSET    10
+#define EOE_HDR_TIME_REQUEST           (0x1 << 10)
+#define EOE_HDR_TIME_REQUEST_SET(x)    (((x) & 0x1) << 10)
+#define EOE_HDR_TIME_REQUEST_GET(x)    (((x) >> 10) & 0x1)
+
+/** Header frame info 2 */
+#define EOE_HDR_FRAG_NO_OFFSET         0
+#define EOE_HDR_FRAG_NO                (0x3F << 0)
+#define EOE_HDR_FRAG_NO_SET(x)         (((x) & 0x3F) << 0)
+#define EOE_HDR_FRAG_NO_GET(x)         (((x) >> 0) & 0x3F)
+#define EOE_HDR_FRAME_OFFSET_OFFSET    6
+#define EOE_HDR_FRAME_OFFSET           (0x3F << 6)
+#define EOE_HDR_FRAME_OFFSET_SET(x)    (((x) & 0x3F) << 6)
+#define EOE_HDR_FRAME_OFFSET_GET(x)    (((x) >> 6) & 0x3F)
+#define EOE_HDR_FRAME_NO_OFFSET        12
+#define EOE_HDR_FRAME_NO               (0xF << 12)
+#define EOE_HDR_FRAME_NO_SET(x)        (((x) & 0xF) << 12)
+#define EOE_HDR_FRAME_NO_GET(x)        (((x) >> 12) & 0xF)
+
+/** EOE param */
+#define EOE_PARAM_OFFSET                  4
+#define EOE_PARAM_MAC_INCLUDE             (0x1 << 0)
+#define EOE_PARAM_IP_INCLUDE              (0x1 << 1)
+#define EOE_PARAM_SUBNET_IP_INCLUDE       (0x1 << 2)
+#define EOE_PARAM_DEFAULT_GATEWAY_INCLUDE (0x1 << 3)
+#define EOE_PARAM_DNS_IP_INCLUDE          (0x1 << 4)
+#define EOE_PARAM_DNS_NAME_INCLUDE        (0x1 << 5)
+
+/** EoE frame types */
+#define EOE_FRAG_DATA                  0
+#define EOE_INIT_RESP_TIMESTAMP        1
+#define EOE_INIT_REQ                   2 /* Spec SET IP REQ */
+#define EOE_INIT_RESP                  3 /* Spec SET IP RESP */
+#define EOE_SET_ADDR_FILTER_REQ        4
+#define EOE_SET_ADDR_FILTER_RESP       5
+#define EOE_GET_IP_PARAM_REQ           6
+#define EOE_GET_IP_PARAM_RESP          7
+#define EOE_GET_ADDR_FILTER_REQ        8
+#define EOE_GET_ADDR_FILTER_RESP       9
+
+/** EoE parameter result codes */
+#define EOE_RESULT_SUCCESS                   0x0000
+#define EOE_RESULT_UNSPECIFIED_ERROR         0x0001
+#define EOE_RESULT_UNSUPPORTED_FRAME_TYPE    0x0002
+#define EOE_RESULT_NO_IP_SUPPORT             0x0201
+#define EOE_RESULT_NO_DHCP_SUPPORT           0x0202
+#define EOE_RESULT_NO_FILTER_SUPPORT         0x0401
+
+
+/** EOE ip4 address in network order */
+typedef struct eoe_ip4_addr {
+   uint32_t addr;
+}eoe_ip4_addr_t;
+
+/** EOE ethernet address */
+PACKED_BEGIN
+typedef struct PACKED eoe_ethaddr
+{
+   uint8_t addr[EOE_ETHADDR_LENGTH];
+} eoe_ethaddr_t;
+PACKED_END
+
+/** EoE IP request structure, storage only, no need to pack */
+typedef struct eoe_param
+{
+   uint8_t mac_set : 1;
+   uint8_t ip_set : 1;
+   uint8_t subnet_set : 1;
+   uint8_t default_gateway_set : 1;
+   uint8_t dns_ip_set : 1;
+   uint8_t dns_name_set : 1;
+   eoe_ethaddr_t mac;
+   eoe_ip4_addr_t ip;
+   eoe_ip4_addr_t subnet;
+   eoe_ip4_addr_t default_gateway;
+   eoe_ip4_addr_t dns_ip;
+   char dns_name[EOE_DNS_NAME_LENGTH];
+} eoe_param_t;
+
+/** EOE structure.
+* Used to interpret EoE mailbox packets.
+*/
+PACKED_BEGIN
+typedef struct PACKED
+{
+   ec_mbxheadert mbxheader;
+   uint16_t frameinfo1;
+   union
+   {
+      uint16_t frameinfo2;
+      uint16_t result;
+   };
+   uint8 data[0];
+} ec_EOEt;
+PACKED_END
+
+int ecx_EOEdefinehook(ecx_contextt *context, void *hook);
+int ecx_EOEsetIp(ecx_contextt *context, 
+   uint16 slave, 
+   uint8 port, 
+   eoe_param_t * ipparam, 
+   int timeout);
+int ecx_EOEgetIp(ecx_contextt *context, 
+   uint16 slave, 
+   uint8 port, 
+   eoe_param_t * ipparam, 
+   int timeout);
+int ecx_EOEsend(ecx_contextt *context, 
+   uint16 slave, 
+   uint8 port, 
+   int psize, 
+   void *p, 
+   int timeout);
+int ecx_EOErecv(ecx_contextt *context, 
+   uint16 slave, 
+   uint8 port, 
+   int * psize, 
+   void *p, 
+   int timeout);
+int ecx_EOEreadfragment(
+   ec_mbxbuft * MbxIn,
+   uint8 * rxfragmentno,
+   uint16 * rxframesize,
+   uint16 * rxframeoffset,
+   uint16 * rxframeno,
+   int * psize,
+   void *p);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff -r 000000000000 -r 543d6784d4cc SOEM/ethercatfoe.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SOEM/ethercatfoe.c	Tue Jun 11 10:29:09 2019 +0000
@@ -0,0 +1,370 @@
+/*
+ * Licensed under the GNU General Public License version 2 with exceptions. See
+ * LICENSE file in the project root for full license information
+ */
+
+/** \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. Timeout set to 0 */
+   wkc = ecx_mbxreceive(context, slave, (ec_mbxbuft *)&MbxIn, 0);
+   ec_clearmbx(&MbxOut);
+   aFOEp = (ec_FOEt *)&MbxIn;
+   FOEp = (ec_FOEt *)&MbxOut;
+   fnsize = (uint16)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. Timeout 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 = (uint16)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
diff -r 000000000000 -r 543d6784d4cc SOEM/ethercatfoe.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SOEM/ethercatfoe.h	Tue Jun 11 10:29:09 2019 +0000
@@ -0,0 +1,33 @@
+/*
+ * Licensed under the GNU General Public License version 2 with exceptions. See
+ * LICENSE file in the project root for full license information
+ */
+
+/** \file
+ * \brief
+ * Headerfile for ethercatfoe.c
+ */
+
+#ifndef _ethercatfoe_
+#define _ethercatfoe_
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#ifdef EC_VER1
+int ec_FOEdefinehook(void *hook);
+int ec_FOEread(uint16 slave, char *filename, uint32 password, int *psize, void *p, int timeout);
+int ec_FOEwrite(uint16 slave, char *filename, uint32 password, int psize, void *p, int timeout);
+#endif
+
+int ecx_FOEdefinehook(ecx_contextt *context, void *hook);
+int ecx_FOEread(ecx_contextt *context, uint16 slave, char *filename, uint32 password, int *psize, void *p, int timeout);
+int ecx_FOEwrite(ecx_contextt *context, uint16 slave, char *filename, uint32 password, int psize, void *p, int timeout);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff -r 000000000000 -r 543d6784d4cc SOEM/ethercatmain.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SOEM/ethercatmain.c	Tue Jun 11 10:29:09 2019 +0000
@@ -0,0 +1,2395 @@
+/*
+ * Licensed under the GNU General Public License version 2 with exceptions. See
+ * LICENSE file in the project root for full license information
+ */
+
+/**
+ * \file
+ * \brief
+ * Main EtherCAT functions.
+ *
+ * Initialisation, state set and read, mailbox primitives, EEPROM primitives,
+ * SII reading and processdata exchange.
+ *
+ * Defines ec_slave[]. All slave information is put in this structure.
+ * Needed for most user interaction with slaves.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include "osal.h"
+#include "oshw.h"
+#include "ethercat.h"
+
+
+/** delay in us for eeprom ready loop */
+#define EC_LOCALDELAY  200
+
+/** record for ethercat eeprom communications */
+PACKED_BEGIN
+typedef struct PACKED
+{
+   uint16    comm;
+   uint16    addr;
+   uint16    d2;
+} ec_eepromt;
+PACKED_END
+
+/** mailbox error structure */
+PACKED_BEGIN
+typedef struct PACKED
+{
+   ec_mbxheadert   MbxHeader;
+   uint16          Type;
+   uint16          Detail;
+} ec_mbxerrort;
+PACKED_END
+
+/** emergency request structure */
+PACKED_BEGIN
+typedef struct PACKED
+{
+   ec_mbxheadert   MbxHeader;
+   uint16          CANOpen;
+   uint16          ErrorCode;
+   uint8           ErrorReg;
+   uint8           bData;
+   uint16          w1,w2;
+} ec_emcyt;
+PACKED_END
+
+#ifdef EC_VER1
+/** Main slave data array.
+ *  Each slave found on the network gets its own record.
+ *  ec_slave[0] is reserved for the master. Structure gets filled
+ *  in by the configuration function ec_config().
+ */
+ec_slavet               ec_slave[EC_MAXSLAVE];
+/** number of slaves found on the network */
+int                     ec_slavecount;
+/** slave group structure */
+ec_groupt               ec_group[EC_MAXGROUP];
+
+/** cache for EEPROM read functions */
+static uint8            ec_esibuf[EC_MAXEEPBUF];
+/** bitmap for filled cache buffer bytes */
+static uint32           ec_esimap[EC_MAXEEPBITMAP];
+/** current slave for EEPROM cache buffer */
+static ec_eringt        ec_elist;
+static ec_idxstackT     ec_idxstack;
+
+/** SyncManager Communication Type struct to store data of one slave */
+static ec_SMcommtypet   ec_SMcommtype[EC_MAX_MAPT];
+/** PDO assign struct to store data of one slave */
+static ec_PDOassignt    ec_PDOassign[EC_MAX_MAPT];
+/** PDO description struct to store data of one slave */
+static ec_PDOdesct      ec_PDOdesc[EC_MAX_MAPT];
+
+/** buffer for EEPROM SM data */
+static ec_eepromSMt     ec_SM;
+/** buffer for EEPROM FMMU data */
+static ec_eepromFMMUt   ec_FMMU;
+/** Global variable TRUE if error available in error stack */
+boolean                 EcatError = FALSE;
+
+int64                   ec_DCtime;
+
+ecx_portt               ecx_port;
+ecx_redportt            ecx_redport;
+
+ecx_contextt  ecx_context = {
+    &ecx_port,          // .port          =
+    &ec_slave[0],       // .slavelist     =
+    &ec_slavecount,     // .slavecount    =
+    EC_MAXSLAVE,        // .maxslave      =
+    &ec_group[0],       // .grouplist     =
+    EC_MAXGROUP,        // .maxgroup      =
+    &ec_esibuf[0],      // .esibuf        =
+    &ec_esimap[0],      // .esimap        =
+    0,                  // .esislave      =
+    &ec_elist,          // .elist         =
+    &ec_idxstack,       // .idxstack      =
+    &EcatError,         // .ecaterror     =
+    0,                  // .DCtO          =
+    0,                  // .DCl           =
+    &ec_DCtime,         // .DCtime        =
+    &ec_SMcommtype[0],  // .SMcommtype    =
+    &ec_PDOassign[0],   // .PDOassign     =
+    &ec_PDOdesc[0],     // .PDOdesc       =
+    &ec_SM,             // .eepSM         =
+    &ec_FMMU,           // .eepFMMU       =
+    NULL,               // .FOEhook()
+    NULL                // .EOEhook()
+};
+#endif
+
+/** Create list over available network adapters.
+ *
+ * @return First element in list over available network adapters.
+ */
+ec_adaptert * ec_find_adapters (void)
+{
+   ec_adaptert * ret_adapter;
+
+   ret_adapter = oshw_find_adapters ();
+
+   return ret_adapter;
+}
+
+/** Free dynamically allocated list over available network adapters.
+ *
+ * @param[in] adapter = Struct holding adapter name, description and pointer to next.
+ */
+void ec_free_adapters (ec_adaptert * adapter)
+{
+   oshw_free_adapters (adapter);
+}
+
+/** Pushes an error on the error list.
+ *
+ * @param[in] context        = context struct
+ * @param[in] Ec pointer describing the error.
+ */
+void ecx_pusherror(ecx_contextt *context, const ec_errort *Ec)
+{
+   context->elist->Error[context->elist->head] = *Ec;
+   context->elist->Error[context->elist->head].Signal = TRUE;
+   context->elist->head++;
+   if (context->elist->head > EC_MAXELIST)
+   {
+      context->elist->head = 0;
+   }
+   if (context->elist->head == context->elist->tail)
+   {
+      context->elist->tail++;
+   }
+   if (context->elist->tail > EC_MAXELIST)
+   {
+      context->elist->tail = 0;
+   }
+   *(context->ecaterror) = TRUE;
+}
+
+/** Pops an error from the list.
+ *
+ * @param[in] context        = context struct
+ * @param[out] Ec = Struct describing the error.
+ * @return TRUE if an error was popped.
+ */
+boolean ecx_poperror(ecx_contextt *context, ec_errort *Ec)
+{
+   boolean notEmpty = (context->elist->head != context->elist->tail);
+
+   *Ec = context->elist->Error[context->elist->tail];
+   context->elist->Error[context->elist->tail].Signal = FALSE;
+   if (notEmpty)
+   {
+      context->elist->tail++;
+      if (context->elist->tail > EC_MAXELIST)
+      {
+         context->elist->tail = 0;
+      }
+   }
+   else
+   {
+      *(context->ecaterror) = FALSE;
+   }
+   return notEmpty;
+}
+
+/** Check if error list has entries.
+ *
+ * @param[in] context        = context struct
+ * @return TRUE if error list contains entries.
+ */
+boolean ecx_iserror(ecx_contextt *context)
+{
+   return (context->elist->head != context->elist->tail);
+}
+
+/** Report packet error
+ *
+ * @param[in]  context        = context struct
+ * @param[in]  Slave      = Slave number
+ * @param[in]  Index      = Index that generated error
+ * @param[in]  SubIdx     = Subindex that generated error
+ * @param[in]  ErrorCode  = Error code
+ */
+void ecx_packeterror(ecx_contextt *context, uint16 Slave, uint16 Index, uint8 SubIdx, uint16 ErrorCode)
+{
+   ec_errort Ec;
+
+   memset(&Ec, 0, sizeof(Ec));
+   Ec.Time = osal_current_time();
+   Ec.Slave = Slave;
+   Ec.Index = Index;
+   Ec.SubIdx = SubIdx;
+   *(context->ecaterror) = TRUE;
+   Ec.Etype = EC_ERR_TYPE_PACKET_ERROR;
+   Ec.ErrorCode = ErrorCode;
+   ecx_pusherror(context, &Ec);
+}
+
+/** Report Mailbox Error
+ *
+ * @param[in]  context        = context struct
+ * @param[in]  Slave        = Slave number
+ * @param[in]  Detail       = Following EtherCAT specification
+ */
+static void ecx_mbxerror(ecx_contextt *context, uint16 Slave,uint16 Detail)
+{
+   ec_errort Ec;
+
+   memset(&Ec, 0, sizeof(Ec));
+   Ec.Time = osal_current_time();
+   Ec.Slave = Slave;
+   Ec.Index = 0;
+   Ec.SubIdx = 0;
+   Ec.Etype = EC_ERR_TYPE_MBX_ERROR;
+   Ec.ErrorCode = Detail;
+   ecx_pusherror(context, &Ec);
+}
+
+/** Report Mailbox Emergency Error
+ *
+ * @param[in]  context        = context struct
+ * @param[in]  Slave      = Slave number
+ * @param[in]  ErrorCode  = Following EtherCAT specification
+ * @param[in]  ErrorReg
+ * @param[in]  b1
+ * @param[in]  w1
+ * @param[in]  w2
+ */
+static void ecx_mbxemergencyerror(ecx_contextt *context, uint16 Slave,uint16 ErrorCode,uint16 ErrorReg,
+    uint8 b1, uint16 w1, uint16 w2)
+{
+   ec_errort Ec;
+
+   memset(&Ec, 0, sizeof(Ec));
+   Ec.Time = osal_current_time();
+   Ec.Slave = Slave;
+   Ec.Index = 0;
+   Ec.SubIdx = 0;
+   Ec.Etype = EC_ERR_TYPE_EMERGENCY;
+   Ec.ErrorCode = ErrorCode;
+   Ec.ErrorReg = (uint8)ErrorReg;
+   Ec.b1 = b1;
+   Ec.w1 = w1;
+   Ec.w2 = w2;
+   ecx_pusherror(context, &Ec);
+}
+
+/** Initialise lib in single NIC mode
+ * @param[in]  context = context struct
+ * @param[in] ifname   = Dev name, f.e. "eth0"
+ * @return >0 if OK
+ */
+int ecx_init(ecx_contextt *context, const char * ifname)
+{
+   return ecx_setupnic(context->port, ifname, FALSE);
+}
+
+/** Initialise lib in redundant NIC mode
+ * @param[in]  context  = context struct
+ * @param[in]  redport  = pointer to redport, redundant port data
+ * @param[in]  ifname   = Primary Dev name, f.e. "eth0"
+ * @param[in]  if2name  = Secondary Dev name, f.e. "eth1"
+ * @return >0 if OK
+ */
+int ecx_init_redundant(ecx_contextt *context, ecx_redportt *redport, const char *ifname, char *if2name)
+{
+   int rval, zbuf;
+   ec_etherheadert *ehp;
+
+   context->port->redport = redport;
+   ecx_setupnic(context->port, ifname, FALSE);
+   rval = ecx_setupnic(context->port, if2name, TRUE);
+   /* prepare "dummy" BRD tx frame for redundant operation */
+   ehp = (ec_etherheadert *)&(context->port->txbuf2);
+   ehp->sa1 = oshw_htons(secMAC[0]);
+   zbuf = 0;
+   ecx_setupdatagram(context->port, &(context->port->txbuf2), EC_CMD_BRD, 0, 0x0000, 0x0000, 2, &zbuf);
+   context->port->txbuflength2 = ETH_HEADERSIZE + EC_HEADERSIZE + EC_WKCSIZE + 2;
+
+   return rval;
+}
+
+/** Close lib.
+ * @param[in]  context        = context struct
+ */
+void ecx_close(ecx_contextt *context)
+{
+   ecx_closenic(context->port);
+};
+
+/** Read one byte from slave EEPROM via cache.
+ *  If the cache location is empty then a read request is made to the slave.
+ *  Depending on the slave capabilities the request is 4 or 8 bytes.
+ *  @param[in] context = context struct
+ *  @param[in] slave   = slave number
+ *  @param[in] address = eeprom address in bytes (slave uses words)
+ *  @return requested byte, if not available then 0xff
+ */
+uint8 ecx_siigetbyte(ecx_contextt *context, uint16 slave, uint16 address)
+{
+   uint16 configadr, eadr;
+   uint64 edat64;
+   uint32 edat32;
+   uint16 mapw, mapb;
+   int lp,cnt;
+   uint8 retval;
+
+   retval = 0xff;
+   if (slave != context->esislave) /* not the same slave? */
+   {
+      memset(context->esimap, 0x00, EC_MAXEEPBITMAP * sizeof(uint32)); /* clear esibuf cache map */
+      context->esislave = slave;
+   }
+   if (address < EC_MAXEEPBUF)
+   {
+      mapw = address >> 5;
+      mapb = address - (mapw << 5);
+      if (context->esimap[mapw] & (uint32)(1 << mapb))
+      {
+         /* byte is already in buffer */
+         retval = context->esibuf[address];
+      }
+      else
+      {
+         /* byte is not in buffer, put it there */
+         configadr = context->slavelist[slave].configadr;
+         ecx_eeprom2master(context, slave); /* set eeprom control to master */
+         eadr = address >> 1;
+         edat64 = ecx_readeepromFP (context, configadr, eadr, EC_TIMEOUTEEP);
+         /* 8 byte response */
+         if (context->slavelist[slave].eep_8byte)
+         {
+            put_unaligned64(edat64, &(context->esibuf[eadr << 1]));
+            cnt = 8;
+         }
+         /* 4 byte response */
+         else
+         {
+            edat32 = (uint32)edat64;
+            put_unaligned32(edat32, &(context->esibuf[eadr << 1]));
+            cnt = 4;
+         }
+         /* find bitmap location */
+         mapw = eadr >> 4;
+         mapb = (eadr << 1) - (mapw << 5);
+         for(lp = 0 ; lp < cnt ; lp++)
+         {
+            /* set bitmap for each byte that is read */
+            context->esimap[mapw] |= (1 << mapb);
+            mapb++;
+            if (mapb > 31)
+            {
+               mapb = 0;
+               mapw++;
+            }
+         }
+         retval = context->esibuf[address];
+      }
+   }
+
+   return retval;
+}
+
+/** Find SII section header in slave EEPROM.
+ *  @param[in]  context        = context struct
+ *  @param[in] slave   = slave number
+ *  @param[in] cat     = section category
+ *  @return byte address of section at section length entry, if not available then 0
+ */
+int16 ecx_siifind(ecx_contextt *context, uint16 slave, uint16 cat)
+{
+   int16 a;
+   uint16 p;
+   uint8 eectl = context->slavelist[slave].eep_pdi;
+
+   a = ECT_SII_START << 1;
+   /* read first SII section category */
+   p = ecx_siigetbyte(context, slave, a++);
+   p += (ecx_siigetbyte(context, slave, a++) << 8);
+   /* traverse SII while category is not found and not EOF */
+   while ((p != cat) && (p != 0xffff))
+   {
+      /* read section length */
+      p = ecx_siigetbyte(context, slave, a++);
+      p += (ecx_siigetbyte(context, slave, a++) << 8);
+      /* locate next section category */
+      a += p << 1;
+      /* read section category */
+      p = ecx_siigetbyte(context, slave, a++);
+      p += (ecx_siigetbyte(context, slave, a++) << 8);
+   }
+   if (p != cat)
+   {
+      a = 0;
+   }
+   if (eectl)
+   {
+      ecx_eeprom2pdi(context, slave); /* if eeprom control was previously pdi then restore */
+   }
+
+   return a;
+}
+
+/** Get string from SII string section in slave EEPROM.
+ *  @param[in]  context = context struct
+ *  @param[out] str     = requested string, 0x00 if not found
+ *  @param[in]  slave   = slave number
+ *  @param[in]  Sn      = string number
+ */
+void ecx_siistring(ecx_contextt *context, char *str, uint16 slave, uint16 Sn)
+{
+   uint16 a,i,j,l,n,ba;
+   char *ptr;
+   uint8 eectl = context->slavelist[slave].eep_pdi;
+
+   ptr = str;
+   a = ecx_siifind (context, slave, ECT_SII_STRING); /* find string section */
+   if (a > 0)
+   {
+      ba = a + 2; /* skip SII section header */
+      n = ecx_siigetbyte(context, slave, ba++); /* read number of strings in section */
+      if (Sn <= n) /* is req string available? */
+      {
+         for (i = 1; i <= Sn; i++) /* walk through strings */
+         {
+            l = ecx_siigetbyte(context, slave, ba++); /* length of this string */
+            if (i < Sn)
+            {
+               ba += l;
+            }
+            else
+            {
+               ptr = str;
+               for (j = 1; j <= l; j++) /* copy one string */
+               {
+                  if(j <= EC_MAXNAME)
+                  {
+                     *ptr = (char)ecx_siigetbyte(context, slave, ba++);
+                     ptr++;
+                  }
+                  else
+                  {
+                     ba++;
+                  }
+               }
+            }
+         }
+         *ptr = 0; /* add zero terminator */
+      }
+      else
+      {
+         ptr = str;
+         *ptr = 0; /* empty string */
+      }
+   }
+   if (eectl)
+   {
+      ecx_eeprom2pdi(context, slave); /* if eeprom control was previously pdi then restore */
+   }
+}
+
+/** Get FMMU data from SII FMMU section in slave EEPROM.
+ *  @param[in]  context = context struct
+ *  @param[in]  slave   = slave number
+ *  @param[out] FMMU    = FMMU struct from SII, max. 4 FMMU's
+ *  @return number of FMMU's defined in section
+ */
+uint16 ecx_siiFMMU(ecx_contextt *context, uint16 slave, ec_eepromFMMUt* FMMU)
+{
+   uint16  a;
+   uint8 eectl = context->slavelist[slave].eep_pdi;
+
+   FMMU->nFMMU = 0;
+   FMMU->FMMU0 = 0;
+   FMMU->FMMU1 = 0;
+   FMMU->FMMU2 = 0;
+   FMMU->FMMU3 = 0;
+   FMMU->Startpos = ecx_siifind(context, slave, ECT_SII_FMMU);
+
+   if (FMMU->Startpos > 0)
+   {
+      a = FMMU->Startpos;
+      FMMU->nFMMU = ecx_siigetbyte(context, slave, a++);
+      FMMU->nFMMU += (ecx_siigetbyte(context, slave, a++) << 8);
+      FMMU->nFMMU *= 2;
+      FMMU->FMMU0 = ecx_siigetbyte(context, slave, a++);
+      FMMU->FMMU1 = ecx_siigetbyte(context, slave, a++);
+      if (FMMU->nFMMU > 2)
+      {
+         FMMU->FMMU2 = ecx_siigetbyte(context, slave, a++);
+         FMMU->FMMU3 = ecx_siigetbyte(context, slave, a++);
+      }
+   }
+   if (eectl)
+   {
+      ecx_eeprom2pdi(context, slave); /* if eeprom control was previously pdi then restore */
+   }
+
+   return FMMU->nFMMU;
+}
+
+/** Get SM data from SII SM section in slave EEPROM.
+ *  @param[in]  context = context struct
+ *  @param[in]  slave   = slave number
+ *  @param[out] SM      = first SM struct from SII
+ *  @return number of SM's defined in section
+ */
+uint16 ecx_siiSM(ecx_contextt *context, uint16 slave, ec_eepromSMt* SM)
+{
+   uint16 a,w;
+   uint8 eectl = context->slavelist[slave].eep_pdi;
+
+   SM->nSM = 0;
+   SM->Startpos = ecx_siifind(context, slave, ECT_SII_SM);
+   if (SM->Startpos > 0)
+   {
+      a = SM->Startpos;
+      w = ecx_siigetbyte(context, slave, a++);
+      w += (ecx_siigetbyte(context, slave, a++) << 8);
+      SM->nSM = (w / 4);
+      SM->PhStart = ecx_siigetbyte(context, slave, a++);
+      SM->PhStart += (ecx_siigetbyte(context, slave, a++) << 8);
+      SM->Plength = ecx_siigetbyte(context, slave, a++);
+      SM->Plength += (ecx_siigetbyte(context, slave, a++) << 8);
+      SM->Creg = ecx_siigetbyte(context, slave, a++);
+      SM->Sreg = ecx_siigetbyte(context, slave, a++);
+      SM->Activate = ecx_siigetbyte(context, slave, a++);
+      SM->PDIctrl = ecx_siigetbyte(context, slave, a++);
+   }
+   if (eectl)
+   {
+      ecx_eeprom2pdi(context, slave); /* if eeprom control was previously pdi then restore */
+   }
+
+   return SM->nSM;
+}
+
+/** Get next SM data from SII SM section in slave EEPROM.
+ *  @param[in]  context = context struct
+ *  @param[in]  slave   = slave number
+ *  @param[out] SM      = first SM struct from SII
+ *  @param[in]  n       = SM number
+ *  @return >0 if OK
+ */
+uint16 ecx_siiSMnext(ecx_contextt *context, uint16 slave, ec_eepromSMt* SM, uint16 n)
+{
+   uint16 a;
+   uint16 retVal = 0;
+   uint8 eectl = context->slavelist[slave].eep_pdi;
+
+   if (n < SM->nSM)
+   {
+      a = SM->Startpos + 2 + (n * 8);
+      SM->PhStart = ecx_siigetbyte(context, slave, a++);
+      SM->PhStart += (ecx_siigetbyte(context, slave, a++) << 8);
+      SM->Plength = ecx_siigetbyte(context, slave, a++);
+      SM->Plength += (ecx_siigetbyte(context, slave, a++) << 8);
+      SM->Creg = ecx_siigetbyte(context, slave, a++);
+      SM->Sreg = ecx_siigetbyte(context, slave, a++);
+      SM->Activate = ecx_siigetbyte(context, slave, a++);
+      SM->PDIctrl = ecx_siigetbyte(context, slave, a++);
+      retVal = 1;
+   }
+   if (eectl)
+   {
+      ecx_eeprom2pdi(context, slave); /* if eeprom control was previously pdi then restore */
+   }
+
+   return retVal;
+}
+
+/** Get PDO data from SII PDO section in slave EEPROM.
+ *  @param[in]  context = context struct
+ *  @param[in]  slave   = slave number
+ *  @param[out] PDO     = PDO struct from SII
+ *  @param[in]  t       = 0=RXPDO 1=TXPDO
+ *  @return mapping size in bits of PDO
+ */
+int ecx_siiPDO(ecx_contextt *context, uint16 slave, ec_eepromPDOt* PDO, uint8 t)
+{
+   uint16 a , w, c, e, er, Size;
+   uint8 eectl = context->slavelist[slave].eep_pdi;
+
+   Size = 0;
+   PDO->nPDO = 0;
+   PDO->Length = 0;
+   PDO->Index[1] = 0;
+   for (c = 0 ; c < EC_MAXSM ; c++) PDO->SMbitsize[c] = 0;
+   if (t > 1)
+      t = 1;
+   PDO->Startpos = ecx_siifind(context, slave, ECT_SII_PDO + t);
+   if (PDO->Startpos > 0)
+   {
+      a = PDO->Startpos;
+      w = ecx_siigetbyte(context, slave, a++);
+      w += (ecx_siigetbyte(context, slave, a++) << 8);
+      PDO->Length = w;
+      c = 1;
+      /* traverse through all PDOs */
+      do
+      {
+         PDO->nPDO++;
+         PDO->Index[PDO->nPDO] = ecx_siigetbyte(context, slave, a++);
+         PDO->Index[PDO->nPDO] += (ecx_siigetbyte(context, slave, a++) << 8);
+         PDO->BitSize[PDO->nPDO] = 0;
+         c++;
+         e = ecx_siigetbyte(context, slave, a++);
+         PDO->SyncM[PDO->nPDO] = ecx_siigetbyte(context, slave, a++);
+         a += 4;
+         c += 2;
+         if (PDO->SyncM[PDO->nPDO] < EC_MAXSM) /* active and in range SM? */
+         {
+            /* read all entries defined in PDO */
+            for (er = 1; er <= e; er++)
+            {
+               c += 4;
+               a += 5;
+               PDO->BitSize[PDO->nPDO] += ecx_siigetbyte(context, slave, a++);
+               a += 2;
+            }
+            PDO->SMbitsize[ PDO->SyncM[PDO->nPDO] ] += PDO->BitSize[PDO->nPDO];
+            Size += PDO->BitSize[PDO->nPDO];
+            c++;
+         }
+         else /* PDO deactivated because SM is 0xff or > EC_MAXSM */
+         {
+            c += 4 * e;
+            a += 8 * e;
+            c++;
+         }
+         if (PDO->nPDO >= (EC_MAXEEPDO - 1))
+         {
+            c = PDO->Length; /* limit number of PDO entries in buffer */
+         }
+      }
+      while (c < PDO->Length);
+   }
+   if (eectl)
+   {
+      ecx_eeprom2pdi(context, slave); /* if eeprom control was previously pdi then restore */
+   }
+
+   return (Size);
+}
+
+#define MAX_FPRD_MULTI 64
+
+int ecx_FPRD_multi(ecx_contextt *context, int n, uint16 *configlst, ec_alstatust *slstatlst, int timeout)
+{
+   int wkc;
+   uint8 idx;
+   ecx_portt *port;
+   int sldatapos[MAX_FPRD_MULTI];
+   int slcnt;
+
+   port = context->port;
+   idx = ecx_getindex(port);
+   slcnt = 0;
+   ecx_setupdatagram(port, &(port->txbuf[idx]), EC_CMD_FPRD, idx,
+      *(configlst + slcnt), ECT_REG_ALSTAT, sizeof(ec_alstatust), slstatlst + slcnt);
+   sldatapos[slcnt] = EC_HEADERSIZE;
+   while(++slcnt < (n - 1))
+   {
+      sldatapos[slcnt] = ecx_adddatagram(port, &(port->txbuf[idx]), EC_CMD_FPRD, idx, TRUE,
+                            *(configlst + slcnt), ECT_REG_ALSTAT, sizeof(ec_alstatust), slstatlst + slcnt);
+   }
+   if(slcnt < n)
+   {
+      sldatapos[slcnt] = ecx_adddatagram(port, &(port->txbuf[idx]), EC_CMD_FPRD, idx, FALSE,
+                            *(configlst + slcnt), ECT_REG_ALSTAT, sizeof(ec_alstatust), slstatlst + slcnt);
+   }
+   wkc = ecx_srconfirm(port, idx, timeout);
+   if (wkc >= 0)
+   {
+      for(slcnt = 0 ; slcnt < n ; slcnt++)
+      {
+         memcpy(slstatlst + slcnt, &(port->rxbuf[idx][sldatapos[slcnt]]), sizeof(ec_alstatust));
+      }
+   }
+   ecx_setbufstat(port, idx, EC_BUF_EMPTY);
+   return wkc;
+}
+
+/** Read all slave states in ec_slave.
+ * @param[in] context = context struct
+ * @return lowest state found
+ */
+int ecx_readstate(ecx_contextt *context)
+{
+   uint16 slave, fslave, lslave, configadr, lowest, rval, bitwisestate;
+   ec_alstatust sl[MAX_FPRD_MULTI];
+   uint16 slca[MAX_FPRD_MULTI];
+   boolean noerrorflag, allslavessamestate;
+   boolean allslavespresent = FALSE;
+   int wkc;
+
+   /* Try to establish the state of all slaves sending only one broadcast datagram.
+    * This way a number of datagrams equal to the number of slaves will be sent only if needed.*/
+   rval = 0;
+   wkc = ecx_BRD(context->port, 0, ECT_REG_ALSTAT, sizeof(rval), &rval, EC_TIMEOUTRET);
+
+   if(wkc >= *(context->slavecount))
+   {
+      allslavespresent = TRUE;
+   }
+
+   rval = etohs(rval);
+   bitwisestate = (rval & 0x0f);
+
+   if ((rval & EC_STATE_ERROR) == 0)
+   {
+      noerrorflag = TRUE;
+      context->slavelist[0].ALstatuscode = 0;
+   }   
+   else
+   {
+      noerrorflag = FALSE;
+   }
+
+   switch (bitwisestate)
+   {
+      case EC_STATE_INIT:
+      case EC_STATE_PRE_OP:
+      case EC_STATE_BOOT:
+      case EC_STATE_SAFE_OP:
+      case EC_STATE_OPERATIONAL:
+         allslavessamestate = TRUE;
+         context->slavelist[0].state = bitwisestate;
+         break;
+      default:
+         allslavessamestate = FALSE;
+         break;
+   }
+    
+   if (noerrorflag && allslavessamestate && allslavespresent)
+   {
+      /* No slave has toggled the error flag so the alstatuscode
+       * (even if different from 0) should be ignored and
+       * the slaves have reached the same state so the internal state
+       * can be updated without sending any datagram. */
+      for (slave = 1; slave <= *(context->slavecount); slave++)
+      {
+         context->slavelist[slave].ALstatuscode = 0x0000;
+         context->slavelist[slave].state = bitwisestate;
+      }
+      lowest = bitwisestate;
+   }
+   else
+   {
+      /* Not all slaves have the same state or at least one is in error so one datagram per slave
+       * is needed. */
+      context->slavelist[0].ALstatuscode = 0;
+      lowest = 0xff;
+      fslave = 1;
+      do
+      {
+         lslave = *(context->slavecount);
+         if ((lslave - fslave) >= MAX_FPRD_MULTI)
+         {
+            lslave = fslave + MAX_FPRD_MULTI - 1;
+         }
+         for (slave = fslave; slave <= lslave; slave++)
+         {
+            const ec_alstatust zero = { 0, 0, 0 };
+
+            configadr = context->slavelist[slave].configadr;
+            slca[slave - fslave] = configadr;
+            sl[slave - fslave] = zero;
+         }
+         ecx_FPRD_multi(context, (lslave - fslave) + 1, &(slca[0]), &(sl[0]), EC_TIMEOUTRET3);
+         for (slave = fslave; slave <= lslave; slave++)
+         {
+            configadr = context->slavelist[slave].configadr;
+            rval = etohs(sl[slave - fslave].alstatus);
+            context->slavelist[slave].ALstatuscode = etohs(sl[slave - fslave].alstatuscode);
+            if ((rval & 0xf) < lowest)
+            {
+               lowest = (rval & 0xf);
+            }
+            context->slavelist[slave].state = rval;
+            context->slavelist[0].ALstatuscode |= context->slavelist[slave].ALstatuscode;
+         }
+         fslave = lslave + 1;
+      } while (lslave < *(context->slavecount));
+      context->slavelist[0].state = lowest;
+   }
+  
+   return lowest;
+}
+
+/** Write slave state, if slave = 0 then write to all slaves.
+ * The function does not check if the actual state is changed.
+ * @param[in]  context        = context struct
+ * @param[in] slave    = Slave number, 0 = master
+ * @return Workcounter or EC_NOFRAME
+ */
+int ecx_writestate(ecx_contextt *context, uint16 slave)
+{
+   int ret;
+   uint16 configadr, slstate;
+
+   if (slave == 0)
+   {
+      slstate = htoes(context->slavelist[slave].state);
+      ret = ecx_BWR(context->port, 0, ECT_REG_ALCTL, sizeof(slstate),
+	            &slstate, EC_TIMEOUTRET3);
+   }
+   else
+   {
+      configadr = context->slavelist[slave].configadr;
+
+      ret = ecx_FPWRw(context->port, configadr, ECT_REG_ALCTL,
+	        htoes(context->slavelist[slave].state), EC_TIMEOUTRET3);
+   }
+   return ret;
+}
+
+/** Check actual slave state.
+ * This is a blocking function.
+ * To refresh the state of all slaves ecx_readstate()should be called
+ * @param[in] context     = context struct
+ * @param[in] slave       = Slave number, 0 = all slaves (only the "slavelist[0].state" is refreshed)
+ * @param[in] reqstate    = Requested state
+ * @param[in] timeout     = Timeout value in us
+ * @return Requested state, or found state after timeout.
+ */
+uint16 ecx_statecheck(ecx_contextt *context, uint16 slave, uint16 reqstate, int timeout)
+{
+   uint16 configadr, state, rval;
+   ec_alstatust slstat;
+   osal_timert timer;
+
+   if ( slave > *(context->slavecount) )
+   {
+      return 0;
+   }
+   osal_timer_start(&timer, timeout);
+   configadr = context->slavelist[slave].configadr;
+   do
+   {
+      if (slave < 1)
+      {
+         rval = 0;
+         ecx_BRD(context->port, 0, ECT_REG_ALSTAT, sizeof(rval), &rval , EC_TIMEOUTRET);
+         rval = etohs(rval);
+      }
+      else
+      {
+         slstat.alstatus = 0;
+         slstat.alstatuscode = 0;
+         ecx_FPRD(context->port, configadr, ECT_REG_ALSTAT, sizeof(slstat), &slstat, EC_TIMEOUTRET);
+         rval = etohs(slstat.alstatus);
+         context->slavelist[slave].ALstatuscode = etohs(slstat.alstatuscode);
+      }
+      state = rval & 0x000f; /* read slave status */
+      if (state != reqstate)
+      {
+         osal_usleep(1000);
+      }
+   }
+   while ((state != reqstate) && (osal_timer_is_expired(&timer) == FALSE));
+   context->slavelist[slave].state = rval;
+
+   return state;
+}
+
+/** Get index of next mailbox counter value.
+ * Used for Mailbox Link Layer.
+ * @param[in] cnt     = Mailbox counter value [0..7]
+ * @return next mailbox counter value
+ */
+uint8 ec_nextmbxcnt(uint8 cnt)
+{
+   cnt++;
+   if (cnt > 7)
+   {
+      cnt = 1; /* wrap around to 1, not 0 */
+   }
+
+   return cnt;
+}
+
+/** Clear mailbox buffer.
+ * @param[out] Mbx     = Mailbox buffer to clear
+ */
+void ec_clearmbx(ec_mbxbuft *Mbx)
+{
+    memset(Mbx, 0x00, EC_MAXMBX);
+}
+
+/** Check if IN mailbox of slave is empty.
+ * @param[in] context  = context struct
+ * @param[in] slave    = Slave number
+ * @param[in] timeout  = Timeout in us
+ * @return >0 is success
+ */
+int ecx_mbxempty(ecx_contextt *context, uint16 slave, int timeout)
+{
+   uint16 configadr;
+   uint8 SMstat;
+   int wkc;
+   osal_timert timer;
+
+   osal_timer_start(&timer, timeout);
+   configadr = context->slavelist[slave].configadr;
+   do
+   {
+      SMstat = 0;
+      wkc = ecx_FPRD(context->port, configadr, ECT_REG_SM0STAT, sizeof(SMstat), &SMstat, EC_TIMEOUTRET);
+      SMstat = etohs(SMstat);
+      if (((SMstat & 0x08) != 0) && (timeout > EC_LOCALDELAY))
+      {
+         osal_usleep(EC_LOCALDELAY);
+      }
+   }
+   while (((wkc <= 0) || ((SMstat & 0x08) != 0)) && (osal_timer_is_expired(&timer) == FALSE));
+
+   if ((wkc > 0) && ((SMstat & 0x08) == 0))
+   {
+      return 1;
+   }
+
+   return 0;
+}
+
+/** Write IN mailbox to slave.
+ * @param[in]  context    = context struct
+ * @param[in]  slave      = Slave number
+ * @param[out] mbx        = Mailbox data
+ * @param[in]  timeout    = Timeout in us
+ * @return Work counter (>0 is success)
+ */
+int ecx_mbxsend(ecx_contextt *context, uint16 slave,ec_mbxbuft *mbx, int timeout)
+{
+   uint16 mbxwo,mbxl,configadr;
+   int wkc;
+
+   wkc = 0;
+   configadr = context->slavelist[slave].configadr;
+   mbxl = context->slavelist[slave].mbx_l;
+   if ((mbxl > 0) && (mbxl <= EC_MAXMBX))
+   {
+      if (ecx_mbxempty(context, slave, timeout))
+      {
+         mbxwo = context->slavelist[slave].mbx_wo;
+         /* write slave in mailbox */
+         wkc = ecx_FPWR(context->port, configadr, mbxwo, mbxl, mbx, EC_TIMEOUTRET3);
+      }
+      else
+      {
+         wkc = 0;
+      }
+   }
+
+   return wkc;
+}
+
+/** Read OUT mailbox from slave.
+ * Supports Mailbox Link Layer with repeat requests.
+ * @param[in]  context    = context struct
+ * @param[in]  slave      = Slave number
+ * @param[out] mbx        = Mailbox data
+ * @param[in]  timeout    = Timeout in us
+ * @return Work counter (>0 is success)
+ */
+int ecx_mbxreceive(ecx_contextt *context, uint16 slave, ec_mbxbuft *mbx, int timeout)
+{
+   uint16 mbxro,mbxl,configadr;
+   int wkc=0;
+   int wkc2;
+   uint16 SMstat;
+   uint8 SMcontr;
+   ec_mbxheadert *mbxh;
+   ec_emcyt *EMp;
+   ec_mbxerrort *MBXEp;
+
+   configadr = context->slavelist[slave].configadr;
+   mbxl = context->slavelist[slave].mbx_rl;
+   if ((mbxl > 0) && (mbxl <= EC_MAXMBX))
+   {
+      osal_timert timer;
+
+      osal_timer_start(&timer, timeout);
+      wkc = 0;
+      do /* wait for read mailbox available */
+      {
+         SMstat = 0;
+         wkc = ecx_FPRD(context->port, configadr, ECT_REG_SM1STAT, sizeof(SMstat), &SMstat, EC_TIMEOUTRET);
+         SMstat = etohs(SMstat);
+         if (((SMstat & 0x08) == 0) && (timeout > EC_LOCALDELAY))
+         {
+            osal_usleep(EC_LOCALDELAY);
+         }
+      }
+      while (((wkc <= 0) || ((SMstat & 0x08) == 0)) && (osal_timer_is_expired(&timer) == FALSE));
+
+      if ((wkc > 0) && ((SMstat & 0x08) > 0)) /* read mailbox available ? */
+      {
+         mbxro = context->slavelist[slave].mbx_ro;
+         mbxh = (ec_mbxheadert *)mbx;
+         do
+         {
+            wkc = ecx_FPRD(context->port, configadr, mbxro, mbxl, mbx, EC_TIMEOUTRET); /* get mailbox */
+            if ((wkc > 0) && ((mbxh->mbxtype & 0x0f) == 0x00)) /* Mailbox error response? */
+            {
+               MBXEp = (ec_mbxerrort *)mbx;
+               ecx_mbxerror(context, slave, etohs(MBXEp->Detail));
+               wkc = 0; /* prevent emergency to cascade up, it is already handled. */
+            }
+            else if ((wkc > 0) && ((mbxh->mbxtype & 0x0f) == ECT_MBXT_COE)) /* CoE response? */
+            {
+               EMp = (ec_emcyt *)mbx;
+               if ((etohs(EMp->CANOpen) >> 12) == 0x01) /* Emergency request? */
+               {
+                  ecx_mbxemergencyerror(context, slave, etohs(EMp->ErrorCode), EMp->ErrorReg,
+                          EMp->bData, etohs(EMp->w1), etohs(EMp->w2));
+                  wkc = 0; /* prevent emergency to cascade up, it is already handled. */
+               }
+            }
+            else if ((wkc > 0) && ((mbxh->mbxtype & 0x0f) == ECT_MBXT_EOE)) /* EoE response? */
+            {
+               ec_EOEt * eoembx = (ec_EOEt *)mbx;
+               uint16 frameinfo1 = etohs(eoembx->frameinfo1);
+               /* All non fragment data frame types are expected to be handled by
+               * slave send/receive API if the EoE hook is set
+               */
+               if (EOE_HDR_FRAME_TYPE_GET(frameinfo1) == EOE_FRAG_DATA)
+               {
+                  if (context->EOEhook)
+                  {
+                     if (context->EOEhook(context, slave, eoembx) > 0)
+                     {
+                        /* Fragment handled by EoE hook */
+                        wkc = 0;
+                     }
+                  }
+               }
+            }
+            else
+            {
+               if (wkc <= 0) /* read mailbox lost */
+               {
+                  SMstat ^= 0x0200; /* toggle repeat request */
+                  SMstat = htoes(SMstat);
+                  wkc2 = ecx_FPWR(context->port, configadr, ECT_REG_SM1STAT, sizeof(SMstat), &SMstat, EC_TIMEOUTRET);
+                  SMstat = etohs(SMstat);
+                  do /* wait for toggle ack */
+                  {
+                     wkc2 = ecx_FPRD(context->port, configadr, ECT_REG_SM1CONTR, sizeof(SMcontr), &SMcontr, EC_TIMEOUTRET);
+                   } while (((wkc2 <= 0) || ((SMcontr & 0x02) != (HI_BYTE(SMstat) & 0x02))) && (osal_timer_is_expired(&timer) == FALSE));
+                  do /* wait for read mailbox available */
+                  {
+                     wkc2 = ecx_FPRD(context->port, configadr, ECT_REG_SM1STAT, sizeof(SMstat), &SMstat, EC_TIMEOUTRET);
+                     SMstat = etohs(SMstat);
+                     if (((SMstat & 0x08) == 0) && (timeout > EC_LOCALDELAY))
+                     {
+                        osal_usleep(EC_LOCALDELAY);
+                     }
+                  } while (((wkc2 <= 0) || ((SMstat & 0x08) == 0)) && (osal_timer_is_expired(&timer) == FALSE));
+               }
+            }
+         } while ((wkc <= 0) && (osal_timer_is_expired(&timer) == FALSE)); /* if WKC<=0 repeat */
+      }
+      else /* no read mailbox available */
+      {
+          wkc = 0;
+      }
+   }
+
+   return wkc;
+}
+
+/** Dump complete EEPROM data from slave in buffer.
+ * @param[in]  context  = context struct
+ * @param[in]  slave    = Slave number
+ * @param[out] esibuf   = EEPROM data buffer, make sure it is big enough.
+ */
+void ecx_esidump(ecx_contextt *context, uint16 slave, uint8 *esibuf)
+{
+   int address, incr;
+   uint16 configadr;
+   uint64 *p64;
+   uint16 *p16;
+   uint64 edat;
+   uint8 eectl = context->slavelist[slave].eep_pdi;
+
+   ecx_eeprom2master(context, slave); /* set eeprom control to master */
+   configadr = context->slavelist[slave].configadr;
+   address = ECT_SII_START;
+   p16=(uint16*)esibuf;
+   if (context->slavelist[slave].eep_8byte)
+   {
+      incr = 4;
+   }
+   else
+   {
+      incr = 2;
+   }
+   do
+   {
+      edat = ecx_readeepromFP(context, configadr, address, EC_TIMEOUTEEP);
+      p64 = (uint64*)p16;
+      *p64 = edat;
+      p16 += incr;
+      address += incr;
+   } while ((address <= (EC_MAXEEPBUF >> 1)) && ((uint32)edat != 0xffffffff));
+
+   if (eectl)
+   {
+      ecx_eeprom2pdi(context, slave); /* if eeprom control was previously pdi then restore */
+   }
+}
+
+/** Read EEPROM from slave bypassing cache.
+ * @param[in] context   = context struct
+ * @param[in] slave     = Slave number
+ * @param[in] eeproma   = (WORD) Address in the EEPROM
+ * @param[in] timeout   = Timeout in us.
+ * @return EEPROM data 32bit
+ */
+uint32 ecx_readeeprom(ecx_contextt *context, uint16 slave, uint16 eeproma, int timeout)
+{
+   uint16 configadr;
+
+   ecx_eeprom2master(context, slave); /* set eeprom control to master */
+   configadr = context->slavelist[slave].configadr;
+
+   return ((uint32)ecx_readeepromFP(context, configadr, eeproma, timeout));
+}
+
+/** Write EEPROM to slave bypassing cache.
+ * @param[in] context   = context struct
+ * @param[in] slave     = Slave number
+ * @param[in] eeproma   = (WORD) Address in the EEPROM
+ * @param[in] data      = 16bit data
+ * @param[in] timeout   = Timeout in us.
+ * @return >0 if OK
+ */
+int ecx_writeeeprom(ecx_contextt *context, uint16 slave, uint16 eeproma, uint16 data, int timeout)
+{
+   uint16 configadr;
+
+   ecx_eeprom2master(context, slave); /* set eeprom control to master */
+   configadr = context->slavelist[slave].configadr;
+   return (ecx_writeeepromFP(context, configadr, eeproma, data, timeout));
+}
+
+/** Set eeprom control to master. Only if set to PDI.
+ * @param[in] context   = context struct
+ * @param[in] slave     = Slave number
+ * @return >0 if OK
+ */
+int ecx_eeprom2master(ecx_contextt *context, uint16 slave)
+{
+   int wkc = 1, cnt = 0;
+   uint16 configadr;
+   uint8 eepctl;
+
+   if ( context->slavelist[slave].eep_pdi )
+   {
+      configadr = context->slavelist[slave].configadr;
+      eepctl = 2;
+      do
+      {
+         wkc = ecx_FPWR(context->port, configadr, ECT_REG_EEPCFG, sizeof(eepctl), &eepctl , EC_TIMEOUTRET); /* force Eeprom from PDI */
+      }
+      while ((wkc <= 0) && (cnt++ < EC_DEFAULTRETRIES));
+      eepctl = 0;
+      cnt = 0;
+      do
+      {
+         wkc = ecx_FPWR(context->port, configadr, ECT_REG_EEPCFG, sizeof(eepctl), &eepctl , EC_TIMEOUTRET); /* set Eeprom to master */
+      }
+      while ((wkc <= 0) && (cnt++ < EC_DEFAULTRETRIES));
+      context->slavelist[slave].eep_pdi = 0;
+   }
+
+   return wkc;
+}
+
+/** Set eeprom control to PDI. Only if set to master.
+ * @param[in]  context        = context struct
+ * @param[in] slave     = Slave number
+ * @return >0 if OK
+ */
+int ecx_eeprom2pdi(ecx_contextt *context, uint16 slave)
+{
+   int wkc = 1, cnt = 0;
+   uint16 configadr;
+   uint8 eepctl;
+
+   if ( !context->slavelist[slave].eep_pdi )
+   {
+      configadr = context->slavelist[slave].configadr;
+      eepctl = 1;
+      do
+      {
+         wkc = ecx_FPWR(context->port, configadr, ECT_REG_EEPCFG, sizeof(eepctl), &eepctl , EC_TIMEOUTRET); /* set Eeprom to PDI */
+      }
+      while ((wkc <= 0) && (cnt++ < EC_DEFAULTRETRIES));
+      context->slavelist[slave].eep_pdi = 1;
+   }
+
+   return wkc;
+}
+
+uint16 ecx_eeprom_waitnotbusyAP(ecx_contextt *context, uint16 aiadr,uint16 *estat, int timeout)
+{
+   int wkc, cnt = 0, retval = 0;
+   osal_timert timer;
+
+   osal_timer_start(&timer, timeout);
+   do
+   {
+      if (cnt++)
+      {
+         osal_usleep(EC_LOCALDELAY);
+      }
+      *estat = 0;
+      wkc=ecx_APRD(context->port, aiadr, ECT_REG_EEPSTAT, sizeof(*estat), estat, EC_TIMEOUTRET);
+      *estat = etohs(*estat);
+   }
+   while (((wkc <= 0) || ((*estat & EC_ESTAT_BUSY) > 0)) && (osal_timer_is_expired(&timer) == FALSE)); /* wait for eeprom ready */
+   if ((*estat & EC_ESTAT_BUSY) == 0)
+   {
+      retval = 1;
+   }
+
+   return retval;
+}
+
+/** Read EEPROM from slave bypassing cache. APRD method.
+ * @param[in] context     = context struct
+ * @param[in] aiadr       = auto increment address of slave
+ * @param[in] eeproma     = (WORD) Address in the EEPROM
+ * @param[in] timeout     = Timeout in us.
+ * @return EEPROM data 64bit or 32bit
+ */
+uint64 ecx_readeepromAP(ecx_contextt *context, uint16 aiadr, uint16 eeproma, int timeout)
+{
+   uint16 estat;
+   uint32 edat32;
+   uint64 edat64;
+   ec_eepromt ed;
+   int wkc, cnt, nackcnt = 0;
+
+   edat64 = 0;
+   edat32 = 0;
+   if (ecx_eeprom_waitnotbusyAP(context, aiadr, &estat, timeout))
+   {
+      if (estat & EC_ESTAT_EMASK) /* error bits are set */
+      {
+         estat = htoes(EC_ECMD_NOP); /* clear error bits */
+         wkc = ecx_APWR(context->port, aiadr, ECT_REG_EEPCTL, sizeof(estat), &estat, EC_TIMEOUTRET3);
+      }
+
+      do
+      {
+         ed.comm = htoes(EC_ECMD_READ);
+         ed.addr = htoes(eeproma);
+         ed.d2   = 0x0000;
+         cnt = 0;
+         do
+         {
+            wkc = ecx_APWR(context->port, aiadr, ECT_REG_EEPCTL, sizeof(ed), &ed, EC_TIMEOUTRET);
+         }
+         while ((wkc <= 0) && (cnt++ < EC_DEFAULTRETRIES));
+         if (wkc)
+         {
+            osal_usleep(EC_LOCALDELAY);
+            estat = 0x0000;
+            if (ecx_eeprom_waitnotbusyAP(context, aiadr, &estat, timeout))
+            {
+               if (estat & EC_ESTAT_NACK)
+               {
+                  nackcnt++;
+                  osal_usleep(EC_LOCALDELAY * 5);
+               }
+               else
+               {
+                  nackcnt = 0;
+                  if (estat & EC_ESTAT_R64)
+                  {
+                     cnt = 0;
+                     do
+                     {
+                        wkc = ecx_APRD(context->port, aiadr, ECT_REG_EEPDAT, sizeof(edat64), &edat64, EC_TIMEOUTRET);
+                     }
+                     while ((wkc <= 0) && (cnt++ < EC_DEFAULTRETRIES));
+                  }
+                  else
+                  {
+                     cnt = 0;
+                     do
+                     {
+                        wkc = ecx_APRD(context->port, aiadr, ECT_REG_EEPDAT, sizeof(edat32), &edat32, EC_TIMEOUTRET);
+                     }
+                     while ((wkc <= 0) && (cnt++ < EC_DEFAULTRETRIES));
+                     edat64=(uint64)edat32;
+                  }
+               }
+            }
+         }
+      }
+      while ((nackcnt > 0) && (nackcnt < 3));
+   }
+
+   return edat64;
+}
+
+/** Write EEPROM to slave bypassing cache. APWR method.
+ * @param[in] context   = context struct
+ * @param[in] aiadr     = configured address of slave
+ * @param[in] eeproma   = (WORD) Address in the EEPROM
+ * @param[in] data      = 16bit data
+ * @param[in] timeout   = Timeout in us.
+ * @return >0 if OK
+ */
+int ecx_writeeepromAP(ecx_contextt *context, uint16 aiadr, uint16 eeproma, uint16 data, int timeout)
+{
+   uint16 estat;
+   ec_eepromt ed;
+   int wkc, rval = 0, cnt = 0, nackcnt = 0;
+
+   if (ecx_eeprom_waitnotbusyAP(context, aiadr, &estat, timeout))
+   {
+      if (estat & EC_ESTAT_EMASK) /* error bits are set */
+      {
+         estat = htoes(EC_ECMD_NOP); /* clear error bits */
+         wkc = ecx_APWR(context->port, aiadr, ECT_REG_EEPCTL, sizeof(estat), &estat, EC_TIMEOUTRET3);
+      }
+      do
+      {
+         cnt = 0;
+         do
+         {
+            wkc = ecx_APWR(context->port, aiadr, ECT_REG_EEPDAT, sizeof(data), &data, EC_TIMEOUTRET);
+         }
+         while ((wkc <= 0) && (cnt++ < EC_DEFAULTRETRIES));
+
+         ed.comm = EC_ECMD_WRITE;
+         ed.addr = eeproma;
+         ed.d2   = 0x0000;
+         cnt = 0;
+         do
+         {
+            wkc = ecx_APWR(context->port, aiadr, ECT_REG_EEPCTL, sizeof(ed), &ed, EC_TIMEOUTRET);
+         }
+         while ((wkc <= 0) && (cnt++ < EC_DEFAULTRETRIES));
+         if (wkc)
+         {
+            osal_usleep(EC_LOCALDELAY * 2);
+            estat = 0x0000;
+            if (ecx_eeprom_waitnotbusyAP(context, aiadr, &estat, timeout))
+            {
+               if (estat & EC_ESTAT_NACK)
+               {
+                  nackcnt++;
+                  osal_usleep(EC_LOCALDELAY * 5);
+               }
+               else
+               {
+                  nackcnt = 0;
+                  rval = 1;
+               }
+            }
+         }
+
+      }
+      while ((nackcnt > 0) && (nackcnt < 3));
+   }
+
+   return rval;
+}
+
+uint16 ecx_eeprom_waitnotbusyFP(ecx_contextt *context, uint16 configadr,uint16 *estat, int timeout)
+{
+   int wkc, cnt = 0, retval = 0;
+   osal_timert timer;
+
+   osal_timer_start(&timer, timeout);
+   do
+   {
+      if (cnt++)
+      {
+         osal_usleep(EC_LOCALDELAY);
+      }
+      *estat = 0;
+      wkc=ecx_FPRD(context->port, configadr, ECT_REG_EEPSTAT, sizeof(*estat), estat, EC_TIMEOUTRET);
+      *estat = etohs(*estat);
+   }
+   while (((wkc <= 0) || ((*estat & EC_ESTAT_BUSY) > 0)) && (osal_timer_is_expired(&timer) == FALSE)); /* wait for eeprom ready */
+   if ((*estat & EC_ESTAT_BUSY) == 0)
+   {
+      retval = 1;
+   }
+
+   return retval;
+}
+
+/** Read EEPROM from slave bypassing cache. FPRD method.
+ * @param[in] context     = context struct
+ * @param[in] configadr   = configured address of slave
+ * @param[in] eeproma     = (WORD) Address in the EEPROM
+ * @param[in] timeout     = Timeout in us.
+ * @return EEPROM data 64bit or 32bit
+ */
+uint64 ecx_readeepromFP(ecx_contextt *context, uint16 configadr, uint16 eeproma, int timeout)
+{
+   uint16 estat;
+   uint32 edat32;
+   uint64 edat64;
+   ec_eepromt ed;
+   int wkc, cnt, nackcnt = 0;
+
+   edat64 = 0;
+   edat32 = 0;
+   if (ecx_eeprom_waitnotbusyFP(context, configadr, &estat, timeout))
+   {
+      if (estat & EC_ESTAT_EMASK) /* error bits are set */
+      {
+         estat = htoes(EC_ECMD_NOP); /* clear error bits */
+         wkc=ecx_FPWR(context->port, configadr, ECT_REG_EEPCTL, sizeof(estat), &estat, EC_TIMEOUTRET3);
+      }
+
+      do
+      {
+         ed.comm = htoes(EC_ECMD_READ);
+         ed.addr = htoes(eeproma);
+         ed.d2   = 0x0000;
+         cnt = 0;
+         do
+         {
+            wkc=ecx_FPWR(context->port, configadr, ECT_REG_EEPCTL, sizeof(ed), &ed, EC_TIMEOUTRET);
+         }
+         while ((wkc <= 0) && (cnt++ < EC_DEFAULTRETRIES));
+         if (wkc)
+         {
+            osal_usleep(EC_LOCALDELAY);
+            estat = 0x0000;
+            if (ecx_eeprom_waitnotbusyFP(context, configadr, &estat, timeout))
+            {
+               if (estat & EC_ESTAT_NACK)
+               {
+                  nackcnt++;
+                  osal_usleep(EC_LOCALDELAY * 5);
+               }
+               else
+               {
+                  nackcnt = 0;
+                  if (estat & EC_ESTAT_R64)
+                  {
+                     cnt = 0;
+                     do
+                     {
+                        wkc=ecx_FPRD(context->port, configadr, ECT_REG_EEPDAT, sizeof(edat64), &edat64, EC_TIMEOUTRET);
+                     }
+                     while ((wkc <= 0) && (cnt++ < EC_DEFAULTRETRIES));
+                  }
+                  else
+                  {
+                     cnt = 0;
+                     do
+                     {
+                        wkc=ecx_FPRD(context->port, configadr, ECT_REG_EEPDAT, sizeof(edat32), &edat32, EC_TIMEOUTRET);
+                     }
+                     while ((wkc <= 0) && (cnt++ < EC_DEFAULTRETRIES));
+                     edat64=(uint64)edat32;
+                  }
+               }
+            }
+         }
+      }
+      while ((nackcnt > 0) && (nackcnt < 3));
+   }
+
+   return edat64;
+}
+
+/** Write EEPROM to slave bypassing cache. FPWR method.
+ * @param[in]  context        = context struct
+ * @param[in] configadr   = configured address of slave
+ * @param[in] eeproma     = (WORD) Address in the EEPROM
+ * @param[in] data        = 16bit data
+ * @param[in] timeout     = Timeout in us.
+ * @return >0 if OK
+ */
+int ecx_writeeepromFP(ecx_contextt *context, uint16 configadr, uint16 eeproma, uint16 data, int timeout)
+{
+   uint16 estat;
+   ec_eepromt ed;
+   int wkc, rval = 0, cnt = 0, nackcnt = 0;
+
+   if (ecx_eeprom_waitnotbusyFP(context, configadr, &estat, timeout))
+   {
+      if (estat & EC_ESTAT_EMASK) /* error bits are set */
+      {
+         estat = htoes(EC_ECMD_NOP); /* clear error bits */
+         wkc = ecx_FPWR(context->port, configadr, ECT_REG_EEPCTL, sizeof(estat), &estat, EC_TIMEOUTRET3);
+      }
+      do
+      {
+         cnt = 0;
+         do
+         {
+            wkc = ecx_FPWR(context->port, configadr, ECT_REG_EEPDAT, sizeof(data), &data, EC_TIMEOUTRET);
+         }
+         while ((wkc <= 0) && (cnt++ < EC_DEFAULTRETRIES));
+         ed.comm = EC_ECMD_WRITE;
+         ed.addr = eeproma;
+         ed.d2   = 0x0000;
+         cnt = 0;
+         do
+         {
+            wkc = ecx_FPWR(context->port, configadr, ECT_REG_EEPCTL, sizeof(ed), &ed, EC_TIMEOUTRET);
+         }
+         while ((wkc <= 0) && (cnt++ < EC_DEFAULTRETRIES));
+         if (wkc)
+         {
+            osal_usleep(EC_LOCALDELAY * 2);
+            estat = 0x0000;
+            if (ecx_eeprom_waitnotbusyFP(context, configadr, &estat, timeout))
+            {
+               if (estat & EC_ESTAT_NACK)
+               {
+                  nackcnt++;
+                  osal_usleep(EC_LOCALDELAY * 5);
+               }
+               else
+               {
+                  nackcnt = 0;
+                  rval = 1;
+               }
+            }
+         }
+      }
+      while ((nackcnt > 0) && (nackcnt < 3));
+   }
+
+   return rval;
+}
+
+/** Read EEPROM from slave bypassing cache.
+ * Parallel read step 1, make request to slave.
+ * @param[in] context     = context struct
+ * @param[in] slave       = Slave number
+ * @param[in] eeproma     = (WORD) Address in the EEPROM
+ */
+void ecx_readeeprom1(ecx_contextt *context, uint16 slave, uint16 eeproma)
+{
+   uint16 configadr, estat;
+   ec_eepromt ed;
+   int wkc, cnt = 0;
+
+   ecx_eeprom2master(context, slave); /* set eeprom control to master */
+   configadr = context->slavelist[slave].configadr;
+   if (ecx_eeprom_waitnotbusyFP(context, configadr, &estat, EC_TIMEOUTEEP))
+   {
+      if (estat & EC_ESTAT_EMASK) /* error bits are set */
+      {
+         estat = htoes(EC_ECMD_NOP); /* clear error bits */
+         wkc = ecx_FPWR(context->port, configadr, ECT_REG_EEPCTL, sizeof(estat), &estat, EC_TIMEOUTRET3);
+      }
+      ed.comm = htoes(EC_ECMD_READ);
+      ed.addr = htoes(eeproma);
+      ed.d2   = 0x0000;
+      do
+      {
+         wkc = ecx_FPWR(context->port, configadr, ECT_REG_EEPCTL, sizeof(ed), &ed, EC_TIMEOUTRET);
+      }
+      while ((wkc <= 0) && (cnt++ < EC_DEFAULTRETRIES));
+   }
+}
+
+/** Read EEPROM from slave bypassing cache.
+ * Parallel read step 2, actual read from slave.
+ * @param[in]  context        = context struct
+ * @param[in] slave       = Slave number
+ * @param[in] timeout     = Timeout in us.
+ * @return EEPROM data 32bit
+ */
+uint32 ecx_readeeprom2(ecx_contextt *context, uint16 slave, int timeout)
+{
+   uint16 estat, configadr;
+   uint32 edat;
+   int wkc, cnt = 0;
+
+   configadr = context->slavelist[slave].configadr;
+   edat = 0;
+   estat = 0x0000;
+   if (ecx_eeprom_waitnotbusyFP(context, configadr, &estat, timeout))
+   {
+      do
+      {
+          wkc = ecx_FPRD(context->port, configadr, ECT_REG_EEPDAT, sizeof(edat), &edat, EC_TIMEOUTRET);
+      }
+      while ((wkc <= 0) && (cnt++ < EC_DEFAULTRETRIES));
+   }
+
+   return edat;
+}
+
+/** Push index of segmented LRD/LWR/LRW combination.
+ * @param[in]  context        = context struct
+ * @param[in] idx         = Used datagram index.
+ * @param[in] data        = Pointer to process data segment.
+ * @param[in] length      = Length of data segment in bytes.
+ */
+static void ecx_pushindex(ecx_contextt *context, uint8 idx, void *data, uint16 length)
+{
+   if(context->idxstack->pushed < EC_MAXBUF)
+   {
+      context->idxstack->idx[context->idxstack->pushed] = idx;
+      context->idxstack->data[context->idxstack->pushed] = data;
+      context->idxstack->length[context->idxstack->pushed] = length;
+      context->idxstack->pushed++;
+   }
+}
+
+/** Pull index of segmented LRD/LWR/LRW combination.
+ * @param[in]  context        = context struct
+ * @return Stack location, -1 if stack is empty.
+ */
+static int ecx_pullindex(ecx_contextt *context)
+{
+   int rval = -1;
+   if(context->idxstack->pulled < context->idxstack->pushed)
+   {
+      rval = context->idxstack->pulled;
+      context->idxstack->pulled++;
+   }
+
+   return rval;
+}
+
+/** 
+ * Clear the idx stack.
+ * 
+ * @param context           = context struct
+ */
+static void ecx_clearindex(ecx_contextt *context)  {
+
+   context->idxstack->pushed = 0;
+   context->idxstack->pulled = 0;
+
+}
+
+/** Transmit processdata to slaves.
+ * Uses LRW, or LRD/LWR if LRW is not allowed (blockLRW).
+ * Both the input and output processdata are transmitted.
+ * The outputs with the actual data, the inputs have a placeholder.
+ * The inputs are gathered with the receive processdata function.
+ * In contrast to the base LRW function this function is non-blocking.
+ * If the processdata does not fit in one datagram, multiple are used.
+ * In order to recombine the slave response, a stack is used.
+ * @param[in]  context        = context struct
+ * @param[in]  group          = group number
+ * @return >0 if processdata is transmitted.
+ */
+static int ecx_main_send_processdata(ecx_contextt *context, uint8 group, boolean use_overlap_io)
+{
+   uint32 LogAdr;
+   uint16 w1, w2;
+   int length, sublength;
+   uint8 idx;
+   int wkc;
+   uint8* data;
+   boolean first=FALSE;
+   uint16 currentsegment = 0;
+   uint32 iomapinputoffset;
+
+   wkc = 0;
+   if(context->grouplist[group].hasdc)
+   {
+      first = TRUE;
+   }
+
+   /* For overlapping IO map use the biggest */
+   if(use_overlap_io == TRUE)
+   {
+      /* For overlap IOmap make the frame EQ big to biggest part */
+      length = (context->grouplist[group].Obytes > context->grouplist[group].Ibytes) ?
+         context->grouplist[group].Obytes : context->grouplist[group].Ibytes;
+      /* Save the offset used to compensate where to save inputs when frame returns */
+      iomapinputoffset = context->grouplist[group].Obytes;
+   }
+   else
+   {
+      length = context->grouplist[group].Obytes + context->grouplist[group].Ibytes;
+      iomapinputoffset = 0;
+   }
+   
+   LogAdr = context->grouplist[group].logstartaddr;
+   if(length)
+   {
+
+      wkc = 1;
+      /* LRW blocked by one or more slaves ? */
+      if(context->grouplist[group].blockLRW)
+      {
+         /* if inputs available generate LRD */
+         if(context->grouplist[group].Ibytes)
+         {
+            currentsegment = context->grouplist[group].Isegment;
+            data = context->grouplist[group].inputs;
+            length = context->grouplist[group].Ibytes;
+            LogAdr += context->grouplist[group].Obytes;
+            /* segment transfer if needed */
+            do
+            {
+               if(currentsegment == context->grouplist[group].Isegment)
+               {
+                  sublength = context->grouplist[group].IOsegment[currentsegment++] - context->grouplist[group].Ioffset;
+               }
+               else
+               {
+                  sublength = context->grouplist[group].IOsegment[currentsegment++];
+               }
+               /* get new index */
+               idx = ecx_getindex(context->port);
+               w1 = LO_WORD(LogAdr);
+               w2 = HI_WORD(LogAdr);
+               ecx_setupdatagram(context->port, &(context->port->txbuf[idx]), EC_CMD_LRD, idx, w1, w2, sublength, data);
+               if(first)
+               {
+                  context->DCl = sublength;
+                  /* FPRMW in second datagram */
+                  context->DCtO = ecx_adddatagram(context->port, &(context->port->txbuf[idx]), EC_CMD_FRMW, idx, FALSE,
+                                           context->slavelist[context->grouplist[group].DCnext].configadr,
+                                           ECT_REG_DCSYSTIME, sizeof(int64), context->DCtime);
+                  first = FALSE;
+               }
+               /* send frame */
+               ecx_outframe_red(context->port, idx);
+               /* push index and data pointer on stack */
+               ecx_pushindex(context, idx, data, sublength);
+               length -= sublength;
+               LogAdr += sublength;
+               data += sublength;
+            } while (length && (currentsegment < context->grouplist[group].nsegments));
+         }
+         /* if outputs available generate LWR */
+         if(context->grouplist[group].Obytes)
+         {
+            data = context->grouplist[group].outputs;
+            length = context->grouplist[group].Obytes;
+            LogAdr = context->grouplist[group].logstartaddr;
+            currentsegment = 0;
+            /* segment transfer if needed */
+            do
+            {
+               sublength = context->grouplist[group].IOsegment[currentsegment++];
+               if((length - sublength) < 0)
+               {
+                  sublength = length;
+               }
+               /* get new index */
+               idx = ecx_getindex(context->port);
+               w1 = LO_WORD(LogAdr);
+               w2 = HI_WORD(LogAdr);
+               ecx_setupdatagram(context->port, &(context->port->txbuf[idx]), EC_CMD_LWR, idx, w1, w2, sublength, data);
+               if(first)
+               {
+                  context->DCl = sublength;
+                  /* FPRMW in second datagram */
+                  context->DCtO = ecx_adddatagram(context->port, &(context->port->txbuf[idx]), EC_CMD_FRMW, idx, FALSE,
+                                           context->slavelist[context->grouplist[group].DCnext].configadr,
+                                           ECT_REG_DCSYSTIME, sizeof(int64), context->DCtime);
+                  first = FALSE;
+               }
+               /* send frame */
+               ecx_outframe_red(context->port, idx);
+               /* push index and data pointer on stack */
+               ecx_pushindex(context, idx, data, sublength);
+               length -= sublength;
+               LogAdr += sublength;
+               data += sublength;
+            } while (length && (currentsegment < context->grouplist[group].nsegments));
+         }
+      }
+      /* LRW can be used */
+      else
+      {
+         if (context->grouplist[group].Obytes)
+         {
+            data = context->grouplist[group].outputs;
+         }
+         else
+         {
+            data = context->grouplist[group].inputs;
+            /* Clear offset, don't compensate for overlapping IOmap if we only got inputs */
+            iomapinputoffset = 0;
+         }
+         /* segment transfer if needed */
+         do
+         {
+            sublength = context->grouplist[group].IOsegment[currentsegment++];
+            /* get new index */
+            idx = ecx_getindex(context->port);
+            w1 = LO_WORD(LogAdr);
+            w2 = HI_WORD(LogAdr);
+            ecx_setupdatagram(context->port, &(context->port->txbuf[idx]), EC_CMD_LRW, idx, w1, w2, sublength, data);
+            if(first)
+            {
+               context->DCl = sublength;
+               /* FPRMW in second datagram */
+               context->DCtO = ecx_adddatagram(context->port, &(context->port->txbuf[idx]), EC_CMD_FRMW, idx, FALSE,
+                                        context->slavelist[context->grouplist[group].DCnext].configadr,
+                                        ECT_REG_DCSYSTIME, sizeof(int64), context->DCtime);
+               first = FALSE;
+            }
+            /* send frame */
+            ecx_outframe_red(context->port, idx);
+            /* push index and data pointer on stack.
+             * the iomapinputoffset compensate for where the inputs are stored 
+             * in the IOmap if we use an overlapping IOmap. If a regular IOmap
+             * is used it should always be 0.
+             */
+            ecx_pushindex(context, idx, (data + iomapinputoffset), sublength);      
+            length -= sublength;
+            LogAdr += sublength;
+            data += sublength;
+         } while (length && (currentsegment < context->grouplist[group].nsegments));
+      }
+   }
+
+   return wkc;
+}
+
+/** Transmit processdata to slaves.
+* Uses LRW, or LRD/LWR if LRW is not allowed (blockLRW).
+* Both the input and output processdata are transmitted in the overlapped IOmap.
+* The outputs with the actual data, the inputs replace the output data in the
+* returning frame. The inputs are gathered with the receive processdata function.
+* In contrast to the base LRW function this function is non-blocking.
+* If the processdata does not fit in one datagram, multiple are used.
+* In order to recombine the slave response, a stack is used.
+* @param[in]  context        = context struct
+* @param[in]  group          = group number
+* @return >0 if processdata is transmitted.
+*/
+int ecx_send_overlap_processdata_group(ecx_contextt *context, uint8 group)
+{
+   return ecx_main_send_processdata(context, group, TRUE);
+}
+
+/** Transmit processdata to slaves.
+* Uses LRW, or LRD/LWR if LRW is not allowed (blockLRW).
+* Both the input and output processdata are transmitted.
+* The outputs with the actual data, the inputs have a placeholder.
+* The inputs are gathered with the receive processdata function.
+* In contrast to the base LRW function this function is non-blocking.
+* If the processdata does not fit in one datagram, multiple are used.
+* In order to recombine the slave response, a stack is used.
+* @param[in]  context        = context struct
+* @param[in]  group          = group number
+* @return >0 if processdata is transmitted.
+*/
+int ecx_send_processdata_group(ecx_contextt *context, uint8 group)
+{
+   return ecx_main_send_processdata(context, group, FALSE);
+}
+
+/** Receive processdata from slaves.
+ * Second part from ec_send_processdata().
+ * Received datagrams are recombined with the processdata with help from the stack.
+ * If a datagram contains input processdata it copies it to the processdata structure.
+ * @param[in]  context        = context struct
+ * @param[in]  group          = group number
+ * @param[in]  timeout        = Timeout in us.
+ * @return Work counter.
+ */
+int ecx_receive_processdata_group(ecx_contextt *context, uint8 group, int timeout)
+{
+   int pos, idx;
+   int wkc = 0, wkc2;
+   uint16 le_wkc = 0;
+   int valid_wkc = 0;
+   int64 le_DCtime;
+   boolean first = FALSE;
+
+   if(context->grouplist[group].hasdc)
+   {
+      first = TRUE;
+   }
+   /* get first index */
+   pos = ecx_pullindex(context);
+   /* read the same number of frames as send */
+   while (pos >= 0)
+   {
+      idx = context->idxstack->idx[pos];
+      wkc2 = ecx_waitinframe(context->port, context->idxstack->idx[pos], timeout);
+      /* check if there is input data in frame */
+      if (wkc2 > EC_NOFRAME)
+      {
+         if((context->port->rxbuf[idx][EC_CMDOFFSET]==EC_CMD_LRD) || (context->port->rxbuf[idx][EC_CMDOFFSET]==EC_CMD_LRW))
+         {
+            if(first)
+            {
+               memcpy(context->idxstack->data[pos], &(context->port->rxbuf[idx][EC_HEADERSIZE]), context->DCl);
+               memcpy(&le_wkc, &(context->port->rxbuf[idx][EC_HEADERSIZE + context->DCl]), EC_WKCSIZE);
+               wkc = etohs(le_wkc);
+               memcpy(&le_DCtime, &(context->port->rxbuf[idx][context->DCtO]), sizeof(le_DCtime));
+               *(context->DCtime) = etohll(le_DCtime);
+               first = FALSE;
+            }
+            else
+            {
+               /* copy input data back to process data buffer */
+               memcpy(context->idxstack->data[pos], &(context->port->rxbuf[idx][EC_HEADERSIZE]), context->idxstack->length[pos]);
+               wkc += wkc2;
+            }
+            valid_wkc = 1;
+         }
+         else if(context->port->rxbuf[idx][EC_CMDOFFSET]==EC_CMD_LWR)
+         {
+            if(first)
+            {
+               memcpy(&le_wkc, &(context->port->rxbuf[idx][EC_HEADERSIZE + context->DCl]), EC_WKCSIZE);
+               /* output WKC counts 2 times when using LRW, emulate the same for LWR */
+               wkc = etohs(le_wkc) * 2;
+               memcpy(&le_DCtime, &(context->port->rxbuf[idx][context->DCtO]), sizeof(le_DCtime));
+               *(context->DCtime) = etohll(le_DCtime);
+               first = FALSE;
+            }
+            else
+            {
+               /* output WKC counts 2 times when using LRW, emulate the same for LWR */
+               wkc += wkc2 * 2;
+            }
+            valid_wkc = 1;
+         }
+      }
+      /* release buffer */
+      ecx_setbufstat(context->port, idx, EC_BUF_EMPTY);
+      /* get next index */
+      pos = ecx_pullindex(context);
+   }
+
+   ecx_clearindex(context);
+
+   /* if no frames has arrived */
+   if (valid_wkc == 0)
+   {
+      return EC_NOFRAME;
+   }
+   return wkc;
+}
+
+
+int ecx_send_processdata(ecx_contextt *context)
+{
+   return ecx_send_processdata_group(context, 0);
+}
+
+int ecx_send_overlap_processdata(ecx_contextt *context)
+{
+   return ecx_send_overlap_processdata_group(context, 0);
+}
+
+int ecx_receive_processdata(ecx_contextt *context, int timeout)
+{
+   return ecx_receive_processdata_group(context, 0, timeout);
+}
+
+#ifdef EC_VER1
+void ec_pusherror(const ec_errort *Ec)
+{
+   ecx_pusherror(&ecx_context, Ec);
+}
+
+boolean ec_poperror(ec_errort *Ec)
+{
+   return ecx_poperror(&ecx_context, Ec);
+}
+
+boolean ec_iserror(void)
+{
+   return ecx_iserror(&ecx_context);
+}
+
+void ec_packeterror(uint16 Slave, uint16 Index, uint8 SubIdx, uint16 ErrorCode)
+{
+   ecx_packeterror(&ecx_context, Slave, Index, SubIdx, ErrorCode);
+}
+
+/** Initialise lib in single NIC mode
+ * @param[in] ifname   = Dev name, f.e. "eth0"
+ * @return >0 if OK
+ * @see ecx_init
+ */
+int ec_init(const char * ifname)
+{
+   return ecx_init(&ecx_context, ifname);
+}
+
+/** Initialise lib in redundant NIC mode
+ * @param[in]  ifname   = Primary Dev name, f.e. "eth0"
+ * @param[in]  if2name  = Secondary Dev name, f.e. "eth1"
+ * @return >0 if OK
+ * @see ecx_init_redundant
+ */
+int ec_init_redundant(const char *ifname, char *if2name)
+{
+   return ecx_init_redundant (&ecx_context, &ecx_redport, ifname, if2name);
+}
+
+/** Close lib.
+ * @see ecx_close
+ */
+void ec_close(void)
+{
+   ecx_close(&ecx_context);
+};
+
+/** Read one byte from slave EEPROM via cache.
+ *  If the cache location is empty then a read request is made to the slave.
+ *  Depending on the slave capabillities the request is 4 or 8 bytes.
+ *  @param[in] slave   = slave number
+ *  @param[in] address = eeprom address in bytes (slave uses words)
+ *  @return requested byte, if not available then 0xff
+ * @see ecx_siigetbyte
+ */
+uint8 ec_siigetbyte(uint16 slave, uint16 address)
+{
+   return ecx_siigetbyte (&ecx_context, slave, address);
+}
+
+/** Find SII section header in slave EEPROM.
+ *  @param[in] slave   = slave number
+ *  @param[in] cat     = section category
+ *  @return byte address of section at section length entry, if not available then 0
+ *  @see ecx_siifind
+ */
+int16 ec_siifind(uint16 slave, uint16 cat)
+{
+   return ecx_siifind (&ecx_context, slave, cat);
+}
+
+/** Get string from SII string section in slave EEPROM.
+ *  @param[out] str    = requested string, 0x00 if not found
+ *  @param[in]  slave  = slave number
+ *  @param[in]  Sn     = string number
+ *  @see ecx_siistring
+ */
+void ec_siistring(char *str, uint16 slave, uint16 Sn)
+{
+   ecx_siistring(&ecx_context, str, slave, Sn);
+}
+
+/** Get FMMU data from SII FMMU section in slave EEPROM.
+ *  @param[in]  slave  = slave number
+ *  @param[out] FMMU   = FMMU struct from SII, max. 4 FMMU's
+ *  @return number of FMMU's defined in section
+ *  @see ecx_siiFMMU
+ */
+uint16 ec_siiFMMU(uint16 slave, ec_eepromFMMUt* FMMU)
+{
+   return ecx_siiFMMU (&ecx_context, slave, FMMU);
+}
+
+/** Get SM data from SII SM section in slave EEPROM.
+ *  @param[in]  slave   = slave number
+ *  @param[out] SM      = first SM struct from SII
+ *  @return number of SM's defined in section
+ *  @see ecx_siiSM
+ */
+uint16 ec_siiSM(uint16 slave, ec_eepromSMt* SM)
+{
+   return ecx_siiSM (&ecx_context, slave, SM);
+}
+
+/** Get next SM data from SII SM section in slave EEPROM.
+ *  @param[in]  slave  = slave number
+ *  @param[out] SM     = first SM struct from SII
+ *  @param[in]  n      = SM number
+ *  @return >0 if OK
+ *  @see ecx_siiSMnext
+ */
+uint16 ec_siiSMnext(uint16 slave, ec_eepromSMt* SM, uint16 n)
+{
+   return ecx_siiSMnext (&ecx_context, slave, SM, n);
+}
+
+/** Get PDO data from SII PDO section in slave EEPROM.
+ *  @param[in]  slave  = slave number
+ *  @param[out] PDO    = PDO struct from SII
+ *  @param[in]  t      = 0=RXPDO 1=TXPDO
+ *  @return mapping size in bits of PDO
+ *  @see ecx_siiPDO
+ */
+int ec_siiPDO(uint16 slave, ec_eepromPDOt* PDO, uint8 t)
+{
+   return ecx_siiPDO (&ecx_context, slave, PDO, t);
+}
+
+/** Read all slave states in ec_slave.
+ * @return lowest state found
+ * @see ecx_readstate
+ */
+int ec_readstate(void)
+{
+   return ecx_readstate (&ecx_context);
+}
+
+/** Write slave state, if slave = 0 then write to all slaves.
+ * The function does not check if the actual state is changed.
+ * @param[in] slave = Slave number, 0 = master
+ * @return 0
+ * @see ecx_writestate
+ */
+int ec_writestate(uint16 slave)
+{
+   return ecx_writestate(&ecx_context, slave);
+}
+
+/** Check actual slave state.
+ * This is a blocking function.
+ * @param[in] slave       = Slave number, 0 = all slaves
+ * @param[in] reqstate    = Requested state
+ * @param[in] timeout     = Timeout value in us
+ * @return Requested state, or found state after timeout.
+ * @see ecx_statecheck
+ */
+uint16 ec_statecheck(uint16 slave, uint16 reqstate, int timeout)
+{
+   return ecx_statecheck (&ecx_context, slave, reqstate, timeout);
+}
+
+/** Check if IN mailbox of slave is empty.
+ * @param[in] slave    = Slave number
+ * @param[in] timeout  = Timeout in us
+ * @return >0 is success
+ * @see ecx_mbxempty
+ */
+int ec_mbxempty(uint16 slave, int timeout)
+{
+   return ecx_mbxempty (&ecx_context, slave, timeout);
+}
+
+/** Write IN mailbox to slave.
+ * @param[in]  slave      = Slave number
+ * @param[out] mbx        = Mailbox data
+ * @param[in]  timeout    = Timeout in us
+ * @return Work counter (>0 is success)
+ * @see ecx_mbxsend
+ */
+int ec_mbxsend(uint16 slave,ec_mbxbuft *mbx, int timeout)
+{
+   return ecx_mbxsend (&ecx_context, slave, mbx, timeout);
+}
+
+/** Read OUT mailbox from slave.
+ * Supports Mailbox Link Layer with repeat requests.
+ * @param[in]  slave      = Slave number
+ * @param[out] mbx        = Mailbox data
+ * @param[in]  timeout    = Timeout in us
+ * @return Work counter (>0 is success)
+ * @see ecx_mbxreceive
+ */
+int ec_mbxreceive(uint16 slave, ec_mbxbuft *mbx, int timeout)
+{
+   return ecx_mbxreceive (&ecx_context, slave, mbx, timeout);
+}
+
+/** Dump complete EEPROM data from slave in buffer.
+ * @param[in]  slave    = Slave number
+ * @param[out] esibuf   = EEPROM data buffer, make sure it is big enough.
+ * @see ecx_esidump
+ */
+void ec_esidump(uint16 slave, uint8 *esibuf)
+{
+   ecx_esidump (&ecx_context, slave, esibuf);
+}
+
+/** Read EEPROM from slave bypassing cache.
+ * @param[in] slave     = Slave number
+ * @param[in] eeproma   = (WORD) Address in the EEPROM
+ * @param[in] timeout   = Timeout in us.
+ * @return EEPROM data 32bit
+ * @see ecx_readeeprom
+ */
+uint32 ec_readeeprom(uint16 slave, uint16 eeproma, int timeout)
+{
+   return ecx_readeeprom (&ecx_context, slave, eeproma, timeout);
+}
+
+/** Write EEPROM to slave bypassing cache.
+ * @param[in] slave     = Slave number
+ * @param[in] eeproma   = (WORD) Address in the EEPROM
+ * @param[in] data      = 16bit data
+ * @param[in] timeout   = Timeout in us.
+ * @return >0 if OK
+ * @see ecx_writeeeprom
+ */
+int ec_writeeeprom(uint16 slave, uint16 eeproma, uint16 data, int timeout)
+{
+   return ecx_writeeeprom (&ecx_context, slave, eeproma, data, timeout);
+}
+
+/** Set eeprom control to master. Only if set to PDI.
+ * @param[in] slave = Slave number
+ * @return >0 if OK
+ * @see ecx_eeprom2master
+ */
+int ec_eeprom2master(uint16 slave)
+{
+   return ecx_eeprom2master(&ecx_context, slave);
+}
+
+int ec_eeprom2pdi(uint16 slave)
+{
+   return ecx_eeprom2pdi(&ecx_context, slave);
+}
+
+uint16 ec_eeprom_waitnotbusyAP(uint16 aiadr,uint16 *estat, int timeout)
+{
+   return ecx_eeprom_waitnotbusyAP (&ecx_context, aiadr, estat, timeout);
+}
+
+/** Read EEPROM from slave bypassing cache. APRD method.
+ * @param[in] aiadr       = auto increment address of slave
+ * @param[in] eeproma     = (WORD) Address in the EEPROM
+ * @param[in] timeout     = Timeout in us.
+ * @return EEPROM data 64bit or 32bit
+ */
+uint64 ec_readeepromAP(uint16 aiadr, uint16 eeproma, int timeout)
+{
+   return ecx_readeepromAP (&ecx_context, aiadr, eeproma, timeout);
+}
+
+/** Write EEPROM to slave bypassing cache. APWR method.
+ * @param[in] aiadr     = configured address of slave
+ * @param[in] eeproma   = (WORD) Address in the EEPROM
+ * @param[in] data      = 16bit data
+ * @param[in] timeout   = Timeout in us.
+ * @return >0 if OK
+ * @see ecx_writeeepromAP
+ */
+int ec_writeeepromAP(uint16 aiadr, uint16 eeproma, uint16 data, int timeout)
+{
+   return ecx_writeeepromAP (&ecx_context, aiadr, eeproma, data, timeout);
+}
+
+uint16 ec_eeprom_waitnotbusyFP(uint16 configadr,uint16 *estat, int timeout)
+{
+   return ecx_eeprom_waitnotbusyFP (&ecx_context, configadr, estat, timeout);
+}
+
+/** Read EEPROM from slave bypassing cache. FPRD method.
+ * @param[in] configadr   = configured address of slave
+ * @param[in] eeproma     = (WORD) Address in the EEPROM
+ * @param[in] timeout     = Timeout in us.
+ * @return EEPROM data 64bit or 32bit
+ * @see ecx_readeepromFP
+ */
+uint64 ec_readeepromFP(uint16 configadr, uint16 eeproma, int timeout)
+{
+   return ecx_readeepromFP (&ecx_context, configadr, eeproma, timeout);
+}
+
+/** Write EEPROM to slave bypassing cache. FPWR method.
+ * @param[in] configadr   = configured address of slave
+ * @param[in] eeproma     = (WORD) Address in the EEPROM
+ * @param[in] data        = 16bit data
+ * @param[in] timeout     = Timeout in us.
+ * @return >0 if OK
+ * @see ecx_writeeepromFP
+ */
+int ec_writeeepromFP(uint16 configadr, uint16 eeproma, uint16 data, int timeout)
+{
+   return ecx_writeeepromFP (&ecx_context, configadr, eeproma, data, timeout);
+}
+
+/** Read EEPROM from slave bypassing cache.
+ * Parallel read step 1, make request to slave.
+ * @param[in] slave       = Slave number
+ * @param[in] eeproma     = (WORD) Address in the EEPROM
+ * @see ecx_readeeprom1
+ */
+void ec_readeeprom1(uint16 slave, uint16 eeproma)
+{
+   ecx_readeeprom1 (&ecx_context, slave, eeproma);
+}
+
+/** Read EEPROM from slave bypassing cache.
+ * Parallel read step 2, actual read from slave.
+ * @param[in] slave       = Slave number
+ * @param[in] timeout     = Timeout in us.
+ * @return EEPROM data 32bit
+ * @see ecx_readeeprom2
+ */
+uint32 ec_readeeprom2(uint16 slave, int timeout)
+{
+   return ecx_readeeprom2 (&ecx_context, slave, timeout);
+}
+
+/** Transmit processdata to slaves.
+ * Uses LRW, or LRD/LWR if LRW is not allowed (blockLRW).
+ * Both the input and output processdata are transmitted.
+ * The outputs with the actual data, the inputs have a placeholder.
+ * The inputs are gathered with the receive processdata function.
+ * In contrast to the base LRW function this function is non-blocking.
+ * If the processdata does not fit in one datagram, multiple are used.
+ * In order to recombine the slave response, a stack is used.
+ * @param[in]  group          = group number
+ * @return >0 if processdata is transmitted.
+ * @see ecx_send_processdata_group
+ */
+int ec_send_processdata_group(uint8 group)
+{
+   return ecx_send_processdata_group (&ecx_context, group);
+}
+
+/** Transmit processdata to slaves.
+* Uses LRW, or LRD/LWR if LRW is not allowed (blockLRW).
+* Both the input and output processdata are transmitted in the overlapped IOmap.
+* The outputs with the actual data, the inputs replace the output data in the
+* returning frame. The inputs are gathered with the receive processdata function.
+* In contrast to the base LRW function this function is non-blocking.
+* If the processdata does not fit in one datagram, multiple are used.
+* In order to recombine the slave response, a stack is used.
+* @param[in]  context        = context struct
+* @param[in]  group          = group number
+* @return >0 if processdata is transmitted.
+* @see ecx_send_overlap_processdata_group
+*/
+int ec_send_overlap_processdata_group(uint8 group)
+{
+   return ecx_send_overlap_processdata_group(&ecx_context, group);
+}
+
+/** Receive processdata from slaves.
+ * Second part from ec_send_processdata().
+ * Received datagrams are recombined with the processdata with help from the stack.
+ * If a datagram contains input processdata it copies it to the processdata structure.
+ * @param[in]  group          = group number
+ * @param[in]  timeout        = Timeout in us.
+ * @return Work counter.
+ * @see ecx_receive_processdata_group
+ */
+int ec_receive_processdata_group(uint8 group, int timeout)
+{
+   return ecx_receive_processdata_group (&ecx_context, group, timeout);
+}
+
+int ec_send_processdata(void)
+{
+   return ec_send_processdata_group(0);
+}
+
+int ec_send_overlap_processdata(void)
+{
+   return ec_send_overlap_processdata_group(0);
+}
+
+int ec_receive_processdata(int timeout)
+{
+   return ec_receive_processdata_group(0, timeout);
+}
+#endif
diff -r 000000000000 -r 543d6784d4cc SOEM/ethercatmain.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SOEM/ethercatmain.h	Tue Jun 11 10:29:09 2019 +0000
@@ -0,0 +1,526 @@
+/*
+ * Licensed under the GNU General Public License version 2 with exceptions. See
+ * LICENSE file in the project root for full license information
+ */
+
+/** \file
+ * \brief
+ * Headerfile for ethercatmain.c
+ */
+
+#ifndef _ethercatmain_
+#define _ethercatmain_
+
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/** max. entries in EtherCAT error list */
+#define EC_MAXELIST       64
+/** max. length of readable name in slavelist and Object Description List */
+#define EC_MAXNAME        40
+/** max. number of slaves in array */
+#define EC_MAXSLAVE       200
+/** max. number of groups */
+#define EC_MAXGROUP       2
+/** max. number of IO segments per group */
+#define EC_MAXIOSEGMENTS  64
+/** max. mailbox size */
+#define EC_MAXMBX         1486
+/** max. eeprom PDO entries */
+#define EC_MAXEEPDO       0x200
+/** max. SM used */
+#define EC_MAXSM          8
+/** max. FMMU used */
+#define EC_MAXFMMU        4
+/** max. Adapter */
+#define EC_MAXLEN_ADAPTERNAME    128
+/** define maximum number of concurrent threads in mapping */
+#define EC_MAX_MAPT           1
+
+typedef struct ec_adapter ec_adaptert;
+struct ec_adapter
+{
+   char   name[EC_MAXLEN_ADAPTERNAME];
+   char   desc[EC_MAXLEN_ADAPTERNAME];
+   ec_adaptert *next;
+};
+
+/** record for FMMU */
+PACKED_BEGIN
+typedef struct PACKED ec_fmmu
+{
+   uint32  LogStart;
+   uint16  LogLength;
+   uint8   LogStartbit;
+   uint8   LogEndbit;
+   uint16  PhysStart;
+   uint8   PhysStartBit;
+   uint8   FMMUtype;
+   uint8   FMMUactive;
+   uint8   unused1;
+   uint16  unused2;
+}  ec_fmmut;
+PACKED_END
+
+/** record for sync manager */
+PACKED_BEGIN
+typedef struct PACKED ec_sm
+{
+   uint16  StartAddr;
+   uint16  SMlength;
+   uint32  SMflags;
+} ec_smt;
+PACKED_END
+
+PACKED_BEGIN
+typedef struct PACKED ec_state_status
+{
+   uint16  State;
+   uint16  Unused;
+   uint16  ALstatuscode;
+} ec_state_status;
+PACKED_END
+
+#define ECT_MBXPROT_AOE      0x0001
+#define ECT_MBXPROT_EOE      0x0002
+#define ECT_MBXPROT_COE      0x0004
+#define ECT_MBXPROT_FOE      0x0008
+#define ECT_MBXPROT_SOE      0x0010
+#define ECT_MBXPROT_VOE      0x0020
+
+#define ECT_COEDET_SDO       0x01
+#define ECT_COEDET_SDOINFO   0x02
+#define ECT_COEDET_PDOASSIGN 0x04
+#define ECT_COEDET_PDOCONFIG 0x08
+#define ECT_COEDET_UPLOAD    0x10
+#define ECT_COEDET_SDOCA     0x20
+
+#define EC_SMENABLEMASK      0xfffeffff
+
+/** for list of ethercat slaves detected */
+typedef struct ec_slave
+{
+   /** state of slave */
+   uint16           state;
+   /** AL status code */
+   uint16           ALstatuscode;
+   /** Configured address */
+   uint16           configadr;
+   /** Alias address */
+   uint16           aliasadr;
+   /** Manufacturer from EEprom */
+   uint32           eep_man;
+   /** ID from EEprom */
+   uint32           eep_id;
+   /** revision from EEprom */
+   uint32           eep_rev;
+   /** Interface type */
+   uint16           Itype;
+   /** Device type */
+   uint16           Dtype;
+   /** output bits */
+   uint16           Obits;
+   /** output bytes, if Obits < 8 then Obytes = 0 */
+   uint32           Obytes;
+   /** output pointer in IOmap buffer */
+   uint8            *outputs;
+   /** startbit in first output byte */
+   uint8            Ostartbit;
+   /** input bits */
+   uint16           Ibits;
+   /** input bytes, if Ibits < 8 then Ibytes = 0 */
+   uint32           Ibytes;
+   /** input pointer in IOmap buffer */
+   uint8            *inputs;
+   /** startbit in first input byte */
+   uint8            Istartbit;
+   /** SM structure */
+   ec_smt           SM[EC_MAXSM];
+   /** SM type 0=unused 1=MbxWr 2=MbxRd 3=Outputs 4=Inputs */
+   uint8            SMtype[EC_MAXSM];
+   /** FMMU structure */
+   ec_fmmut         FMMU[EC_MAXFMMU];
+   /** FMMU0 function */
+   uint8            FMMU0func;
+   /** FMMU1 function */
+   uint8            FMMU1func;
+   /** FMMU2 function */
+   uint8            FMMU2func;
+   /** FMMU3 function */
+   uint8            FMMU3func;
+   /** length of write mailbox in bytes, if no mailbox then 0 */
+   uint16           mbx_l;
+   /** mailbox write offset */
+   uint16           mbx_wo;
+   /** length of read mailbox in bytes */
+   uint16           mbx_rl;
+   /** mailbox read offset */
+   uint16           mbx_ro;
+   /** mailbox supported protocols */
+   uint16           mbx_proto;
+   /** Counter value of mailbox link layer protocol 1..7 */
+   uint8            mbx_cnt;
+   /** has DC capability */
+   boolean          hasdc;
+   /** Physical type; Ebus, EtherNet combinations */
+   uint8            ptype;
+   /** topology: 1 to 3 links */
+   uint8            topology;
+   /** active ports bitmap : ....3210 , set if respective port is active **/
+   uint8            activeports;
+   /** consumed ports bitmap : ....3210, used for internal delay measurement **/
+   uint8            consumedports;
+   /** slave number for parent, 0=master */
+   uint16           parent;
+   /** port number on parent this slave is connected to **/
+   uint8            parentport;
+   /** port number on this slave the parent is connected to **/
+   uint8            entryport;
+   /** DC receivetimes on port A */
+   int32            DCrtA;
+   /** DC receivetimes on port B */
+   int32            DCrtB;
+   /** DC receivetimes on port C */
+   int32            DCrtC;
+   /** DC receivetimes on port D */
+   int32            DCrtD;
+   /** propagation delay */
+   int32            pdelay;
+   /** next DC slave */
+   uint16           DCnext;
+   /** previous DC slave */
+   uint16           DCprevious;
+   /** DC cycle time in ns */
+   int32            DCcycle;
+   /** DC shift from clock modulus boundary */
+   int32            DCshift;
+   /** DC sync activation, 0=off, 1=on */
+   uint8            DCactive;
+   /** link to config table */
+   uint16           configindex;
+   /** link to SII config */
+   uint16           SIIindex;
+   /** 1 = 8 bytes per read, 0 = 4 bytes per read */
+   uint8            eep_8byte;
+   /** 0 = eeprom to master , 1 = eeprom to PDI */
+   uint8            eep_pdi;
+   /** CoE details */
+   uint8            CoEdetails;
+   /** FoE details */
+   uint8            FoEdetails;
+   /** EoE details */
+   uint8            EoEdetails;
+   /** SoE details */
+   uint8            SoEdetails;
+   /** E-bus current */
+   int16            Ebuscurrent;
+   /** if >0 block use of LRW in processdata */
+   uint8            blockLRW;
+   /** group */
+   uint8            group;
+   /** first unused FMMU */
+   uint8            FMMUunused;
+   /** Boolean for tracking whether the slave is (not) responding, not used/set by the SOEM library */
+   boolean          islost;
+   /** registered configuration function PO->SO */
+   int              (*PO2SOconfig)(uint16 slave);
+   /** readable name */
+   char             name[EC_MAXNAME + 1];
+} ec_slavet;
+
+/** for list of ethercat slave groups */
+typedef struct ec_group
+{
+   /** logical start address for this group */
+   uint32           logstartaddr;
+   /** output bytes, if Obits < 8 then Obytes = 0 */
+   uint32           Obytes;
+   /** output pointer in IOmap buffer */
+   uint8            *outputs;
+   /** input bytes, if Ibits < 8 then Ibytes = 0 */
+   uint32           Ibytes;
+   /** input pointer in IOmap buffer */
+   uint8            *inputs;
+   /** has DC capabillity */
+   boolean          hasdc;
+   /** next DC slave */
+   uint16           DCnext;
+   /** E-bus current */
+   int16            Ebuscurrent;
+   /** if >0 block use of LRW in processdata */
+   uint8            blockLRW;
+   /** IO segments used */
+   uint16           nsegments;
+   /** 1st input segment */
+   uint16           Isegment;
+   /** Offset in input segment */
+   uint16           Ioffset;
+   /** Expected workcounter outputs */
+   uint16           outputsWKC;
+   /** Expected workcounter inputs */
+   uint16           inputsWKC;
+   /** check slave states */
+   boolean          docheckstate;
+   /** IO segmentation list. Datagrams must not break SM in two. */
+   uint32           IOsegment[EC_MAXIOSEGMENTS];
+} ec_groupt;
+
+/** SII FMMU structure */
+typedef struct ec_eepromFMMU
+{
+   uint16  Startpos;
+   uint8   nFMMU;
+   uint8   FMMU0;
+   uint8   FMMU1;
+   uint8   FMMU2;
+   uint8   FMMU3;
+} ec_eepromFMMUt;
+
+/** SII SM structure */
+typedef struct ec_eepromSM
+{
+   uint16  Startpos;
+   uint8   nSM;
+   uint16  PhStart;
+   uint16  Plength;
+   uint8   Creg;
+   uint8   Sreg;       /* don't care */
+   uint8   Activate;
+   uint8   PDIctrl;      /* don't care */
+} ec_eepromSMt;
+
+/** record to store rxPDO and txPDO table from eeprom */
+typedef struct ec_eepromPDO
+{
+   uint16  Startpos;
+   uint16  Length;
+   uint16  nPDO;
+   uint16  Index[EC_MAXEEPDO];
+   uint16  SyncM[EC_MAXEEPDO];
+   uint16  BitSize[EC_MAXEEPDO];
+   uint16  SMbitsize[EC_MAXSM];
+} ec_eepromPDOt;
+
+/** mailbox buffer array */
+typedef uint8 ec_mbxbuft[EC_MAXMBX + 1];
+
+/** standard ethercat mailbox header */
+PACKED_BEGIN
+typedef struct PACKED ec_mbxheader
+{
+   uint16  length;
+   uint16  address;
+   uint8   priority;
+   uint8   mbxtype;
+} ec_mbxheadert;
+PACKED_END
+
+/** ALstatus and ALstatus code */
+PACKED_BEGIN
+typedef struct PACKED ec_alstatus
+{
+   uint16  alstatus;
+   uint16  unused;
+   uint16  alstatuscode;
+} ec_alstatust;
+PACKED_END
+
+/** stack structure to store segmented LRD/LWR/LRW constructs */
+typedef struct ec_idxstack
+{
+   uint8   pushed;
+   uint8   pulled;
+   uint8   idx[EC_MAXBUF];
+   void    *data[EC_MAXBUF];
+   uint16  length[EC_MAXBUF];
+} ec_idxstackT;
+
+/** ringbuf for error storage */
+typedef struct ec_ering
+{
+   int16     head;
+   int16     tail;
+   ec_errort Error[EC_MAXELIST + 1];
+} ec_eringt;
+
+/** SyncManager Communication Type structure for CA */
+PACKED_BEGIN
+typedef struct PACKED ec_SMcommtype
+{
+   uint8   n;
+   uint8   nu1;
+   uint8   SMtype[EC_MAXSM];
+} ec_SMcommtypet;
+PACKED_END
+
+/** SDO assign structure for CA */
+PACKED_BEGIN
+typedef struct PACKED ec_PDOassign
+{
+   uint8   n;
+   uint8   nu1;
+   uint16  index[256];
+} ec_PDOassignt;
+PACKED_END
+
+/** SDO description structure for CA */
+PACKED_BEGIN
+typedef struct PACKED ec_PDOdesc
+{
+   uint8   n;
+   uint8   nu1;
+   uint32  PDO[256];
+} ec_PDOdesct;
+PACKED_END
+
+/** Context structure , referenced by all ecx functions*/
+typedef struct ecx_context ecx_contextt;
+struct ecx_context
+{
+   /** port reference, may include red_port */
+   ecx_portt      *port;
+   /** slavelist reference */
+   ec_slavet      *slavelist;
+   /** number of slaves found in configuration */
+   int            *slavecount;
+   /** maximum number of slaves allowed in slavelist */
+   int            maxslave;
+   /** grouplist reference */
+   ec_groupt      *grouplist;
+   /** maximum number of groups allowed in grouplist */
+   int            maxgroup;
+   /** internal, reference to eeprom cache buffer */
+   uint8          *esibuf;
+   /** internal, reference to eeprom cache map */
+   uint32         *esimap;
+   /** internal, current slave for eeprom cache */
+   uint16         esislave;
+   /** internal, reference to error list */
+   ec_eringt      *elist;
+   /** internal, reference to processdata stack buffer info */
+   ec_idxstackT   *idxstack;
+   /** reference to ecaterror state */
+   boolean        *ecaterror;
+   /** internal, position of DC datagram in process data packet */
+   uint16         DCtO;
+   /** internal, length of DC datagram */
+   uint16         DCl;
+   /** reference to last DC time from slaves */
+   int64          *DCtime;
+   /** internal, SM buffer */
+   ec_SMcommtypet *SMcommtype;
+   /** internal, PDO assign list */
+   ec_PDOassignt  *PDOassign;
+   /** internal, PDO description list */
+   ec_PDOdesct    *PDOdesc;
+   /** internal, SM list from eeprom */
+   ec_eepromSMt   *eepSM;
+   /** internal, FMMU list from eeprom */
+   ec_eepromFMMUt *eepFMMU;
+   /** registered FoE hook */
+   int            (*FOEhook)(uint16 slave, int packetnumber, int datasize);
+   /** registered EoE hook */
+   int            (*EOEhook)(ecx_contextt * context, uint16 slave, void * eoembx);
+};
+
+#ifdef EC_VER1
+/** global struct to hold default master context */
+extern ecx_contextt  ecx_context;
+/** main slave data structure array */
+extern ec_slavet   ec_slave[EC_MAXSLAVE];
+/** number of slaves found by configuration function */
+extern int         ec_slavecount;
+/** slave group structure */
+extern ec_groupt   ec_group[EC_MAXGROUP];
+extern boolean     EcatError;
+extern int64       ec_DCtime;
+
+void ec_pusherror(const ec_errort *Ec);
+boolean ec_poperror(ec_errort *Ec);
+boolean ec_iserror(void);
+void ec_packeterror(uint16 Slave, uint16 Index, uint8 SubIdx, uint16 ErrorCode);
+int ec_init(const char * ifname);
+int ec_init_redundant(const char *ifname, char *if2name);
+void ec_close(void);
+uint8 ec_siigetbyte(uint16 slave, uint16 address);
+int16 ec_siifind(uint16 slave, uint16 cat);
+void ec_siistring(char *str, uint16 slave, uint16 Sn);
+uint16 ec_siiFMMU(uint16 slave, ec_eepromFMMUt* FMMU);
+uint16 ec_siiSM(uint16 slave, ec_eepromSMt* SM);
+uint16 ec_siiSMnext(uint16 slave, ec_eepromSMt* SM, uint16 n);
+int ec_siiPDO(uint16 slave, ec_eepromPDOt* PDO, uint8 t);
+int ec_readstate(void);
+int ec_writestate(uint16 slave);
+uint16 ec_statecheck(uint16 slave, uint16 reqstate, int timeout);
+int ec_mbxempty(uint16 slave, int timeout);
+int ec_mbxsend(uint16 slave,ec_mbxbuft *mbx, int timeout);
+int ec_mbxreceive(uint16 slave, ec_mbxbuft *mbx, int timeout);
+void ec_esidump(uint16 slave, uint8 *esibuf);
+uint32 ec_readeeprom(uint16 slave, uint16 eeproma, int timeout);
+int ec_writeeeprom(uint16 slave, uint16 eeproma, uint16 data, int timeout);
+int ec_eeprom2master(uint16 slave);
+int ec_eeprom2pdi(uint16 slave);
+uint64 ec_readeepromAP(uint16 aiadr, uint16 eeproma, int timeout);
+int ec_writeeepromAP(uint16 aiadr, uint16 eeproma, uint16 data, int timeout);
+uint64 ec_readeepromFP(uint16 configadr, uint16 eeproma, int timeout);
+int ec_writeeepromFP(uint16 configadr, uint16 eeproma, uint16 data, int timeout);
+void ec_readeeprom1(uint16 slave, uint16 eeproma);
+uint32 ec_readeeprom2(uint16 slave, int timeout);
+int ec_send_processdata_group(uint8 group);
+int ec_send_overlap_processdata_group(uint8 group);
+int ec_receive_processdata_group(uint8 group, int timeout);
+int ec_send_processdata(void);
+int ec_send_overlap_processdata(void);
+int ec_receive_processdata(int timeout);
+#endif
+
+ec_adaptert * ec_find_adapters(void);
+void ec_free_adapters(ec_adaptert * adapter);
+uint8 ec_nextmbxcnt(uint8 cnt);
+void ec_clearmbx(ec_mbxbuft *Mbx);
+void ecx_pusherror(ecx_contextt *context, const ec_errort *Ec);
+boolean ecx_poperror(ecx_contextt *context, ec_errort *Ec);
+boolean ecx_iserror(ecx_contextt *context);
+void ecx_packeterror(ecx_contextt *context, uint16 Slave, uint16 Index, uint8 SubIdx, uint16 ErrorCode);
+int ecx_init(ecx_contextt *context, const char * ifname);
+int ecx_init_redundant(ecx_contextt *context, ecx_redportt *redport, const char *ifname, char *if2name);
+void ecx_close(ecx_contextt *context);
+uint8 ecx_siigetbyte(ecx_contextt *context, uint16 slave, uint16 address);
+int16 ecx_siifind(ecx_contextt *context, uint16 slave, uint16 cat);
+void ecx_siistring(ecx_contextt *context, char *str, uint16 slave, uint16 Sn);
+uint16 ecx_siiFMMU(ecx_contextt *context, uint16 slave, ec_eepromFMMUt* FMMU);
+uint16 ecx_siiSM(ecx_contextt *context, uint16 slave, ec_eepromSMt* SM);
+uint16 ecx_siiSMnext(ecx_contextt *context, uint16 slave, ec_eepromSMt* SM, uint16 n);
+int ecx_siiPDO(ecx_contextt *context, uint16 slave, ec_eepromPDOt* PDO, uint8 t);
+int ecx_readstate(ecx_contextt *context);
+int ecx_writestate(ecx_contextt *context, uint16 slave);
+uint16 ecx_statecheck(ecx_contextt *context, uint16 slave, uint16 reqstate, int timeout);
+int ecx_mbxempty(ecx_contextt *context, uint16 slave, int timeout);
+int ecx_mbxsend(ecx_contextt *context, uint16 slave,ec_mbxbuft *mbx, int timeout);
+int ecx_mbxreceive(ecx_contextt *context, uint16 slave, ec_mbxbuft *mbx, int timeout);
+void ecx_esidump(ecx_contextt *context, uint16 slave, uint8 *esibuf);
+uint32 ecx_readeeprom(ecx_contextt *context, uint16 slave, uint16 eeproma, int timeout);
+int ecx_writeeeprom(ecx_contextt *context, uint16 slave, uint16 eeproma, uint16 data, int timeout);
+int ecx_eeprom2master(ecx_contextt *context, uint16 slave);
+int ecx_eeprom2pdi(ecx_contextt *context, uint16 slave);
+uint64 ecx_readeepromAP(ecx_contextt *context, uint16 aiadr, uint16 eeproma, int timeout);
+int ecx_writeeepromAP(ecx_contextt *context, uint16 aiadr, uint16 eeproma, uint16 data, int timeout);
+uint64 ecx_readeepromFP(ecx_contextt *context, uint16 configadr, uint16 eeproma, int timeout);
+int ecx_writeeepromFP(ecx_contextt *context, uint16 configadr, uint16 eeproma, uint16 data, int timeout);
+void ecx_readeeprom1(ecx_contextt *context, uint16 slave, uint16 eeproma);
+uint32 ecx_readeeprom2(ecx_contextt *context, uint16 slave, int timeout);
+int ecx_send_overlap_processdata_group(ecx_contextt *context, uint8 group);
+int ecx_receive_processdata_group(ecx_contextt *context, uint8 group, int timeout);
+int ecx_send_processdata(ecx_contextt *context);
+int ecx_send_overlap_processdata(ecx_contextt *context);
+int ecx_receive_processdata(ecx_contextt *context, int timeout);
+int ecx_send_processdata_group(ecx_contextt *context, uint8 group);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff -r 000000000000 -r 543d6784d4cc SOEM/ethercatprint.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SOEM/ethercatprint.c	Tue Jun 11 10:29:09 2019 +0000
@@ -0,0 +1,365 @@
+/*
+ * Licensed under the GNU General Public License version 2 with exceptions. See
+ * LICENSE file in the project root for full license information
+ */
+
+/** \file
+ * \brief
+ * Module to convert EtherCAT errors to readable messages.
+ *
+ * SDO abort messages and AL status codes are used to relay slave errors to
+ * the user application. This module converts the binary codes to readable text.
+ * For the defined error codes see the EtherCAT protocol documentation.
+ */
+
+#include <stdio.h>
+#include "oshw.h"
+#include "ethercattype.h"
+#include "ethercatmain.h"
+
+#define EC_MAXERRORNAME 127
+
+/** SDO error list type definition */
+typedef struct
+{
+   /** Error code returned from SDO */
+   uint32        errorcode;
+   /** Readable error description */
+   char          errordescription[EC_MAXERRORNAME + 1];
+} ec_sdoerrorlist_t;
+
+/** AL status code list type definition */
+typedef struct
+{
+   /** AL status code */
+   uint16        ALstatuscode;
+   /** Readable description */
+   char          ALstatuscodedescription[EC_MAXERRORNAME + 1];
+} ec_ALstatuscodelist_t;
+
+/** SoE error list type definition */
+typedef struct
+{
+   /** SoE error code */
+   uint16        errorcode;
+   /** Readable description */
+   char          errordescription[EC_MAXERRORNAME + 1];
+} ec_soeerrorlist_t;
+
+/** MBX error list type definition */
+typedef struct
+{
+   /** MBX error code */
+   uint16              errorcode;
+   /** Readable description */
+   char                errordescription[EC_MAXERRORNAME + 1];
+} ec_mbxerrorlist_t;
+
+char estring[EC_MAXERRORNAME];
+
+/** SDO error list definition */
+const ec_sdoerrorlist_t ec_sdoerrorlist[] = {
+   {0x00000000, "No error" },
+   {0x05030000, "Toggle bit not changed" },
+   {0x05040000, "SDO protocol timeout" },
+   {0x05040001, "Client/Server command specifier not valid or unknown" },
+   {0x05040005, "Out of memory" },
+   {0x06010000, "Unsupported access to an object" },
+   {0x06010001, "Attempt to read to a write only object" },
+   {0x06010002, "Attempt to write to a read only object" },
+   {0x06010003, "Subindex can not be written, SI0 must be 0 for write access" },
+   {0x06010004, "SDO Complete access not supported for variable length objects" },
+   {0x06010005, "Object length exceeds mailbox size" },
+   {0x06010006, "Object mapped to RxPDO, SDO download blocked" },
+   {0x06020000, "The object does not exist in the object directory" },
+   {0x06040041, "The object can not be mapped into the PDO" },
+   {0x06040042, "The number and length of the objects to be mapped would exceed the PDO length" },
+   {0x06040043, "General parameter incompatibility reason" },
+   {0x06040047, "General internal incompatibility in the device" },
+   {0x06060000, "Access failed due to a hardware error" },
+   {0x06070010, "Data type does not match, length of service parameter does not match" },
+   {0x06070012, "Data type does not match, length of service parameter too high" },
+   {0x06070013, "Data type does not match, length of service parameter too low" },
+   {0x06090011, "Subindex does not exist" },
+   {0x06090030, "Value range of parameter exceeded (only for write access)" },
+   {0x06090031, "Value of parameter written too high" },
+   {0x06090032, "Value of parameter written too low" },
+   {0x06090036, "Maximum value is less than minimum value" },
+   {0x08000000, "General error" },
+   {0x08000020, "Data cannot be transferred or stored to the application" },
+   {0x08000021, "Data cannot be transferred or stored to the application because of local control" },
+   {0x08000022, "Data cannot be transferred or stored to the application because of the present device state" },
+   {0x08000023, "Object dictionary dynamic generation fails or no object dictionary is present" },
+   {0xffffffff, "Unknown" }
+};
+
+/** AL status code list definition */
+const ec_ALstatuscodelist_t ec_ALstatuscodelist[] = {
+   {0x0000 , "No error" },
+   {0x0001 , "Unspecified error" },
+   {0x0002 , "No memory" },
+   {0x0011 , "Invalid requested state change" },
+   {0x0012 , "Unknown requested state" },
+   {0x0013 , "Bootstrap not supported" },
+   {0x0014 , "No valid firmware" },
+   {0x0015 , "Invalid mailbox configuration" },
+   {0x0016 , "Invalid mailbox configuration" },
+   {0x0017 , "Invalid sync manager configuration" },
+   {0x0018 , "No valid inputs available" },
+   {0x0019 , "No valid outputs" },
+   {0x001A , "Synchronization error" },
+   {0x001B , "Sync manager watchdog" },
+   {0x001C , "Invalid sync Manager types" },
+   {0x001D , "Invalid output configuration" },
+   {0x001E , "Invalid input configuration" },
+   {0x001F , "Invalid watchdog configuration" },
+   {0x0020 , "Slave needs cold start" },
+   {0x0021 , "Slave needs INIT" },
+   {0x0022 , "Slave needs PREOP" },
+   {0x0023 , "Slave needs SAFEOP" },
+   {0x0024 , "Invalid input mapping" },
+   {0x0025 , "Invalid output mapping" },
+   {0x0026 , "Inconsistent settings" },
+   {0x0027 , "Freerun not supported" },
+   {0x0028 , "Synchronisation not supported" },
+   {0x0029 , "Freerun needs 3buffer mode" },
+   {0x002A , "Background watchdog" },
+   {0x002B , "No valid Inputs and Outputs" },
+   {0x002C , "Fatal sync error" },
+   {0x002D , "No sync error" }, // was "Invalid Output FMMU Configuration"
+   {0x002E , "Invalid input FMMU configuration" },
+   {0x0030 , "Invalid DC SYNC configuration" },
+   {0x0031 , "Invalid DC latch configuration" },
+   {0x0032 , "PLL error" },
+   {0x0033 , "DC sync IO error" },
+   {0x0034 , "DC sync timeout error" },
+   {0x0035 , "DC invalid sync cycle time" },
+   {0x0036 , "DC invalid sync0 cycle time" },
+   {0x0037 , "DC invalid sync1 cycle time" },
+   {0x0041 , "MBX_AOE" },
+   {0x0042 , "MBX_EOE" },
+   {0x0043 , "MBX_COE" },
+   {0x0044 , "MBX_FOE" },
+   {0x0045 , "MBX_SOE" },
+   {0x004F , "MBX_VOE" },
+   {0x0050 , "EEPROM no access" },
+   {0x0051 , "EEPROM error" },
+   {0x0060 , "Slave restarted locally" },
+   {0x0061 , "Device identification value updated" },
+   {0x00f0 , "Application controller available" },
+   {0xffff , "Unknown" }
+};
+
+/** SoE error list definition */
+const ec_soeerrorlist_t ec_soeerrorlist[] = {
+   {0x0000, "No error" },
+   {0x1001, "No IDN" },
+   {0x1009, "Invalid access to element 1" },
+   {0x2001, "No Name" },
+   {0x2002, "Name transmission too short" },
+   {0x2003, "Name transmission too long" },
+   {0x2004, "Name cannot be changed (read only)" },
+   {0x2005, "Name is write-protected at this time" },
+   {0x3002, "Attribute transmission too short" },
+   {0x3003, "Attribute transmission too long" },
+   {0x3004, "Attribute cannot be changed (read only)" },
+   {0x3005, "Attribute is write-protected at this time" },
+   {0x4001, "No units" },
+   {0x4002, "Unit transmission too short" },
+   {0x4003, "Unit transmission too long" },
+   {0x4004, "Unit cannot be changed (read only)" },
+   {0x4005, "Unit is write-protected at this time" },
+   {0x5001, "No minimum input value" },
+   {0x5002, "Minimum input value transmission too short" },
+   {0x5003, "Minimum input value transmission too long" },
+   {0x5004, "Minimum input value cannot be changed (read only)" },
+   {0x5005, "Minimum input value is write-protected at this time" },
+   {0x6001, "No maximum input value" },
+   {0x6002, "Maximum input value transmission too short" },
+   {0x6003, "Maximum input value transmission too long" },
+   {0x6004, "Maximum input value cannot be changed (read only)" },
+   {0x6005, "Maximum input value is write-protected at this time" },
+   {0x7002, "Operation data transmission too short" },
+   {0x7003, "Operation data transmission too long" },
+   {0x7004, "Operation data cannot be changed (read only)" },
+   {0x7005, "Operation data is write-protected at this time (state)" },
+   {0x7006, "Operation data is smaller than the minimum input value" },
+   {0x7007, "Operation data is smaller than the maximum input value" },
+   {0x7008, "Invalid operation data:Configured IDN will not be supported" },
+   {0x7009, "Operation data write protected by a password" },
+   {0x700A, "Operation data is write protected, it is configured cyclically" },
+   {0x700B, "Invalid indirect addressing: (e.g., data container, list handling)" },
+   {0x700C, "Operation data is write protected, due to other settings" },
+   {0x700D, "Reserved" },
+   {0x7010, "Procedure command already active" },
+   {0x7011, "Procedure command not interruptible" },
+   {0x7012, "Procedure command at this time not executable (state)" },
+   {0x7013, "Procedure command not executable (invalid or false parameters)" },
+   {0x7014, "No data state" },
+   {0x8001, "No default value" },
+   {0x8002, "Default value transmission too long" },
+   {0x8004, "Default value cannot be changed, read only" },
+   {0x800A, "Invalid drive number" },
+   {0x800A, "General error" },
+   {0x800A, "No element addressed" },
+   {0xffff, "Unknown" }
+};
+
+/** MBX error list definition */
+const ec_mbxerrorlist_t ec_mbxerrorlist[] = {
+   {0x0000, "No error" },
+   {0x0001, "Syntax of 6 octet Mailbox Header is wrong" },
+   {0x0002, "The mailbox protocol is not supported" },
+   {0x0003, "Channel Field contains wrong value"},
+   {0x0004, "The service is no supported"},
+   {0x0005, "Invalid mailbox header"},
+   {0x0006, "Length of received mailbox data is too short"},
+   {0x0007, "No more memory in slave"},
+   {0x0008, "The length of data is inconsistent"},
+   {0xffff, "Unknown"}
+};
+
+/** Look up text string that belongs to SDO error code.
+ *
+ * @param[in] sdoerrorcode   = SDO error code as defined in EtherCAT protocol
+ * @return readable string
+ */
+const char* ec_sdoerror2string( uint32 sdoerrorcode)
+{
+   int i = 0;
+
+   while ( (ec_sdoerrorlist[i].errorcode != 0xffffffffUL) &&
+           (ec_sdoerrorlist[i].errorcode != sdoerrorcode) )
+   {
+      i++;
+   }
+
+   return ec_sdoerrorlist[i].errordescription;
+}
+
+/** Look up text string that belongs to AL status code.
+ *
+ * @param[in] ALstatuscode   = AL status code as defined in EtherCAT protocol
+ * @return readable string
+ */
+char* ec_ALstatuscode2string( uint16 ALstatuscode)
+{
+   int i = 0;
+
+   while ( (ec_ALstatuscodelist[i].ALstatuscode != 0xffff) &&
+           (ec_ALstatuscodelist[i].ALstatuscode != ALstatuscode) )
+   {
+      i++;
+   }
+
+   return (char *) ec_ALstatuscodelist[i].ALstatuscodedescription;
+}
+
+/** Look up text string that belongs to SoE error code.
+ *
+ * @param[in] errorcode   = SoE error code as defined in EtherCAT protocol
+ * @return readable string
+ */
+char* ec_soeerror2string( uint16 errorcode)
+{
+   int i = 0;
+
+   while ( (ec_soeerrorlist[i].errorcode != 0xffff) &&
+           (ec_soeerrorlist[i].errorcode != errorcode) )
+   {
+      i++;
+   }
+
+   return (char *) ec_soeerrorlist[i].errordescription;
+}
+
+/** Look up text string that belongs to MBX error code.
+ *
+ * @param[in] errorcode   = MBX error code as defined in EtherCAT protocol
+ * @return readable string
+ */
+char* ec_mbxerror2string( uint16 errorcode)
+{
+   int i = 0;
+
+   while ( (ec_mbxerrorlist[i].errorcode != 0xffff) &&
+           (ec_mbxerrorlist[i].errorcode != errorcode) )
+   {
+      i++;
+   }
+
+   return (char *) ec_mbxerrorlist[i].errordescription;
+}
+
+/** Look up error in ec_errorlist and convert to text string.
+ *
+ * @param[in]  context        = context struct
+ * @return readable string
+ */
+char* ecx_elist2string(ecx_contextt *context)
+{
+   ec_errort Ec;
+   char timestr[20];
+
+   if (ecx_poperror(context, &Ec))
+   {
+      sprintf(timestr, "Time:%12.3f", Ec.Time.sec + (Ec.Time.usec / 1000000.0) );
+      switch (Ec.Etype)
+      {
+         case EC_ERR_TYPE_SDO_ERROR:
+         {
+            sprintf(estring, "%s SDO slave:%d index:%4.4x.%2.2x error:%8.8x %s\n",
+                    timestr, Ec.Slave, Ec.Index, Ec.SubIdx, (unsigned)Ec.AbortCode, ec_sdoerror2string(Ec.AbortCode));
+            break;
+         }
+         case EC_ERR_TYPE_EMERGENCY:
+         {
+            sprintf(estring, "%s EMERGENCY slave:%d error:%4.4x\n",
+                    timestr, Ec.Slave, Ec.ErrorCode);
+            break;
+         }
+         case EC_ERR_TYPE_PACKET_ERROR:
+         {
+            sprintf(estring, "%s PACKET slave:%d index:%4.4x.%2.2x error:%d\n",
+                    timestr, Ec.Slave, Ec.Index, Ec.SubIdx, Ec.ErrorCode);
+            break;
+         }
+         case EC_ERR_TYPE_SDOINFO_ERROR:
+         {
+            sprintf(estring, "%s SDO slave:%d index:%4.4x.%2.2x error:%8.8x %s\n",
+                    timestr, Ec.Slave, Ec.Index, Ec.SubIdx, (unsigned)Ec.AbortCode, ec_sdoerror2string(Ec.AbortCode));
+            break;
+         }
+         case EC_ERR_TYPE_SOE_ERROR:
+         {
+            sprintf(estring, "%s SoE slave:%d IDN:%4.4x error:%4.4x %s\n",
+                    timestr, Ec.Slave, Ec.Index, (unsigned)Ec.AbortCode, ec_soeerror2string(Ec.ErrorCode));
+            break;
+         }
+         case EC_ERR_TYPE_MBX_ERROR:
+         {
+            sprintf(estring, "%s MBX slave:%d error:%4.4x %s\n",
+                    timestr, Ec.Slave, Ec.ErrorCode, ec_mbxerror2string(Ec.ErrorCode));
+            break;
+         }
+         default:
+         {
+            sprintf(estring, "%s error:%8.8x\n",
+                    timestr, (unsigned)Ec.AbortCode);
+            break;
+         }
+      }
+      return (char*) estring;
+   }
+   else
+   {
+      return "";
+   }
+}
+
+#ifdef EC_VER1
+char* ec_elist2string(void)
+{
+   return ecx_elist2string(&ecx_context);
+}
+#endif
diff -r 000000000000 -r 543d6784d4cc SOEM/ethercatprint.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SOEM/ethercatprint.h	Tue Jun 11 10:29:09 2019 +0000
@@ -0,0 +1,32 @@
+/*
+ * Licensed under the GNU General Public License version 2 with exceptions. See
+ * LICENSE file in the project root for full license information
+ */
+
+/** \file
+ * \brief
+ * Headerfile for ethercatprint.c
+ */
+
+#ifndef _ethercatprint_
+#define _ethercatprint_
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+char* ec_sdoerror2string( uint32 sdoerrorcode);
+char* ec_ALstatuscode2string( uint16 ALstatuscode);
+char* ec_soeerror2string( uint16 errorcode);
+char* ecx_elist2string(ecx_contextt *context);
+
+#ifdef EC_VER1
+char* ec_elist2string(void);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff -r 000000000000 -r 543d6784d4cc SOEM/ethercatsoe.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SOEM/ethercatsoe.c	Tue Jun 11 10:29:09 2019 +0000
@@ -0,0 +1,389 @@
+/*
+ * Licensed under the GNU General Public License version 2 with exceptions. See
+ * LICENSE file in the project root for full license information
+ */
+
+/** \file
+ * \brief
+ * Servo over EtherCAT (SoE) Module.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include "osal.h"
+#include "oshw.h"
+#include "ethercattype.h"
+#include "ethercatbase.h"
+#include "ethercatmain.h"
+#include "ethercatsoe.h"
+
+#define EC_SOE_MAX_DRIVES 8
+
+/** SoE (Servo over EtherCAT) mailbox structure */
+PACKED_BEGIN
+typedef struct PACKED
+{
+   ec_mbxheadert MbxHeader;
+   uint8         opCode         :3;
+   uint8         incomplete     :1;
+   uint8         error          :1;
+   uint8         driveNo        :3;
+   uint8         elementflags;
+   union
+   {
+      uint16     idn;
+      uint16     fragmentsleft;
+   };
+} ec_SoEt;
+PACKED_END
+
+/** Report SoE error.
+ *
+ * @param[in]  context        = context struct
+ * @param[in]  Slave      = Slave number
+ * @param[in]  idn        = IDN that generated error
+ * @param[in]  Error      = Error code, see EtherCAT documentation for list
+ */
+void ecx_SoEerror(ecx_contextt *context, uint16 Slave, uint16 idn, uint16 Error)
+{
+   ec_errort Ec;
+
+   memset(&Ec, 0, sizeof(Ec));
+   Ec.Time = osal_current_time();
+   Ec.Slave = Slave;
+   Ec.Index = idn;
+   Ec.SubIdx = 0;
+   *(context->ecaterror) = TRUE;
+   Ec.Etype = EC_ERR_TYPE_SOE_ERROR;
+   Ec.ErrorCode = Error;
+   ecx_pusherror(context, &Ec);
+}
+
+/** SoE read, blocking.
+ *
+ * The IDN object of the selected slave and DriveNo is read. If a response
+ * is larger than the mailbox size then the response is segmented. The function
+ * will combine all segments and copy them to the parameter buffer.
+ *
+ * @param[in]  context        = context struct
+ * @param[in]  slave         = Slave number
+ * @param[in]  driveNo       = Drive number in slave
+ * @param[in]  elementflags  = Flags to select what properties of IDN are to be transferred.
+ * @param[in]  idn           = IDN.
+ * @param[in,out] psize      = Size in bytes of parameter buffer, returns bytes read from SoE.
+ * @param[out] p             = Pointer to parameter buffer
+ * @param[in]  timeout       = Timeout in us, standard is EC_TIMEOUTRXM
+ * @return Workcounter from last slave response
+ */
+int ecx_SoEread(ecx_contextt *context, uint16 slave, uint8 driveNo, uint8 elementflags, uint16 idn, int *psize, void *p, int timeout)
+{
+   ec_SoEt *SoEp, *aSoEp;
+   uint16 totalsize, framedatasize;
+   int wkc;
+   uint8 *bp;
+   uint8 *mp;
+   uint16 *errorcode;
+   ec_mbxbuft MbxIn, MbxOut;
+   uint8 cnt;
+   boolean NotLast;
+
+   ec_clearmbx(&MbxIn);
+   /* Empty slave out mailbox if something is in. Timeout set to 0 */
+   wkc = ecx_mbxreceive(context, slave, (ec_mbxbuft *)&MbxIn, 0);
+   ec_clearmbx(&MbxOut);
+   aSoEp = (ec_SoEt *)&MbxIn;
+   SoEp = (ec_SoEt *)&MbxOut;
+   SoEp->MbxHeader.length = htoes(sizeof(ec_SoEt) - sizeof(ec_mbxheadert));
+   SoEp->MbxHeader.address = htoes(0x0000);
+   SoEp->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;
+   SoEp->MbxHeader.mbxtype = ECT_MBXT_SOE + (cnt << 4); /* SoE */
+   SoEp->opCode = ECT_SOE_READREQ;
+   SoEp->incomplete = 0;
+   SoEp->error = 0;
+   SoEp->driveNo = driveNo;
+   SoEp->elementflags = elementflags;
+   SoEp->idn = htoes(idn);
+   totalsize = 0;
+   bp = p;
+   mp = (uint8 *)&MbxIn + sizeof(ec_SoEt);
+   NotLast = TRUE;
+   /* send SoE request to slave */
+   wkc = ecx_mbxsend(context, slave, (ec_mbxbuft *)&MbxOut, EC_TIMEOUTTXM);
+   if (wkc > 0) /* succeeded to place mailbox in slave ? */
+   {
+      while (NotLast)
+      {
+         /* 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 SoE, ReadRes */
+            if (((aSoEp->MbxHeader.mbxtype & 0x0f) == ECT_MBXT_SOE) &&
+                (aSoEp->opCode == ECT_SOE_READRES) &&
+                (aSoEp->error == 0) &&
+                (aSoEp->driveNo == driveNo) &&
+                (aSoEp->elementflags == elementflags))
+            {
+               framedatasize = etohs(aSoEp->MbxHeader.length) - sizeof(ec_SoEt)  + sizeof(ec_mbxheadert);
+               totalsize += framedatasize;
+               /* Does parameter fit in parameter buffer ? */
+               if (totalsize <= *psize)
+               {
+                  /* copy parameter data in parameter buffer */
+                  memcpy(bp, mp, framedatasize);
+                  /* increment buffer pointer */
+                  bp += framedatasize;
+               }
+               else
+               {
+                  framedatasize -= totalsize - *psize;
+                  totalsize = *psize;
+                  /* copy parameter data in parameter buffer */
+                  if (framedatasize > 0) memcpy(bp, mp, framedatasize);
+               }
+
+               if (!aSoEp->incomplete)
+               {
+                  NotLast = FALSE;
+                  *psize = totalsize;
+               }
+            }
+            /* other slave response */
+            else
+            {
+               NotLast = FALSE;
+               if (((aSoEp->MbxHeader.mbxtype & 0x0f) == ECT_MBXT_SOE) &&
+                   (aSoEp->opCode == ECT_SOE_READRES) &&
+                   (aSoEp->error == 1))
+               {
+                  mp = (uint8 *)&MbxIn + (etohs(aSoEp->MbxHeader.length) + sizeof(ec_mbxheadert) - sizeof(uint16));
+                  errorcode = (uint16 *)mp;
+                  ecx_SoEerror(context, slave, idn, *errorcode);
+               }
+               else
+               {
+                  ecx_packeterror(context, slave, idn, 0, 1); /* Unexpected frame returned */
+               }
+               wkc = 0;
+            }
+         }
+         else
+         {
+            NotLast = FALSE;
+            ecx_packeterror(context, slave, idn, 0, 4); /* no response */
+         }
+      }
+   }
+   return wkc;
+}
+
+/** SoE write, blocking.
+ *
+ * The IDN object of the selected slave and DriveNo is written. If a response
+ * is larger than the mailbox size then the response is segmented.
+ *
+ * @param[in]  context        = context struct
+ * @param[in]  slave         = Slave number
+ * @param[in]  driveNo       = Drive number in slave
+ * @param[in]  elementflags  = Flags to select what properties of IDN are to be transferred.
+ * @param[in]  idn           = IDN.
+ * @param[in]  psize         = Size in bytes of parameter buffer.
+ * @param[out] p             = Pointer to parameter buffer
+ * @param[in]  timeout       = Timeout in us, standard is EC_TIMEOUTRXM
+ * @return Workcounter from last slave response
+ */
+int ecx_SoEwrite(ecx_contextt *context, uint16 slave, uint8 driveNo, uint8 elementflags, uint16 idn, int psize, void *p, int timeout)
+{
+   ec_SoEt *SoEp, *aSoEp;
+   uint16 framedatasize, maxdata;
+   int wkc;
+   uint8 *mp;
+   uint8 *hp;
+   uint16 *errorcode;
+   ec_mbxbuft MbxIn, MbxOut;
+   uint8 cnt;
+   boolean NotLast;
+
+   ec_clearmbx(&MbxIn);
+   /* Empty slave out mailbox if something is in. Timeout set to 0 */
+   wkc = ecx_mbxreceive(context, slave, (ec_mbxbuft *)&MbxIn, 0);
+   ec_clearmbx(&MbxOut);
+   aSoEp = (ec_SoEt *)&MbxIn;
+   SoEp = (ec_SoEt *)&MbxOut;
+   SoEp->MbxHeader.address = htoes(0x0000);
+   SoEp->MbxHeader.priority = 0x00;
+   SoEp->opCode = ECT_SOE_WRITEREQ;
+   SoEp->error = 0;
+   SoEp->driveNo = driveNo;
+   SoEp->elementflags = elementflags;
+   hp = p;
+   mp = (uint8 *)&MbxOut + sizeof(ec_SoEt);
+   maxdata = context->slavelist[slave].mbx_l - sizeof(ec_SoEt);
+   NotLast = TRUE;
+   while (NotLast)
+   {
+      framedatasize = psize;
+      NotLast = FALSE;
+      SoEp->idn = htoes(idn);
+      SoEp->incomplete = 0;
+      if (framedatasize > maxdata)
+      {
+         framedatasize = maxdata;  /*  segmented transfer needed  */
+         NotLast = TRUE;
+         SoEp->incomplete = 1;
+         SoEp->fragmentsleft = psize / maxdata;
+      }
+      SoEp->MbxHeader.length = htoes(sizeof(ec_SoEt) - sizeof(ec_mbxheadert) + framedatasize);
+      /* get new mailbox counter, used for session handle */
+      cnt = ec_nextmbxcnt(context->slavelist[slave].mbx_cnt);
+      context->slavelist[slave].mbx_cnt = cnt;
+      SoEp->MbxHeader.mbxtype = ECT_MBXT_SOE + (cnt << 4); /* SoE */
+      /* copy parameter data to mailbox */
+      memcpy(mp, hp, framedatasize);
+      hp += framedatasize;
+      psize -= framedatasize;
+      /* send SoE request to slave */
+      wkc = ecx_mbxsend(context, slave, (ec_mbxbuft *)&MbxOut, EC_TIMEOUTTXM);
+      if (wkc > 0) /* succeeded to place mailbox in slave ? */
+      {
+         if (!NotLast || !ecx_mbxempty(context, slave, timeout))
+         {
+            /* 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 ? */
+            {
+               NotLast = FALSE;
+               /* slave response should be SoE, WriteRes */
+               if (((aSoEp->MbxHeader.mbxtype & 0x0f) == ECT_MBXT_SOE) &&
+                   (aSoEp->opCode == ECT_SOE_WRITERES) &&
+                   (aSoEp->error == 0) &&
+                   (aSoEp->driveNo == driveNo) &&
+                   (aSoEp->elementflags == elementflags))
+               {
+                  /* SoE write succeeded */
+               }
+               /* other slave response */
+               else
+               {
+                  if (((aSoEp->MbxHeader.mbxtype & 0x0f) == ECT_MBXT_SOE) &&
+                      (aSoEp->opCode == ECT_SOE_READRES) &&
+                      (aSoEp->error == 1))
+                  {
+                     mp = (uint8 *)&MbxIn + (etohs(aSoEp->MbxHeader.length) + sizeof(ec_mbxheadert) - sizeof(uint16));
+                     errorcode = (uint16 *)mp;
+                     ecx_SoEerror(context, slave, idn, *errorcode);
+                  }
+                  else
+                  {
+                     ecx_packeterror(context, slave, idn, 0, 1); /* Unexpected frame returned */
+                  }
+                  wkc = 0;
+               }
+            }
+            else
+            {
+               ecx_packeterror(context, slave, idn, 0, 4); /* no response */
+            }
+         }
+      }
+   }
+   return wkc;
+}
+
+/** SoE read AT and MTD mapping.
+ *
+ * SoE has standard indexes defined for mapping. This function
+ * tries to read them and collect a full input and output mapping size
+ * of designated slave.
+ *
+ * @param[in]  context = context struct
+ * @param[in]  slave   = Slave number
+ * @param[out] Osize   = Size in bits of output mapping (MTD) found
+ * @param[out] Isize   = Size in bits of input mapping (AT) found
+ * @return >0 if mapping successful.
+ */
+int ecx_readIDNmap(ecx_contextt *context, uint16 slave, int *Osize, int *Isize)
+{
+   int retVal = 0;
+   int   wkc;
+   int psize;
+   int driveNr;
+   uint16 entries, itemcount;
+   ec_SoEmappingt     SoEmapping;
+   ec_SoEattributet   SoEattribute;
+
+   *Isize = 0;
+   *Osize = 0;
+   for(driveNr = 0; driveNr < EC_SOE_MAX_DRIVES; driveNr++)
+   {
+      psize = sizeof(SoEmapping);
+      /* read output mapping via SoE */
+      wkc = ecx_SoEread(context, slave, driveNr, EC_SOE_VALUE_B, EC_IDN_MDTCONFIG, &psize, &SoEmapping, EC_TIMEOUTRXM);
+      if ((wkc > 0) && (psize >= 4) && ((entries = etohs(SoEmapping.currentlength) / 2) > 0) && (entries <= EC_SOE_MAXMAPPING))
+      {
+         /* command word (uint16) is always mapped but not in list */
+         *Osize = 16;
+         for (itemcount = 0 ; itemcount < entries ; itemcount++)
+         {
+            psize = sizeof(SoEattribute);
+            /* read attribute of each IDN in mapping list */
+            wkc = ecx_SoEread(context, slave, driveNr, EC_SOE_ATTRIBUTE_B, SoEmapping.idn[itemcount], &psize, &SoEattribute, EC_TIMEOUTRXM);
+            if ((wkc > 0) && (!SoEattribute.list))
+            {
+               /* length : 0 = 8bit, 1 = 16bit .... */
+               *Osize += (int)8 << SoEattribute.length;
+            }
+         }
+      }
+      psize = sizeof(SoEmapping);
+      /* read input mapping via SoE */
+      wkc = ecx_SoEread(context, slave, driveNr, EC_SOE_VALUE_B, EC_IDN_ATCONFIG, &psize, &SoEmapping, EC_TIMEOUTRXM);
+      if ((wkc > 0) && (psize >= 4) && ((entries = etohs(SoEmapping.currentlength) / 2) > 0) && (entries <= EC_SOE_MAXMAPPING))
+      {
+         /* status word (uint16) is always mapped but not in list */
+         *Isize = 16;
+         for (itemcount = 0 ; itemcount < entries ; itemcount++)
+         {
+            psize = sizeof(SoEattribute);
+            /* read attribute of each IDN in mapping list */
+            wkc = ecx_SoEread(context, slave, driveNr, EC_SOE_ATTRIBUTE_B, SoEmapping.idn[itemcount], &psize, &SoEattribute, EC_TIMEOUTRXM);
+            if ((wkc > 0) && (!SoEattribute.list))
+            {
+               /* length : 0 = 8bit, 1 = 16bit .... */
+               *Isize += (int)8 << SoEattribute.length;
+            }
+         }
+      }
+   }
+
+   /* found some I/O bits ? */
+   if ((*Isize > 0) || (*Osize > 0))
+   {
+      retVal = 1;
+   }
+   return retVal;
+}
+
+#ifdef EC_VER1
+int ec_SoEread(uint16 slave, uint8 driveNo, uint8 elementflags, uint16 idn, int *psize, void *p, int timeout)
+{
+   return ecx_SoEread(&ecx_context, slave, driveNo, elementflags, idn, psize, p, timeout);
+}
+
+int ec_SoEwrite(uint16 slave, uint8 driveNo, uint8 elementflags, uint16 idn, int psize, void *p, int timeout)
+{
+   return ecx_SoEwrite(&ecx_context, slave, driveNo, elementflags, idn, psize, p, timeout);
+}
+
+int ec_readIDNmap(uint16 slave, int *Osize, int *Isize)
+{
+   return ecx_readIDNmap(&ecx_context, slave, Osize, Isize);
+}
+#endif
diff -r 000000000000 -r 543d6784d4cc SOEM/ethercatsoe.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SOEM/ethercatsoe.h	Tue Jun 11 10:29:09 2019 +0000
@@ -0,0 +1,130 @@
+/*
+ * Licensed under the GNU General Public License version 2 with exceptions. See
+ * LICENSE file in the project root for full license information
+ */
+
+/** \file
+ * \brief
+ * Headerfile for ethercatsoe.c
+ */
+
+#ifndef _ethercatsoe_
+#define _ethercatsoe_
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#define EC_SOE_DATASTATE_B   0x01
+#define EC_SOE_NAME_B        0x02
+#define EC_SOE_ATTRIBUTE_B   0x04
+#define EC_SOE_UNIT_B        0x08
+#define EC_SOE_MIN_B         0x10
+#define EC_SOE_MAX_B         0x20
+#define EC_SOE_VALUE_B       0x40
+#define EC_SOE_DEFAULT_B     0x80
+
+#define EC_SOE_MAXNAME       60
+#define EC_SOE_MAXMAPPING    64
+
+#define EC_IDN_MDTCONFIG     24
+#define EC_IDN_ATCONFIG      16
+
+/** SoE name structure */
+PACKED_BEGIN
+typedef struct PACKED
+{
+   /** current length in bytes of list */
+   uint16     currentlength;
+   /** maximum length in bytes of list */
+   uint16     maxlength;
+   char       name[EC_SOE_MAXNAME];
+} ec_SoEnamet;
+PACKED_END
+
+/** SoE list structure */
+PACKED_BEGIN
+typedef struct PACKED
+{
+   /** current length in bytes of list */
+   uint16     currentlength;
+   /** maximum length in bytes of list */
+   uint16     maxlength;
+   union
+   {
+      uint8   byte[8];
+      uint16  word[4];
+      uint32  dword[2];
+      uint64  lword[1];
+   };
+} ec_SoElistt;
+PACKED_END
+
+/** SoE IDN mapping structure */
+PACKED_BEGIN
+typedef struct PACKED
+{
+   /** current length in bytes of list */
+   uint16     currentlength;
+   /** maximum length in bytes of list */
+   uint16     maxlength;
+   uint16     idn[EC_SOE_MAXMAPPING];
+} ec_SoEmappingt;
+PACKED_END
+
+#define EC_SOE_LENGTH_1         0x00
+#define EC_SOE_LENGTH_2         0x01
+#define EC_SOE_LENGTH_4         0x02
+#define EC_SOE_LENGTH_8         0x03
+#define EC_SOE_TYPE_BINARY      0x00
+#define EC_SOE_TYPE_UINT        0x01
+#define EC_SOE_TYPE_INT         0x02
+#define EC_SOE_TYPE_HEX         0x03
+#define EC_SOE_TYPE_STRING      0x04
+#define EC_SOE_TYPE_IDN         0x05
+#define EC_SOE_TYPE_FLOAT       0x06
+#define EC_SOE_TYPE_PARAMETER   0x07
+
+/** SoE attribute structure */
+PACKED_BEGIN
+typedef struct PACKED
+{
+   /** evaluation factor for display purposes */
+   uint32     evafactor   :16;
+   /** length of IDN element(s) */
+   uint32     length      :2;
+   /** IDN is list */
+   uint32     list        :1;
+   /** IDN is command */
+   uint32     command     :1;
+   /** datatype */
+   uint32     datatype    :3;
+   uint32     reserved1   :1;
+   /** decimals to display if float datatype */
+   uint32     decimals    :4;
+   /** write protected in pre-op */
+   uint32     wppreop     :1;
+   /** write protected in safe-op */
+   uint32     wpsafeop    :1;
+   /** write protected in op */
+   uint32     wpop        :1;
+   uint32     reserved2   :1;
+} ec_SoEattributet;
+PACKED_END
+
+#ifdef EC_VER1
+int ec_SoEread(uint16 slave, uint8 driveNo, uint8 elementflags, uint16 idn, int *psize, void *p, int timeout);
+int ec_SoEwrite(uint16 slave, uint8 driveNo, uint8 elementflags, uint16 idn, int psize, void *p, int timeout);
+int ec_readIDNmap(uint16 slave, int *Osize, int *Isize);
+#endif
+
+int ecx_SoEread(ecx_contextt *context, uint16 slave, uint8 driveNo, uint8 elementflags, uint16 idn, int *psize, void *p, int timeout);
+int ecx_SoEwrite(ecx_contextt *context, uint16 slave, uint8 driveNo, uint8 elementflags, uint16 idn, int psize, void *p, int timeout);
+int ecx_readIDNmap(ecx_contextt *context, uint16 slave, int *Osize, int *Isize);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
\ No newline at end of file
diff -r 000000000000 -r 543d6784d4cc SOEM/ethercattype.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SOEM/ethercattype.h	Tue Jun 11 10:29:09 2019 +0000
@@ -0,0 +1,565 @@
+/*
+ * Licensed under the GNU General Public License version 2 with exceptions. See
+ * LICENSE file in the project root for full license information
+ */
+
+/** \file
+ * \brief
+ * General typedefs and defines for EtherCAT.
+ *
+ * Defines that could need optimisation for specific applications
+ * are the EC_TIMEOUTxxx. Assumptions for the standard settings are a
+ * standard linux PC or laptop and a wired connection to maximal 100 slaves.
+ * For use with wireless connections or lots of slaves the timeouts need
+ * increasing. For fast systems running Xenomai and RT-net or alike the
+ * timeouts need to be shorter.
+ */
+
+#ifndef _EC_TYPE_H
+#define _EC_TYPE_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/** Define Little or Big endian target */
+#define EC_LITTLE_ENDIAN
+
+/** define EC_VER1 if version 1 default context and functions are needed
+ * comment if application uses only ecx_ functions and own context */
+#define EC_VER1
+
+#include "osal.h"
+
+/** return value general error */
+#define EC_ERROR           -3
+/** return value no frame returned */
+#define EC_NOFRAME         -1
+/** return value unknown frame received */
+#define EC_OTHERFRAME      -2
+/** maximum EtherCAT frame length in bytes */
+#define EC_MAXECATFRAME    1518
+/** maximum EtherCAT LRW frame length in bytes */
+/* MTU - Ethernet header - length - datagram header - WCK - FCS */
+#define EC_MAXLRWDATA      (EC_MAXECATFRAME - 14 - 2 - 10 - 2 - 4)
+/** size of DC datagram used in first LRW frame */
+#define EC_FIRSTDCDATAGRAM 20
+/** standard frame buffer size in bytes */
+#define EC_BUFSIZE         EC_MAXECATFRAME
+/** datagram type EtherCAT */
+#define EC_ECATTYPE        0x1000
+/** number of frame buffers per channel (tx, rx1 rx2) */
+#define EC_MAXBUF          16
+/** timeout value in us for tx frame to return to rx */
+//#define EC_TIMEOUTRET      2000
+
+#define EC_TIMEOUTRET     500       //Changed: must be less than the CYCLE_TIME 
+
+/** timeout value in us for safe data transfer, max. triple retry */
+#define EC_TIMEOUTRET3     (EC_TIMEOUTRET * 3)
+/** timeout value in us for return "safe" variant (f.e. wireless) */
+#define EC_TIMEOUTSAFE     20000
+/** timeout value in us for EEPROM access */
+#define EC_TIMEOUTEEP      20000
+/** timeout value in us for tx mailbox cycle */
+#define EC_TIMEOUTTXM      20000
+/** timeout value in us for rx mailbox cycle */
+#define EC_TIMEOUTRXM      700000
+/** timeout value in us for check statechange */
+#define EC_TIMEOUTSTATE    2000000
+/** size of EEPROM bitmap cache */
+#define EC_MAXEEPBITMAP    128
+/** size of EEPROM cache buffer */
+#define EC_MAXEEPBUF       EC_MAXEEPBITMAP << 5
+/** default number of retries if wkc <= 0 */
+#define EC_DEFAULTRETRIES  3
+/** default group size in 2^x */
+#define EC_LOGGROUPOFFSET 16
+
+/** definition for frame buffers */
+typedef uint8 ec_bufT[EC_BUFSIZE];
+
+/** ethernet header definition */
+PACKED_BEGIN
+typedef struct PACKED
+{
+   /** destination MAC */
+   uint16  da0,da1,da2;
+   /** source MAC */
+   uint16  sa0,sa1,sa2;
+   /** ethernet type */
+   uint16  etype;
+} ec_etherheadert;
+PACKED_END
+
+/** ethernet header size */
+#define ETH_HEADERSIZE      sizeof(ec_etherheadert)
+
+/** EtherCAT datagram header definition */
+PACKED_BEGIN
+typedef struct PACKED
+{
+   /** length of EtherCAT datagram */
+   uint16  elength;
+   /** EtherCAT command, see ec_cmdtype */
+   uint8   command;
+   /** index, used in SOEM for Tx to Rx recombination */
+   uint8   index;
+   /** ADP */
+   uint16  ADP;
+   /** ADO */
+   uint16  ADO;
+   /** length of data portion in datagram */
+   uint16  dlength;
+   /** interrupt, currently unused */
+   uint16  irpt;
+} ec_comt;
+PACKED_END
+
+/** EtherCAT header size */
+#define EC_HEADERSIZE       sizeof(ec_comt)
+/** size of ec_comt.elength item in EtherCAT header */
+#define EC_ELENGTHSIZE      sizeof(uint16)
+/** offset position of command in EtherCAT header */
+#define EC_CMDOFFSET        EC_ELENGTHSIZE
+/** size of workcounter item in EtherCAT datagram */
+#define EC_WKCSIZE          sizeof(uint16)
+/** definition of datagram follows bit in ec_comt.dlength */
+#define EC_DATAGRAMFOLLOWS  (1 << 15)
+
+/** Possible error codes returned. */
+typedef enum
+{
+   /** No error */
+   EC_ERR_OK         = 0,
+   /** Library already initialized. */
+   EC_ERR_ALREADY_INITIALIZED,
+   /** Library not initialized. */
+   EC_ERR_NOT_INITIALIZED,
+   /** Timeout occurred during execution of the function. */
+   EC_ERR_TIMEOUT,
+   /** No slaves were found. */
+   EC_ERR_NO_SLAVES,
+   /** Function failed. */
+   EC_ERR_NOK
+} ec_err;
+
+/** Possible EtherCAT slave states */
+typedef enum
+{
+   /** No valid state. */
+   EC_STATE_NONE           = 0x00,
+   /** Init state*/
+   EC_STATE_INIT           = 0x01,
+   /** Pre-operational. */
+   EC_STATE_PRE_OP         = 0x02,
+   /** Boot state*/
+   EC_STATE_BOOT           = 0x03,
+   /** Safe-operational. */
+   EC_STATE_SAFE_OP        = 0x04,
+   /** Operational */
+   EC_STATE_OPERATIONAL    = 0x08,
+   /** Error or ACK error */
+   EC_STATE_ACK            = 0x10,
+   EC_STATE_ERROR          = 0x10
+} ec_state;
+
+/** Possible buffer states */
+typedef enum
+{
+   /** Empty */
+   EC_BUF_EMPTY        = 0x00,
+   /** Allocated, but not filled */
+   EC_BUF_ALLOC        = 0x01,
+   /** Transmitted */
+   EC_BUF_TX           = 0x02,
+   /** Received, but not consumed */
+   EC_BUF_RCVD         = 0x03,
+   /** Cycle completed */
+   EC_BUF_COMPLETE     = 0x04
+} ec_bufstate;
+
+/** Ethercat data types */
+typedef enum
+{
+   ECT_BOOLEAN         = 0x0001,
+   ECT_INTEGER8        = 0x0002,
+   ECT_INTEGER16       = 0x0003,
+   ECT_INTEGER32       = 0x0004,
+   ECT_UNSIGNED8       = 0x0005,
+   ECT_UNSIGNED16      = 0x0006,
+   ECT_UNSIGNED32      = 0x0007,
+   ECT_REAL32          = 0x0008,
+   ECT_VISIBLE_STRING  = 0x0009,
+   ECT_OCTET_STRING    = 0x000A,
+   ECT_UNICODE_STRING  = 0x000B,
+   ECT_TIME_OF_DAY     = 0x000C,
+   ECT_TIME_DIFFERENCE = 0x000D,
+   ECT_DOMAIN          = 0x000F,
+   ECT_INTEGER24       = 0x0010,
+   ECT_REAL64          = 0x0011,
+   ECT_INTEGER64       = 0x0015,
+   ECT_UNSIGNED24      = 0x0016,
+   ECT_UNSIGNED64      = 0x001B,
+   ECT_BIT1            = 0x0030,
+   ECT_BIT2            = 0x0031,
+   ECT_BIT3            = 0x0032,
+   ECT_BIT4            = 0x0033,
+   ECT_BIT5            = 0x0034,
+   ECT_BIT6            = 0x0035,
+   ECT_BIT7            = 0x0036,
+   ECT_BIT8            = 0x0037
+} ec_datatype;
+
+/** Ethercat command types */
+typedef enum
+{
+   /** No operation */
+   EC_CMD_NOP          = 0x00,
+   /** Auto Increment Read */
+   EC_CMD_APRD,
+   /** Auto Increment Write */
+   EC_CMD_APWR,
+   /** Auto Increment Read Write */
+   EC_CMD_APRW,
+   /** Configured Address Read */
+   EC_CMD_FPRD,
+   /** Configured Address Write */
+   EC_CMD_FPWR,
+   /** Configured Address Read Write */
+   EC_CMD_FPRW,
+   /** Broadcast Read */
+   EC_CMD_BRD,
+   /** Broadcast Write */
+   EC_CMD_BWR,
+   /** Broadcast Read Write */
+   EC_CMD_BRW,
+   /** Logical Memory Read */
+   EC_CMD_LRD,
+   /** Logical Memory Write */
+   EC_CMD_LWR,
+   /** Logical Memory Read Write */
+   EC_CMD_LRW,
+   /** Auto Increment Read Multiple Write */
+   EC_CMD_ARMW,
+   /** Configured Read Multiple Write */
+   EC_CMD_FRMW
+   /** Reserved */
+} ec_cmdtype;
+
+/** Ethercat EEprom command types */
+typedef enum
+{
+   /** No operation */
+   EC_ECMD_NOP         = 0x0000,
+   /** Read */
+   EC_ECMD_READ        = 0x0100,
+   /** Write */
+   EC_ECMD_WRITE       = 0x0201,
+   /** Reload */
+   EC_ECMD_RELOAD      = 0x0300
+} ec_ecmdtype;
+
+/** EEprom state machine read size */
+#define EC_ESTAT_R64    0x0040
+/** EEprom state machine busy flag */
+#define EC_ESTAT_BUSY   0x8000
+/** EEprom state machine error flag mask */
+#define EC_ESTAT_EMASK  0x7800
+/** EEprom state machine error acknowledge */
+#define EC_ESTAT_NACK   0x2000
+
+/* Ethercat SSI (Slave Information Interface) */
+
+/** Start address SII sections in Eeprom */
+#define ECT_SII_START   0x0040
+
+enum
+{
+   /** SII category strings */
+   ECT_SII_STRING      = 10,
+   /** SII category general */
+   ECT_SII_GENERAL     = 30,
+   /** SII category FMMU */
+   ECT_SII_FMMU        = 40,
+   /** SII category SM */
+   ECT_SII_SM          = 41,
+   /** SII category PDO */
+   ECT_SII_PDO         = 50
+};
+
+/** Item offsets in SII general section */
+enum
+{
+   ECT_SII_MANUF       = 0x0008,
+   ECT_SII_ID          = 0x000a,
+   ECT_SII_REV         = 0x000c,
+   ECT_SII_BOOTRXMBX   = 0x0014,
+   ECT_SII_BOOTTXMBX   = 0x0016,
+   ECT_SII_MBXSIZE     = 0x0019,
+   ECT_SII_TXMBXADR    = 0x001a,
+   ECT_SII_RXMBXADR    = 0x0018,
+   ECT_SII_MBXPROTO    = 0x001c
+};
+
+/** Mailbox types definitions */
+enum
+{
+   /** Error mailbox type */
+   ECT_MBXT_ERR        = 0x00,
+   /** ADS over EtherCAT mailbox type */
+   ECT_MBXT_AOE,
+   /** Ethernet over EtherCAT mailbox type */
+   ECT_MBXT_EOE,
+   /** CANopen over EtherCAT mailbox type */
+   ECT_MBXT_COE,
+   /** File over EtherCAT mailbox type */
+   ECT_MBXT_FOE,
+   /** Servo over EtherCAT mailbox type */
+   ECT_MBXT_SOE,
+   /** Vendor over EtherCAT mailbox type */
+   ECT_MBXT_VOE        = 0x0f
+};
+
+/** CoE mailbox types */
+enum
+{
+   ECT_COES_EMERGENCY  = 0x01,
+   ECT_COES_SDOREQ,
+   ECT_COES_SDORES,
+   ECT_COES_TXPDO,
+   ECT_COES_RXPDO,
+   ECT_COES_TXPDO_RR,
+   ECT_COES_RXPDO_RR,
+   ECT_COES_SDOINFO
+};
+
+/** CoE SDO commands */
+enum
+{
+   ECT_SDO_DOWN_INIT    = 0x21,
+   ECT_SDO_DOWN_EXP     = 0x23,
+   ECT_SDO_DOWN_INIT_CA = 0x31,
+   ECT_SDO_UP_REQ       = 0x40,
+   ECT_SDO_UP_REQ_CA    = 0x50,
+   ECT_SDO_SEG_UP_REQ   = 0x60,
+   ECT_SDO_ABORT        = 0x80
+};
+
+/** CoE Object Description commands */
+enum
+{
+   ECT_GET_ODLIST_REQ  = 0x01,
+   ECT_GET_ODLIST_RES  = 0x02,
+   ECT_GET_OD_REQ      = 0x03,
+   ECT_GET_OD_RES      = 0x04,
+   ECT_GET_OE_REQ      = 0x05,
+   ECT_GET_OE_RES      = 0x06,
+   ECT_SDOINFO_ERROR   = 0x07
+};
+
+/** FoE opcodes */
+enum
+{
+   ECT_FOE_READ        = 0x01,
+   ECT_FOE_WRITE,
+   ECT_FOE_DATA,
+   ECT_FOE_ACK,
+   ECT_FOE_ERROR,
+   ECT_FOE_BUSY
+};
+
+/** SoE opcodes */
+enum
+{
+   ECT_SOE_READREQ     = 0x01,
+   ECT_SOE_READRES,
+   ECT_SOE_WRITEREQ,
+   ECT_SOE_WRITERES,
+   ECT_SOE_NOTIFICATION,
+   ECT_SOE_EMERGENCY
+};
+
+/** Ethercat registers */
+enum
+{
+   ECT_REG_TYPE        = 0x0000,
+   ECT_REG_PORTDES     = 0x0007,
+   ECT_REG_ESCSUP      = 0x0008,
+   ECT_REG_STADR       = 0x0010,
+   ECT_REG_ALIAS       = 0x0012,
+   ECT_REG_DLCTL       = 0x0100,
+   ECT_REG_DLPORT      = 0x0101,
+   ECT_REG_DLALIAS     = 0x0103,
+   ECT_REG_DLSTAT      = 0x0110,
+   ECT_REG_ALCTL       = 0x0120,
+   ECT_REG_ALSTAT      = 0x0130,
+   ECT_REG_ALSTATCODE  = 0x0134,
+   ECT_REG_PDICTL      = 0x0140,
+   ECT_REG_IRQMASK     = 0x0200,
+   ECT_REG_RXERR       = 0x0300,
+   ECT_REG_FRXERR      = 0x0308,
+   ECT_REG_EPUECNT     = 0x030C,
+   ECT_REG_PECNT       = 0x030D,
+   ECT_REG_PECODE      = 0x030E,
+   ECT_REG_LLCNT       = 0x0310,
+   ECT_REG_WDCNT       = 0x0442,
+   ECT_REG_EEPCFG      = 0x0500,
+   ECT_REG_EEPCTL      = 0x0502,
+   ECT_REG_EEPSTAT     = 0x0502,
+   ECT_REG_EEPADR      = 0x0504,
+   ECT_REG_EEPDAT      = 0x0508,
+   ECT_REG_FMMU0       = 0x0600,
+   ECT_REG_FMMU1       = ECT_REG_FMMU0 + 0x10,
+   ECT_REG_FMMU2       = ECT_REG_FMMU1 + 0x10,
+   ECT_REG_FMMU3       = ECT_REG_FMMU2 + 0x10,
+   ECT_REG_SM0         = 0x0800,
+   ECT_REG_SM1         = ECT_REG_SM0 + 0x08,
+   ECT_REG_SM2         = ECT_REG_SM1 + 0x08,
+   ECT_REG_SM3         = ECT_REG_SM2 + 0x08,
+   ECT_REG_SM0STAT     = ECT_REG_SM0 + 0x05,
+   ECT_REG_SM1STAT     = ECT_REG_SM1 + 0x05,
+   ECT_REG_SM1ACT      = ECT_REG_SM1 + 0x06,
+   ECT_REG_SM1CONTR    = ECT_REG_SM1 + 0x07,
+   ECT_REG_DCTIME0     = 0x0900,
+   ECT_REG_DCTIME1     = 0x0904,
+   ECT_REG_DCTIME2     = 0x0908,
+   ECT_REG_DCTIME3     = 0x090C,
+   ECT_REG_DCSYSTIME   = 0x0910,
+   ECT_REG_DCSOF       = 0x0918,
+   ECT_REG_DCSYSOFFSET = 0x0920,
+   ECT_REG_DCSYSDELAY  = 0x0928,
+   ECT_REG_DCSYSDIFF   = 0x092C,
+   ECT_REG_DCSPEEDCNT  = 0x0930,
+   ECT_REG_DCTIMEFILT  = 0x0934,
+   ECT_REG_DCCUC       = 0x0980,
+   ECT_REG_DCSYNCACT   = 0x0981,
+   ECT_REG_DCSTART0    = 0x0990,
+   ECT_REG_DCCYCLE0    = 0x09A0,
+   ECT_REG_DCCYCLE1    = 0x09A4
+};
+
+/** standard SDO Sync Manager Communication Type */
+#define ECT_SDO_SMCOMMTYPE      0x1c00
+/** standard SDO PDO assignment */
+#define ECT_SDO_PDOASSIGN       0x1c10
+/** standard SDO RxPDO assignment */
+#define ECT_SDO_RXPDOASSIGN     0x1c12
+/** standard SDO TxPDO assignment */
+#define ECT_SDO_TXPDOASSIGN     0x1c13
+
+/** Ethercat packet type */
+#define ETH_P_ECAT              0x88A4
+
+/** Error types */
+typedef enum
+{
+   EC_ERR_TYPE_SDO_ERROR            = 0,
+   EC_ERR_TYPE_EMERGENCY            = 1,
+   EC_ERR_TYPE_PACKET_ERROR         = 3,
+   EC_ERR_TYPE_SDOINFO_ERROR        = 4,
+   EC_ERR_TYPE_FOE_ERROR            = 5,
+   EC_ERR_TYPE_FOE_BUF2SMALL        = 6,
+   EC_ERR_TYPE_FOE_PACKETNUMBER     = 7,
+   EC_ERR_TYPE_SOE_ERROR            = 8,
+   EC_ERR_TYPE_MBX_ERROR            = 9,
+   EC_ERR_TYPE_FOE_FILE_NOTFOUND    = 10,
+   EC_ERR_TYPE_EOE_INVALID_RX_DATA  = 11
+} ec_err_type;
+
+/** Struct to retrieve errors. */
+typedef struct
+{
+   /** Time at which the error was generated. */
+   ec_timet Time;
+   /** Signal bit, error set but not read */
+   boolean     Signal;
+   /** Slave number that generated the error */
+   uint16      Slave;
+   /** CoE SDO index that generated the error */
+   uint16      Index;
+   /** CoE SDO subindex that generated the error */
+   uint8       SubIdx;
+   /** Type of error */
+   ec_err_type Etype;
+   union
+   {
+      /** General abortcode */
+      int32   AbortCode;
+      /** Specific error for Emergency mailbox */
+      struct
+      {
+         uint16  ErrorCode;
+         uint8   ErrorReg;
+         uint8   b1;
+         uint16  w1;
+         uint16  w2;
+      };
+   };
+} ec_errort;
+
+/** Helper macros */
+/** Macro to make a word from 2 bytes */
+#define MK_WORD(msb, lsb)   ((((uint16)(msb))<<8) | (lsb))
+/** Macro to get hi byte of a word */
+#define HI_BYTE(w)          ((w) >> 8)
+/** Macro to get low byte of a word */
+#define LO_BYTE(w)          ((w) & 0x00ff)
+/** Macro to swap hi and low byte of a word */
+#define SWAP(w)             ((((w)& 0xff00) >> 8) | (((w) & 0x00ff) << 8))
+/** Macro to get hi word of a dword */
+#define LO_WORD(l)          ((l) & 0xffff)
+/** Macro to get hi word of a dword */
+#define HI_WORD(l)          ((l) >> 16)
+
+#define get_unaligned(ptr) \
+  ({ __typeof__(*(ptr)) __tmp; memcpy(&__tmp, (ptr), sizeof(*(ptr))); __tmp; })
+
+#define put_unaligned32(val, ptr)        \
+  (memcpy((ptr), &(val), 4))
+
+#define put_unaligned64(val, ptr)        \
+  (memcpy((ptr), &(val), 8))
+
+#if !defined(EC_BIG_ENDIAN) && defined(EC_LITTLE_ENDIAN)
+
+  #define htoes(A) (A)
+  #define htoel(A) (A)
+  #define htoell(A) (A)
+  #define etohs(A) (A)
+  #define etohl(A) (A)
+  #define etohll(A) (A)
+
+#elif !defined(EC_LITTLE_ENDIAN) && defined(EC_BIG_ENDIAN)
+
+  #define htoes(A) ((((uint16)(A) & 0xff00) >> 8) | \
+                    (((uint16)(A) & 0x00ff) << 8))
+  #define htoel(A) ((((uint32)(A) & 0xff000000) >> 24) | \
+                    (((uint32)(A) & 0x00ff0000) >> 8)  | \
+                    (((uint32)(A) & 0x0000ff00) << 8)  | \
+                    (((uint32)(A) & 0x000000ff) << 24))
+  #define htoell(A) ((((uint64)(A) & (uint64)0xff00000000000000ULL) >> 56) | \
+                     (((uint64)(A) & (uint64)0x00ff000000000000ULL) >> 40) | \
+                     (((uint64)(A) & (uint64)0x0000ff0000000000ULL) >> 24) | \
+                     (((uint64)(A) & (uint64)0x000000ff00000000ULL) >> 8)  | \
+                     (((uint64)(A) & (uint64)0x00000000ff000000ULL) << 8)  | \
+                     (((uint64)(A) & (uint64)0x0000000000ff0000ULL) << 24) | \
+                     (((uint64)(A) & (uint64)0x000000000000ff00ULL) << 40) | \
+                     (((uint64)(A) & (uint64)0x00000000000000ffULL) << 56))
+
+  #define etohs  htoes
+  #define etohl  htoel
+  #define etohll htoell
+
+#else
+
+  #error "Must define one of EC_BIG_ENDIAN or EC_LITTLE_ENDIAN"
+
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _EC_TYPE_H */
diff -r 000000000000 -r 543d6784d4cc osal/osal.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/osal/osal.cpp	Tue Jun 11 10:29:09 2019 +0000
@@ -0,0 +1,117 @@
+/*
+ * Licensed under the GNU General Public License version 2 with exceptions. See
+ * LICENSE file in the project root for full license information
+ */
+
+#include "mbed.h"
+
+#include <osal.h>
+#include <config.h>
+
+
+uint64_t tick = 0;
+uint64_t last_tick = 0;
+
+
+#define  timercmp(a, b, CMP)                                \
+  (((a)->tv_sec == (b)->tv_sec) ?                           \
+   ((a)->tv_usec CMP (b)->tv_usec) :                        \
+   ((a)->tv_sec CMP (b)->tv_sec))
+
+
+#define  timeradd(a, b, result)                             \
+  do{                                                       \
+    (result)->tv_sec = (a)->tv_sec + (b)->tv_sec;           \
+    (result)->tv_usec = (a)->tv_usec + (b)->tv_usec;        \
+    if ((result)->tv_usec >= 1000000)                       \
+    {                                                       \
+       ++(result)->tv_sec;                                  \
+       (result)->tv_usec -= 1000000;                        \
+    }                                                       \
+  } while (0)
+#define  timersub(a, b, result)                             \
+  do {                                                      \
+    (result)->tv_sec = (a)->tv_sec - (b)->tv_sec;           \
+    (result)->tv_usec = (a)->tv_usec - (b)->tv_usec;        \
+    if ((result)->tv_usec < 0) {                            \
+      --(result)->tv_sec;                                   \
+      (result)->tv_usec += 1000000;                         \
+    }                                                       \
+  } while (0)
+
+
+#define CFG_TICKS_PER_SECOND 1000000            
+#define USECS_PER_SEC   1000000
+#define USECS_PER_TICK  (USECS_PER_SEC / CFG_TICKS_PER_SECOND)
+
+
+
+int gettimeofday_(struct timeval *tp, void *tzp)
+{
+    //tick_t tick = tick_get();     
+    uint32_t actual_tick = us_ticker_read();
+    tick += (actual_tick - last_tick);
+    last_tick = actual_tick;
+    
+    //tick_t ticks_left;           
+    uint64_t ticks_left;
+  
+    tp->tv_sec = tick / CFG_TICKS_PER_SECOND;
+    ticks_left = tick % CFG_TICKS_PER_SECOND;
+    tp->tv_usec = ticks_left * USECS_PER_TICK;
+
+    return 0;
+}
+
+int osal_usleep (uint32 usec)
+{
+    wait_us(usec); 
+    return 0;
+}
+
+
+int osal_gettimeofday(struct timeval *tv, struct timezone *tz)
+{
+    return gettimeofday_(tv, tz);
+}
+
+
+ec_timet osal_current_time (void)
+{
+    struct timeval current_time;
+    ec_timet return_value;
+
+    gettimeofday_ (&current_time, 0);
+    return_value.sec = current_time.tv_sec;
+    return_value.usec = current_time.tv_usec;
+    return return_value;
+}
+
+void osal_timer_start (osal_timert * self, uint32 timeout_usec)
+{
+    struct timeval start_time;
+    struct timeval timeout;
+    struct timeval stop_time;
+
+    gettimeofday_ (&start_time, 0);
+    timeout.tv_sec = timeout_usec / USECS_PER_SEC;
+    timeout.tv_usec = timeout_usec % USECS_PER_SEC;
+    timeradd (&start_time, &timeout, &stop_time);
+
+    self->stop_time.sec = stop_time.tv_sec;
+    self->stop_time.usec = stop_time.tv_usec;
+}
+
+boolean osal_timer_is_expired (osal_timert * self)
+{
+    struct timeval current_time;
+    struct timeval stop_time;
+    int is_not_yet_expired;
+
+    gettimeofday_ (&current_time, 0);
+    stop_time.tv_sec = self->stop_time.sec;
+    stop_time.tv_usec = self->stop_time.usec;
+    is_not_yet_expired = timercmp (&current_time, &stop_time, <);
+
+    return is_not_yet_expired == false;
+}
diff -r 000000000000 -r 543d6784d4cc osal/osal.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/osal/osal.h	Tue Jun 11 10:29:09 2019 +0000
@@ -0,0 +1,57 @@
+/*
+ * Licensed under the GNU General Public License version 2 with exceptions. See
+ * LICENSE file in the project root for full license information
+ */
+
+#ifndef _osal_
+#define _osal_
+
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include "osal_defs.h"
+#include <stdint.h>
+
+/* General types */
+#define TRUE                1
+#define FALSE               0
+
+#define boolean uint8_t
+
+#define uint8 uint8_t
+#define uint16 uint16_t
+#define uint32 uint32_t
+#define uint64 uint64_t
+#define int8 int8_t
+#define int16 int16_t
+#define int32 int32_t
+#define int64 int64_t
+
+
+
+typedef struct
+{
+    uint32 sec;     //< Seconds elapsed since the Epoch (Jan 1, 1970) 
+    uint32 usec;    //< Microseconds elapsed since last second boundary 
+} ec_timet;
+
+
+typedef struct osal_timer
+{
+    ec_timet stop_time;
+} osal_timert;
+
+void osal_timer_start(osal_timert * self, uint32 timeout_us);
+boolean osal_timer_is_expired(osal_timert * self);
+int osal_usleep(uint32 usec);
+ec_timet osal_current_time(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
diff -r 000000000000 -r 543d6784d4cc osal/osal_defs.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/osal/osal_defs.h	Tue Jun 11 10:29:09 2019 +0000
@@ -0,0 +1,37 @@
+/*
+ * Licensed under the GNU General Public License version 2 with exceptions. See
+ * LICENSE file in the project root for full license information
+ */
+
+#ifndef _osal_defs_
+#define _osal_defs_
+
+
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+
+// define if debug printf is needed
+//#define EC_DEBUG
+
+#ifdef EC_DEBUG
+#define EC_PRINT printf
+#else
+#define EC_PRINT(...) do {} while (0)
+#endif
+
+//#ifndef PACKED                  
+#define PACKED_BEGIN
+#define PACKED  __attribute__((__packed__))
+#define PACKED_END
+//#endif                        
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff -r 000000000000 -r 543d6784d4cc oshw/nicdrv.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/oshw/nicdrv.cpp	Tue Jun 11 10:29:09 2019 +0000
@@ -0,0 +1,782 @@
+/*
+ * Licensed under the GNU General Public License version 2 with exceptions. See
+ * LICENSE file in the project root for full license information
+ */
+
+/** \file
+ * \brief
+ * EtherCAT RAW socket driver.
+ *
+ * Low level interface functions to send and receive EtherCAT packets.
+ * EtherCAT has the property that packets are only send by the master,
+ * and the send packets always return in the receive buffer.
+ * There can be multiple packets "on the wire" before they return.
+ * To combine the received packets with the original send packets a buffer
+ * system is installed. The identifier is put in the index item of the
+ * EtherCAT header. The index is stored and compared when a frame is received.
+ * If there is a match the packet can be combined with the transmit packet
+ * and returned to the higher level function.
+ *
+ * The socket layer can exhibit a reversal in the packet order (rare).
+ * If the Tx order is A-B-C the return order could be A-C-B. The indexed buffer
+ * will reorder the packets automatically.
+ *
+ * The "redundant" option will configure two sockets and two NIC interfaces.
+ * Slaves are connected to both interfaces, one on the IN port and one on the
+ * OUT port. Packets are send via both interfaces. Any one of the connections
+ * (also an interconnect) can be removed and the slaves are still serviced with
+ * packets. The software layer will detect the possible failure modes and
+ * compensate. If needed the packets from interface A are resent through interface B.
+ * This layer is fully transparent for the higher layers.
+ */
+
+
+#include "mbed.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#include "osal.h"
+#include "oshw.h"
+
+#ifndef MAX
+#define MAX(a,b) (((a) > (b)) ? (a) : (b))
+#define MIN(a,b) (((a) < (b)) ? (a) : (b))
+#endif
+
+
+
+/** Redundancy modes */
+enum
+{
+   /** No redundancy, single NIC mode */
+   ECT_RED_NONE,
+   /** Double redundant NIC connection */
+   ECT_RED_DOUBLE
+};
+
+/** Primary source MAC address used for EtherCAT.
+ * This address is not the MAC address used from the NIC.
+ * EtherCAT does not care about MAC addressing, but it is used here to
+ * differentiate the route the packet traverses through the EtherCAT
+ * segment. This is needed to find out the packet flow in redundant
+ * configurations. */
+const uint16 priMAC[3] = { 0x0101, 0x0101, 0x0101 };
+/** Secondary source MAC address used for EtherCAT. */
+const uint16 secMAC[3] = { 0x0404, 0x0404, 0x0404 };
+
+/** second MAC word is used for identification */
+#define RX_PRIM priMAC[1]
+/** second MAC word is used for identification */
+#define RX_SEC secMAC[1]
+
+
+
+
+//******************************************************************************
+// driver used:
+// \mbed-os-5.5.7\targets\TARGET_STM\TARGET_STM32F7\device\stm32f7xx_hal_eth.c
+//******************************************************************************
+
+
+GPIO_InitTypeDef GPIO_InitStructure;
+
+
+
+ETH_HandleTypeDef  hEth;           // ethernet interface handle structure
+
+ETH_DMADescTypeDef DMATxDescTab[ETH_TXBUFNB];        // dma tx descriptors 
+ETH_DMADescTypeDef DMARxDescTab[ETH_RXBUFNB];        // dma rx descriptors
+
+uint8_t DmaTxBuff[ETH_TXBUFNB][ETH_MAX_PACKET_SIZE]; // dma tx buffers    
+uint8_t DmaRxBuff[ETH_RXBUFNB][ETH_MAX_PACKET_SIZE]; // dma rx buffers      
+
+
+
+HAL_StatusTypeDef HalStatus;       // response from Hal functions
+
+
+//------------------------------------------------------------------------------
+
+void EthInit()
+{
+                                        // HAL status codes
+                                        //    
+                                        // HAL_OK       = 0x00U,
+                                        // HAL_ERROR    = 0x01U,
+                                        // HAL_BUSY     = 0x02U,
+                                        // HAL_TIMEOUT  = 0x03U
+    
+           
+    uint8_t MACAddr[6] = {0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC};  // mac address    
+
+    hEth.State = HAL_ETH_STATE_RESET;   // don't know if it is useful !!!  
+    
+    hEth.Instance = ETH;                               
+    hEth.Init.AutoNegotiation = ETH_AUTONEGOTIATION_DISABLE;
+    hEth.Init.Speed = ETH_SPEED_100M;
+    hEth.Init.DuplexMode = ETH_MODE_FULLDUPLEX;
+    hEth.Init.PhyAddress = 0x00;
+    hEth.Init.MACAddr = &MACAddr[0];    
+    hEth.Init.RxMode = ETH_RXPOLLING_MODE;
+    hEth.Init.ChecksumMode = ETH_CHECKSUM_BY_HARDWARE;
+    hEth.Init.MediaInterface = ETH_MEDIA_INTERFACE_RMII;
+        
+    HalStatus = HAL_ETH_Init(&hEth);
+    NVIC_DisableIRQ(ETH_IRQn);         // we don't use interrupt
+    if (HalStatus != HAL_OK)
+    {
+        printf ("HAL_ETH_Init err %d\n", HalStatus);    
+    }      
+     
+    HalStatus = HAL_ETH_DMATxDescListInit(&hEth, &DMATxDescTab[0], &DmaTxBuff[0][0], ETH_TXBUFNB);
+    if (HalStatus != HAL_OK)
+    {
+       printf ("HAL_ETH_DMATxDescListInit err %d\n", HalStatus);   
+    }    
+                                                   
+    HalStatus = HAL_ETH_DMARxDescListInit(&hEth, &DMARxDescTab[0], &DmaRxBuff[0][0], ETH_RXBUFNB);
+    if (HalStatus != HAL_OK)
+    {    
+        printf ("HAL_ETH_DMARxDescListInit err %d\n", HalStatus);     
+    }
+    
+    HalStatus = HAL_ETH_Start(&hEth);
+    if (HalStatus != HAL_OK)    
+    {
+        printf ("HAL_ETH_Start err %d\n", HalStatus);  
+    }   
+}   
+
+//------------------------------------------------------------------------------
+
+int EthWrPacket (uint8_t* pBuff, uint16_t Len)
+{
+    uint8_t* pDmaBuff;          
+            
+    if ((hEth.TxDesc->Status & ETH_DMATXDESC_OWN) == (uint32_t)RESET) 
+    
+    {
+        pDmaBuff = (uint8_t*)(hEth.TxDesc->Buffer1Addr);
+        
+        memcpy (pDmaBuff, pBuff, Len);    
+   
+        HalStatus = HAL_ETH_TransmitFrame(&hEth, Len);
+        if (HalStatus != HAL_OK)
+        {
+            printf ("HAL_ETH_TransmitFrame err %d\n", HalStatus);       
+            return NULL;
+        }        
+          
+        return Len;
+    }        
+    
+    else
+    {
+        return NULL;
+    }
+}    
+
+//------------------------------------------------------------------------------
+
+int EthRdPacket(uint8_t* pBuff)
+{
+    int Len;
+    uint8_t* pDmaBuff;
+    
+    HalStatus = HAL_ETH_GetReceivedFrame(&hEth);            // check if a packet has been received
+    
+    if (HalStatus == HAL_OK)                                // packet received
+    {
+        Len = hEth.RxFrameInfos.length;                     // packet lenght        
+        pDmaBuff = (uint8_t*)hEth.RxFrameInfos.buffer;      // DMA buffer pointer
+               
+        memcpy (pBuff, pDmaBuff, Len);                      // read the data 
+    
+                                                            // release the descriptor                                                           
+        hEth.RxFrameInfos.FSRxDesc->Status |= ETH_DMARXDESC_OWN;
+        hEth.RxFrameInfos.SegCount = 0;                     // reset segment count
+                 
+        return Len;                                         // return the number of bytes read
+    }   
+     
+    else
+    {   
+        return NULL;                                        // no packet received
+    }
+}   
+
+//*****************************************************************************
+//              
+//*****************************************************************************
+
+
+
+static void ecx_clear_rxbufstat(int *rxbufstat)
+{
+   int i;
+   for(i = 0; i < EC_MAXBUF; i++)
+   {
+      rxbufstat[i] = EC_BUF_EMPTY;
+   }
+}
+
+/** Basic setup to connect NIC to socket.
+ * @param[in] port        = port context struct
+ * @param[in] ifname      = Name of NIC device, f.e. "eth0"
+ * @param[in] secondary   = if >0 then use secondary stack instead of primary
+ * @return >0 if succeeded
+ */
+int ecx_setupnic(ecx_portt *port, const char *ifname, int secondary)
+{
+   int i;
+   int rVal;
+   int *psock;
+
+    EthInit();     
+        
+   if (rVal != 0)
+      return 0;
+
+   if (secondary)
+   {
+      /* secondary port struct available? */
+      if (port->redport)
+      {
+         /* when using secondary socket it is automatically a redundant setup */
+         psock = &(port->redport->sockhandle);
+         *psock = -1;
+         port->redstate                   = ECT_RED_DOUBLE;
+         port->redport->stack.sock        = &(port->redport->sockhandle);
+         port->redport->stack.txbuf       = &(port->txbuf);
+         port->redport->stack.txbuflength = &(port->txbuflength);
+         port->redport->stack.tempbuf     = &(port->redport->tempinbuf);
+         port->redport->stack.rxbuf       = &(port->redport->rxbuf);
+         port->redport->stack.rxbufstat   = &(port->redport->rxbufstat);
+         port->redport->stack.rxsa        = &(port->redport->rxsa);
+         ecx_clear_rxbufstat(&(port->redport->rxbufstat[0]));
+      }
+      else
+      {
+         /* fail */
+         return 0;
+      }
+   }
+   else
+   {
+      port->sockhandle        = -1;
+      port->lastidx           = 0;
+      port->redstate          = ECT_RED_NONE;
+      port->stack.sock        = &(port->sockhandle);
+      port->stack.txbuf       = &(port->txbuf);
+      port->stack.txbuflength = &(port->txbuflength);
+      port->stack.tempbuf     = &(port->tempinbuf);
+      port->stack.rxbuf       = &(port->rxbuf);
+      port->stack.rxbufstat   = &(port->rxbufstat);
+      port->stack.rxsa        = &(port->rxsa);
+      ecx_clear_rxbufstat(&(port->rxbufstat[0]));
+      psock = &(port->sockhandle);
+   }
+
+   /* setup ethernet headers in tx buffers so we don't have to repeat it */
+   for (i = 0; i < EC_MAXBUF; i++)
+   {
+      ec_setupheader(&(port->txbuf[i]));
+      port->rxbufstat[i] = EC_BUF_EMPTY;
+   }
+   ec_setupheader(&(port->txbuf2));
+
+   return 1;
+}
+
+/** Close sockets used
+ * @param[in] port        = port context struct
+ * @return 0
+ */
+int ecx_closenic(ecx_portt *port)
+{
+   if (port->sockhandle >= 0)
+   {
+      close(port->sockhandle);
+   }
+   if ((port->redport) && (port->redport->sockhandle >= 0))
+   {
+      close(port->redport->sockhandle);
+   }
+   return 0;
+}
+
+/** Fill buffer with ethernet header structure.
+ * Destination MAC is always broadcast.
+ * Ethertype is always ETH_P_ECAT.
+ * @param[out] p = buffer
+ */
+ 
+void ec_setupheader(void *p)  
+{   
+   ec_etherheadert *bp;                   
+     
+   bp = (ec_etherheadert*)p;
+ 
+ 
+   bp->da0 = oshw_htons(0xffff);
+   bp->da1 = oshw_htons(0xffff);
+   bp->da2 = oshw_htons(0xffff);
+   bp->sa0 = oshw_htons(priMAC[0]);
+   bp->sa1 = oshw_htons(priMAC[1]);
+   bp->sa2 = oshw_htons(priMAC[2]);
+   bp->etype = oshw_htons(ETH_P_ECAT);   
+}
+
+/** Get new frame identifier index and allocate corresponding rx buffer.
+ * @param[in] port        = port context struct
+ * @return new index.
+ */
+int ecx_getindex(ecx_portt *port)
+{
+   int idx;
+   int cnt;
+
+ //  mtx_lock (port->getindex_mutex);               //******//
+
+   idx = port->lastidx + 1;
+   /* index can't be larger than buffer array */
+   if (idx >= EC_MAXBUF)
+   {
+      idx = 0;
+   }
+   cnt = 0;
+   /* try to find unused index */
+   while ((port->rxbufstat[idx] != EC_BUF_EMPTY) && (cnt < EC_MAXBUF))
+   {
+      idx++;
+      cnt++;
+      if (idx >= EC_MAXBUF)
+      {
+         idx = 0;
+      }
+   }
+   port->rxbufstat[idx] = EC_BUF_ALLOC;
+   if (port->redstate != ECT_RED_NONE)
+   {
+      port->redport->rxbufstat[idx] = EC_BUF_ALLOC;
+   }
+   port->lastidx = idx;
+
+ //  mtx_unlock (port->getindex_mutex);             //******//
+
+   return idx;
+}
+
+/** Set rx buffer status.
+ * @param[in] port     = port context struct
+ * @param[in] idx      = index in buffer array
+ * @param[in] bufstat  = status to set
+ */
+void ecx_setbufstat(ecx_portt *port, int idx, int bufstat)
+{
+   port->rxbufstat[idx] = bufstat;
+   if (port->redstate != ECT_RED_NONE)
+   {
+      port->redport->rxbufstat[idx] = bufstat;
+   }
+}
+
+/** Transmit buffer over socket (non blocking).
+ * @param[in] port        = port context struct
+ * @param[in] idx         = index in tx buffer array
+ * @param[in] stacknumber  = 0=Primary 1=Secondary stack
+ * @return socket send result
+ */
+int ecx_outframe(ecx_portt *port, int idx, int stacknumber)
+{
+   int lp, rval;
+   ec_stackT *stack;
+
+   if (!stacknumber)
+   {
+      stack = &(port->stack);
+   }
+   else
+   {
+      stack = &(port->redport->stack);
+   }
+   lp = (*stack->txbuflength)[idx];
+   (*stack->rxbufstat)[idx] = EC_BUF_TX;
+ 
+   rval = EthWrPacket ((*stack->txbuf)[idx], lp); 
+ 
+   return rval;
+}
+
+/** Transmit buffer over socket (non blocking).
+ * @param[in] port        = port context struct
+ * @param[in] idx = index in tx buffer array
+ * @return socket send result
+ */
+int ecx_outframe_red(ecx_portt *port, int idx)
+{
+   ec_comt *datagramP;
+   ec_etherheadert *ehp;
+   int rval;
+
+   ehp = (ec_etherheadert *)&(port->txbuf[idx]);
+   /* rewrite MAC source address 1 to primary */
+   ehp->sa1 = oshw_htons(priMAC[1]);
+   /* transmit over primary socket*/
+   rval = ecx_outframe(port, idx, 0);
+   if (port->redstate != ECT_RED_NONE)
+   {
+ //     mtx_lock (port->tx_mutex);                              //******//
+      ehp = (ec_etherheadert *)&(port->txbuf2);
+      /* use dummy frame for secondary socket transmit (BRD) */
+      datagramP = (ec_comt*)&(port->txbuf2[ETH_HEADERSIZE]);
+      /* write index to frame */
+      datagramP->index = idx;
+      /* rewrite MAC source address 1 to secondary */
+      ehp->sa1 = oshw_htons(secMAC[1]);
+      /* transmit over secondary socket */
+      //send(sockhandle2, &ec_txbuf2, ec_txbuflength2 , 0);
+      // OBS! redundant not ACTIVE for BFIN, just added to compile
+ 
+      port->redport->rxbufstat[idx] = EC_BUF_TX;
+      
+      EthWrPacket((uint8_t*)&(port->txbuf2), port->txbuflength2);
+        
+ //     mtx_unlock (port->tx_mutex);                                  //******//      
+   }                                                                    
+
+   return rval;
+}
+
+/** Non blocking read of socket. Put frame in temporary buffer.
+ * @param[in] port        = port context struct
+ * @param[in] stacknumber = 0=primary 1=secondary stack
+ * @return >0 if frame is available and read
+ */
+static int ecx_recvpkt(ecx_portt *port, int stacknumber)
+{
+   int lp, bytesrx;
+   ec_stackT *stack;
+
+   if (!stacknumber)
+   {
+      stack = &(port->stack);
+   }
+   else
+   {
+      stack = &(port->redport->stack);
+   }
+   lp = sizeof(port->tempinbuf);
+ 
+   bytesrx = EthRdPacket(*stack->tempbuf);
+  
+  
+   port->tempinbufs = bytesrx;
+
+   return (bytesrx > 0);
+}
+
+/** Non blocking receive frame function. Uses RX buffer and index to combine
+ * read frame with transmitted frame. To compensate for received frames that
+ * are out-of-order all frames are stored in their respective indexed buffer.
+ * If a frame was placed in the buffer previously, the function retrieves it
+ * from that buffer index without calling ec_recvpkt. If the requested index
+ * is not already in the buffer it calls ec_recvpkt to fetch it. There are
+ * three options now, 1 no frame read, so exit. 2 frame read but other
+ * than requested index, store in buffer and exit. 3 frame read with matching
+ * index, store in buffer, set completed flag in buffer status and exit.
+ *
+ * @param[in] port        = port context struct
+ * @param[in] idx         = requested index of frame
+ * @param[in] stacknumber = 0=primary 1=secondary stack
+ * @return Workcounter if a frame is found with corresponding index, otherwise
+ * EC_NOFRAME or EC_OTHERFRAME.
+ */
+int ecx_inframe(ecx_portt *port, int idx, int stacknumber)
+{
+   uint16  l;
+   int     rval;
+   uint8   idxf;
+   ec_etherheadert *ehp;
+   ec_comt *ecp;
+   ec_stackT *stack;
+   ec_bufT *rxbuf;
+
+   if (!stacknumber)
+   {
+      stack = &(port->stack);
+   }
+   else
+   {
+      stack = &(port->redport->stack);
+   }
+   rval = EC_NOFRAME;
+   rxbuf = &(*stack->rxbuf)[idx];
+   /* check if requested index is already in buffer ? */
+   if ((idx < EC_MAXBUF) && (   (*stack->rxbufstat)[idx] == EC_BUF_RCVD))
+   {
+      l = (*rxbuf)[0] + ((uint16)((*rxbuf)[1] & 0x0f) << 8);
+      /* return WKC */
+      rval = ((*rxbuf)[l] + ((uint16)(*rxbuf)[l + 1] << 8));
+      /* mark as completed */
+      (*stack->rxbufstat)[idx] = EC_BUF_COMPLETE;
+   }
+   else
+   {
+   //   mtx_lock (port->rx_mutex);                                  //******//
+      /* non blocking call to retrieve frame from socket */
+      if (ecx_recvpkt(port, stacknumber))
+      {
+         rval = EC_OTHERFRAME;
+         ehp =(ec_etherheadert*)(stack->tempbuf);
+         /* check if it is an EtherCAT frame */
+         if (ehp->etype == oshw_htons(ETH_P_ECAT))
+         {
+            ecp =(ec_comt*)(&(*stack->tempbuf)[ETH_HEADERSIZE]);
+            l = etohs(ecp->elength) & 0x0fff;
+            idxf = ecp->index;
+            /* found index equals requested index ? */
+            if (idxf == idx)
+            {
+               /* yes, put it in the buffer array (strip ethernet header) */
+               memcpy(rxbuf, &(*stack->tempbuf)[ETH_HEADERSIZE], (*stack->txbuflength)[idx] - ETH_HEADERSIZE);
+               /* return WKC */
+               rval = ((*rxbuf)[l] + ((uint16)((*rxbuf)[l + 1]) << 8));
+               /* mark as completed */
+               (*stack->rxbufstat)[idx] = EC_BUF_COMPLETE;
+               /* store MAC source word 1 for redundant routing info */
+               (*stack->rxsa)[idx] = oshw_ntohs(ehp->sa1);
+            }
+            else
+            {
+               /* check if index exist and someone is waiting for it */
+               if (idxf < EC_MAXBUF && (*stack->rxbufstat)[idxf] == EC_BUF_TX)
+               {
+                  rxbuf = &(*stack->rxbuf)[idxf];
+                  /* put it in the buffer array (strip ethernet header) */
+                  memcpy(rxbuf, &(*stack->tempbuf)[ETH_HEADERSIZE], (*stack->txbuflength)[idxf] - ETH_HEADERSIZE);
+                  /* mark as received */
+                  (*stack->rxbufstat)[idxf] = EC_BUF_RCVD;
+                  (*stack->rxsa)[idxf] = oshw_ntohs(ehp->sa1);
+               }
+               else
+               {
+                  /* strange things happened */
+               }
+            }
+         }
+      }
+   //   mtx_unlock (port->rx_mutex);            //******//
+
+   }
+
+   /* WKC if matching frame found */
+   return rval;
+}
+
+
+/** Blocking redundant receive frame function. If redundant mode is not active then
+ * it skips the secondary stack and redundancy functions. In redundant mode it waits
+ * for both (primary and secondary) frames to come in. The result goes in an decision
+ * tree that decides, depending on the route of the packet and its possible missing arrival,
+ * how to reroute the original packet to get the data in an other try.
+ *
+ * @param[in] port        = port context struct
+ * @param[in] idx = requested index of frame
+ * @param[in] timer = absolute timeout time
+ * @return Workcounter if a frame is found with corresponding index, otherwise
+ * EC_NOFRAME.
+ */
+static int ecx_waitinframe_red(ecx_portt *port, int idx, osal_timert timer)
+{
+   int wkc  = EC_NOFRAME;
+   int wkc2 = EC_NOFRAME;
+   int primrx, secrx;
+
+   /* if not in redundant mode then always assume secondary is OK */
+   if (port->redstate == ECT_RED_NONE)
+   {
+      wkc2 = 0;
+   }
+   do
+   {
+      /* only read frame if not already in */
+      if (wkc <= EC_NOFRAME)
+      {
+         wkc  = ecx_inframe(port, idx, 0);
+      }
+      /* only try secondary if in redundant mode */
+      if (port->redstate != ECT_RED_NONE)
+      {
+         /* only read frame if not already in */
+         if (wkc2 <= EC_NOFRAME)
+            wkc2 = ecx_inframe(port, idx, 1);
+      }
+   /* wait for both frames to arrive or timeout */                       
+    } while (((wkc <= EC_NOFRAME) || (wkc2 <= EC_NOFRAME)) && (osal_timer_is_expired(&timer) == FALSE));
+      
+   /* only do redundant functions when in redundant mode */
+   if (port->redstate != ECT_RED_NONE)
+   {
+      /* primrx if the received MAC source on primary socket */
+      primrx = 0;
+      if (wkc > EC_NOFRAME)
+      {
+         primrx = port->rxsa[idx];
+      }
+      /* secrx if the received MAC source on psecondary socket */
+      secrx = 0;
+      if (wkc2 > EC_NOFRAME)
+      {
+         secrx = port->redport->rxsa[idx];
+      }
+      /* primary socket got secondary frame and secondary socket got primary frame */
+      /* normal situation in redundant mode */
+      if ( ((primrx == RX_SEC) && (secrx == RX_PRIM)) )
+      {
+         /* copy secondary buffer to primary */
+         memcpy(&(port->rxbuf[idx]), &(port->redport->rxbuf[idx]), port->txbuflength[idx] - ETH_HEADERSIZE);
+         wkc = wkc2;
+      }
+      /* primary socket got nothing or primary frame, and secondary socket got secondary frame */
+      /* we need to resend TX packet */
+      if ( ((primrx == 0) && (secrx == RX_SEC)) ||
+           ((primrx == RX_PRIM) && (secrx == RX_SEC)) )
+      {
+         osal_timert read_timer;
+
+         /* If both primary and secondary have partial connection retransmit the primary received
+          * frame over the secondary socket. The result from the secondary received frame is a combined
+          * frame that traversed all slaves in standard order. */
+         if ( (primrx == RX_PRIM) && (secrx == RX_SEC) )
+         {
+            /* copy primary rx to tx buffer */
+            memcpy(&(port->txbuf[idx][ETH_HEADERSIZE]), &(port->rxbuf[idx]), port->txbuflength[idx] - ETH_HEADERSIZE);
+         }
+         
+         //osal_timer_start(&read_timer, EC_TIMEOUTRET);
+  
+                        osal_timer_start(&read_timer, 500);       
+         
+         /* resend secondary tx */
+         ecx_outframe(port, idx, 1);
+         do
+         {
+            /* retrieve frame */
+            wkc2 = ecx_inframe(port, idx, 1);
+         } while ((wkc2 <= EC_NOFRAME) && (osal_timer_is_expired(&read_timer) == FALSE));
+         if (wkc2 > EC_NOFRAME)
+         {
+            /* copy secondary result to primary rx buffer */
+            memcpy(&(port->rxbuf[idx]), &(port->redport->rxbuf[idx]), port->txbuflength[idx] - ETH_HEADERSIZE);
+            wkc = wkc2;
+         }
+      }
+   }
+
+   /* return WKC or EC_NOFRAME */
+   return wkc;
+}
+
+/** Blocking receive frame function. Calls ec_waitinframe_red().
+ * @param[in] port        = port context struct
+ * @param[in] idx       = requested index of frame
+ * @param[in] timeout   = timeout in us
+ * @return Workcounter if a frame is found with corresponding index, otherwise
+ * EC_NOFRAME.
+ */
+int ecx_waitinframe(ecx_portt *port, int idx, int timeout)
+{
+   int wkc;
+   
+   osal_timert timer;
+   osal_timer_start (&timer, timeout);
+   wkc = ecx_waitinframe_red(port, idx, timer);
+
+   return wkc;
+}
+
+/** Blocking send and receive frame function. Used for non processdata frames.
+ * A datagram is build into a frame and transmitted via this function. It waits
+ * for an answer and returns the workcounter. The function retries if time is
+ * left and the result is WKC=0 or no frame received.
+ *
+ * The function calls ec_outframe_red() and ec_waitinframe_red().
+ *
+ * @param[in] port        = port context struct
+ * @param[in] idx      = index of frame
+ * @param[in] timeout  = timeout in us
+ * @return Workcounter or EC_NOFRAME
+ */
+int ecx_srconfirm(ecx_portt *port, int idx, int timeout)
+{
+   int wkc = EC_NOFRAME;
+   
+   osal_timert timer;
+   osal_timer_start(&timer, timeout);   
+   
+   do
+   {
+      osal_timert read_timer;
+
+      /* tx frame on primary and if in redundant mode a dummy on secondary */
+      ecx_outframe_red(port, idx);
+      osal_timer_start(&read_timer, MIN(timeout, EC_TIMEOUTRET)); 
+      
+      /* get frame from primary or if in redundant mode possibly from secondary */
+      wkc = ecx_waitinframe_red(port, idx, read_timer);
+   /* wait for answer with WKC>0 or otherwise retry until timeout */
+   } while ((wkc <= EC_NOFRAME) && (osal_timer_is_expired(&timer) == FALSE));
+
+   return wkc;
+}
+
+
+#ifdef EC_VER1
+int ec_setupnic(const char *ifname, int secondary)
+{
+   return ecx_setupnic(&ecx_port, ifname, secondary);
+}
+
+int ec_closenic(void)
+{
+   return ecx_closenic(&ecx_port);
+}
+
+int ec_getindex(void)
+{
+   return ecx_getindex(&ecx_port);
+}
+
+void ec_setbufstat(int idx, int bufstat)
+{
+   ecx_setbufstat(&ecx_port, idx, bufstat);
+}
+
+int ec_outframe(int idx, int stacknumber)
+{
+   return ecx_outframe(&ecx_port, idx, stacknumber);
+}
+
+int ec_outframe_red(int idx)
+{
+   return ecx_outframe_red(&ecx_port, idx);
+}
+
+int ec_inframe(int idx, int stacknumber)
+{
+   return ecx_inframe(&ecx_port, idx, stacknumber);
+}
+
+int ec_waitinframe(int idx, int timeout)
+{
+   return ecx_waitinframe(&ecx_port, idx, timeout);
+}
+
+int ec_srconfirm(int idx, int timeout)
+{
+   return ecx_srconfirm(&ecx_port, idx, timeout);
+}
+#endif
+
diff -r 000000000000 -r 543d6784d4cc oshw/nicdrv.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/oshw/nicdrv.h	Tue Jun 11 10:29:09 2019 +0000
@@ -0,0 +1,110 @@
+/*
+ * Licensed under the GNU General Public License version 2 with exceptions. See
+ * LICENSE file in the project root for full license information
+ */
+
+/** \file
+ * \brief
+ * Headerfile for nicdrv.c
+ */
+
+#ifndef _nicdrvh_
+#define _nicdrvh_
+
+/** pointer structure to Tx and Rx stacks */
+typedef struct
+{
+   /** socket connection used */
+   int         *sock;
+   /** tx buffer */
+   ec_bufT     (*txbuf)[EC_MAXBUF];
+   /** tx buffer lengths */
+   int         (*txbuflength)[EC_MAXBUF];
+   /** temporary receive buffer */
+   ec_bufT     *tempbuf;
+   /** rx buffers */
+   ec_bufT     (*rxbuf)[EC_MAXBUF];
+   /** rx buffer status fields */
+   int         (*rxbufstat)[EC_MAXBUF];
+   /** received MAC source address (middle word) */
+   int         (*rxsa)[EC_MAXBUF];
+} ec_stackT;
+
+/** pointer structure to buffers for redundant port */
+typedef struct
+{
+   ec_stackT   stack;
+   int         sockhandle;
+   /** rx buffers */
+   ec_bufT rxbuf[EC_MAXBUF];
+   /** rx buffer status */
+   int rxbufstat[EC_MAXBUF];
+   /** rx MAC source address */
+   int rxsa[EC_MAXBUF];
+   /** temporary rx buffer */
+   ec_bufT tempinbuf;
+} ecx_redportt;
+
+/** pointer structure to buffers, vars and mutexes for port instantiation */
+typedef struct
+{
+   ec_stackT   stack;
+   int         sockhandle;
+   /** rx buffers */
+   ec_bufT rxbuf[EC_MAXBUF];
+   /** rx buffer status */
+   int rxbufstat[EC_MAXBUF];
+   /** rx MAC source address */
+   int rxsa[EC_MAXBUF];
+   /** temporary rx buffer */
+   ec_bufT tempinbuf;
+   /** temporary rx buffer status */
+   int tempinbufs;
+   /** transmit buffers */
+   ec_bufT txbuf[EC_MAXBUF];
+   /** transmit buffer lengths */
+   int txbuflength[EC_MAXBUF];
+   /** temporary tx buffer */
+   ec_bufT txbuf2;
+   /** temporary tx buffer length */
+   int txbuflength2;
+   /** last used frame index */
+   int lastidx;
+   /** current redundancy state */
+   int redstate;
+   /** pointer to redundancy port and buffers */
+   ecx_redportt *redport;
+   //mtx_t * getindex_mutex; //******//
+   //mtx_t * tx_mutex; //******//
+   //mtx_t * rx_mutex; //******//
+} ecx_portt;
+
+extern const uint16 priMAC[3];
+extern const uint16 secMAC[3];
+
+#ifdef EC_VER1
+extern ecx_portt     ecx_port;
+extern ecx_redportt  ecx_redport;
+
+int ec_setupnic(const char * ifname, int secondary);
+int ec_closenic(void);
+void ec_setbufstat(int idx, int bufstat);
+int ec_getindex(void);
+int ec_outframe(int idx, int stacknumber);
+int ec_outframe_red(int idx);
+int ec_waitinframe(int idx, int timeout);
+int ec_srconfirm(int idx,int timeout);
+#endif
+
+void ec_setupheader(void *p);
+
+int ecx_setupnic(ecx_portt *port, const char * ifname, int secondary);
+int ecx_closenic(ecx_portt *port);
+void ecx_setbufstat(ecx_portt *port, int idx, int bufstat);
+int ecx_getindex(ecx_portt *port);
+int ecx_outframe(ecx_portt *port, int idx, int stacknumber);
+int ecx_outframe_red(ecx_portt *port, int idx);
+int ecx_waitinframe(ecx_portt *port, int idx, int timeout);
+int ecx_srconfirm(ecx_portt *port, int idx,int timeout);
+
+#endif
diff -r 000000000000 -r 543d6784d4cc oshw/oshw.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/oshw/oshw.cpp	Tue Jun 11 10:29:09 2019 +0000
@@ -0,0 +1,56 @@
+/*
+ * Licensed under the GNU General Public License version 2 with exceptions. See
+ * LICENSE file in the project root for full license information
+ */
+
+#include "oshw.h"
+#include <stdlib.h>
+#include "lwip/def.h"
+
+
+/**
+ * Host to Network byte order (i.e. to big endian).
+ *
+ * Note that Ethercat uses little endian byte order, except for the Ethernet
+ * header which is big endian as usual.
+ */
+ 
+ 
+
+uint16 oshw_htons(const uint16 host)
+{
+	return htons (host);
+}
+
+
+/**
+ * Network (i.e. big endian) to Host byte order.
+ *
+ * Note that Ethercat uses little endian byte order, except for the Ethernet
+ * header which is big endian as usual.
+ */
+uint16 oshw_ntohs(const uint16 network)
+{
+	return ntohs (network);
+}
+
+/* Create list over available network adapters.
+ * @return First element in linked list of adapters
+ */
+ec_adaptert * oshw_find_adapters(void)
+{
+   ec_adaptert * ret_adapter = NULL;
+
+   /* TODO if needed */
+
+   return ret_adapter;
+}
+
+/** Free memory allocated memory used by adapter collection.
+ * @param[in] adapter = First element in linked list of adapters
+ * EC_NOFRAME.
+ */
+void oshw_free_adapters(ec_adaptert * adapter)
+{
+	   /* TODO if needed */
+}
diff -r 000000000000 -r 543d6784d4cc oshw/oshw.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/oshw/oshw.h	Tue Jun 11 10:29:09 2019 +0000
@@ -0,0 +1,34 @@
+/*
+ * Licensed under the GNU General Public License version 2 with exceptions. See
+ * LICENSE file in the project root for full license information
+ */
+
+/** \file
+ * \brief
+ * Headerfile for oshw.c
+ */
+
+#ifndef _oshw_
+#define _oshw_
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include "ethercattype.h"
+#include "nicdrv.h"
+#include "ethercatmain.h"
+
+
+uint16 oshw_htons(uint16 host);
+uint16 oshw_ntohs(uint16 network);
+
+ec_adaptert * oshw_find_adapters(void);
+void oshw_free_adapters(ec_adaptert * adapter);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif