AB&T / Mbed OS EasyCAT_LAB

Dependencies:   SOEM SPI_STMPE610 SPI_TFT_ILI9341 TFT_fonts

Files at this revision

API Documentation at this revision

Comitter:
sulymarco
Date:
Tue Jun 11 10:19:08 2019 +0000
Child:
1:665b62862f15
Commit message:
Added SPI speed parameter

Changed in this revision

CONTRIBUTING.md Show annotated file Show diff for this revision Revisions of this file
SOEM/LICENSE.txt Show annotated file Show diff for this revision Revisions of this file
SOEM/SOEM/ethercat.h Show annotated file Show diff for this revision Revisions of this file
SOEM/SOEM/ethercatbase.c Show annotated file Show diff for this revision Revisions of this file
SOEM/SOEM/ethercatbase.h Show annotated file Show diff for this revision Revisions of this file
SOEM/SOEM/ethercatcoe.c Show annotated file Show diff for this revision Revisions of this file
SOEM/SOEM/ethercatcoe.h Show annotated file Show diff for this revision Revisions of this file
SOEM/SOEM/ethercatconfig.c Show annotated file Show diff for this revision Revisions of this file
SOEM/SOEM/ethercatconfig.h Show annotated file Show diff for this revision Revisions of this file
SOEM/SOEM/ethercatconfiglist.h Show annotated file Show diff for this revision Revisions of this file
SOEM/SOEM/ethercatdc.c Show annotated file Show diff for this revision Revisions of this file
SOEM/SOEM/ethercatdc.h Show annotated file Show diff for this revision Revisions of this file
SOEM/SOEM/ethercateoe.c Show annotated file Show diff for this revision Revisions of this file
SOEM/SOEM/ethercateoe.h Show annotated file Show diff for this revision Revisions of this file
SOEM/SOEM/ethercatfoe.c Show annotated file Show diff for this revision Revisions of this file
SOEM/SOEM/ethercatfoe.h Show annotated file Show diff for this revision Revisions of this file
SOEM/SOEM/ethercatmain.c Show annotated file Show diff for this revision Revisions of this file
SOEM/SOEM/ethercatmain.h Show annotated file Show diff for this revision Revisions of this file
SOEM/SOEM/ethercatprint.c Show annotated file Show diff for this revision Revisions of this file
SOEM/SOEM/ethercatprint.h Show annotated file Show diff for this revision Revisions of this file
SOEM/SOEM/ethercatsoe.c Show annotated file Show diff for this revision Revisions of this file
SOEM/SOEM/ethercatsoe.h Show annotated file Show diff for this revision Revisions of this file
SOEM/SOEM/ethercattype.h Show annotated file Show diff for this revision Revisions of this file
SOEM/osal/osal.cpp Show annotated file Show diff for this revision Revisions of this file
SOEM/osal/osal.h Show annotated file Show diff for this revision Revisions of this file
SOEM/osal/osal_defs.h Show annotated file Show diff for this revision Revisions of this file
SOEM/oshw/nicdrv.cpp Show annotated file Show diff for this revision Revisions of this file
SOEM/oshw/nicdrv.h Show annotated file Show diff for this revision Revisions of this file
SOEM/oshw/oshw.cpp Show annotated file Show diff for this revision Revisions of this file
SOEM/oshw/oshw.h Show annotated file Show diff for this revision Revisions of this file
SPI_TFT_ILI9341.lib Show annotated file Show diff for this revision Revisions of this file
TFT_fonts.lib Show annotated file Show diff for this revision Revisions of this file
config.h Show annotated file Show diff for this revision Revisions of this file
main.cpp Show annotated file Show diff for this revision Revisions of this file
mbed-os.lib Show annotated file Show diff for this revision Revisions of this file
mbed_app.json Show annotated file Show diff for this revision Revisions of this file
note_marco.txt Show annotated file Show diff for this revision Revisions of this file
soem_start.cpp Show annotated file Show diff for this revision Revisions of this file
soem_start.h Show annotated file Show diff for this revision Revisions of this file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/CONTRIBUTING.md	Tue Jun 11 10:19:08 2019 +0000
@@ -0,0 +1,5 @@
+# Contributing to Mbed OS
+
+Mbed OS is an open-source, device software platform for the Internet of Things. Contributions are an important part of the platform, and our goal is to make it as simple as possible to become a contributor.
+
+To encourage productive collaboration, as well as robust, consistent and maintainable code, we have a set of guidelines for [contributing to Mbed OS](https://os.mbed.com/docs/mbed-os/latest/contributing/index.html).
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SOEM/LICENSE.txt	Tue Jun 11 10:19:08 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).
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SOEM/SOEM/ethercat.h	Tue Jun 11 10:19:08 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 */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SOEM/SOEM/ethercatbase.c	Tue Jun 11 10:19:08 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
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SOEM/SOEM/ethercatbase.h	Tue Jun 11 10:19:08 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
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SOEM/SOEM/ethercatcoe.c	Tue Jun 11 10:19:08 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
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SOEM/SOEM/ethercatcoe.h	Tue Jun 11 10:19:08 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
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SOEM/SOEM/ethercatconfig.c	Tue Jun 11 10:19:08 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
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SOEM/SOEM/ethercatconfig.h	Tue Jun 11 10:19:08 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
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SOEM/SOEM/ethercatconfiglist.h	Tue Jun 11 10:19:08 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
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SOEM/SOEM/ethercatdc.c	Tue Jun 11 10:19:08 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
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SOEM/SOEM/ethercatdc.h	Tue Jun 11 10:19:08 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 */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SOEM/SOEM/ethercateoe.c	Tue Jun 11 10:19:08 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;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SOEM/SOEM/ethercateoe.h	Tue Jun 11 10:19:08 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
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SOEM/SOEM/ethercatfoe.c	Tue Jun 11 10:19:08 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
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SOEM/SOEM/ethercatfoe.h	Tue Jun 11 10:19:08 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
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SOEM/SOEM/ethercatmain.c	Tue Jun 11 10:19:08 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
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SOEM/SOEM/ethercatmain.h	Tue Jun 11 10:19:08 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
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SOEM/SOEM/ethercatprint.c	Tue Jun 11 10:19:08 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
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SOEM/SOEM/ethercatprint.h	Tue Jun 11 10:19:08 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
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SOEM/SOEM/ethercatsoe.c	Tue Jun 11 10:19:08 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
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SOEM/SOEM/ethercatsoe.h	Tue Jun 11 10:19:08 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
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SOEM/SOEM/ethercattype.h	Tue Jun 11 10:19:08 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 */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SOEM/osal/osal.cpp	Tue Jun 11 10:19:08 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;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SOEM/osal/osal.h	Tue Jun 11 10:19:08 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
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SOEM/osal/osal_defs.h	Tue Jun 11 10:19:08 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
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SOEM/oshw/nicdrv.cpp	Tue Jun 11 10:19:08 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
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SOEM/oshw/nicdrv.h	Tue Jun 11 10:19:08 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
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SOEM/oshw/oshw.cpp	Tue Jun 11 10:19:08 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 */
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SOEM/oshw/oshw.h	Tue Jun 11 10:19:08 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
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SPI_TFT_ILI9341.lib	Tue Jun 11 10:19:08 2019 +0000
@@ -0,0 +1,1 @@
+https://os.mbed.com/teams/EasyCAT-master/code/SPI_TFT_ILI9341/#2e5c5943b3fd
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/TFT_fonts.lib	Tue Jun 11 10:19:08 2019 +0000
@@ -0,0 +1,1 @@
+https://os.mbed.com/users/dreschpe/code/TFT_fonts/#76774250fcec
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/config.h	Tue Jun 11 10:19:08 2019 +0000
@@ -0,0 +1,144 @@
+#ifndef config_H
+#define config_H
+
+#include "ethercat.h"
+
+
+//------------------------------------------------------------------------------
+
+#define LAB_1               1
+#define LAB_2               2
+
+/*
+#define EK1100              3
+#define EL2004              4
+#define EL1014              5
+*/
+
+#define SLAVE_NUM           2
+//#define SLAVE_NUM           5
+
+//------------------------------------------------------------------------------
+
+
+#define IO_MAP_SIZE 256
+
+
+
+//------------------------------------------------------------------------------
+
+typedef struct __attribute__((__packed__))
+{
+    uint8_t     Alarm;
+}out_LAB_1t;
+
+typedef struct __attribute__((__packed__))
+{
+    float       Temperature;
+}in_LAB_1t;
+
+//------------------------------------------------------------------------------
+
+typedef struct __attribute__((__packed__))
+{
+    uint8_t     Segments;
+}out_LAB_2t;
+
+typedef struct __attribute__((__packed__))
+{
+    uint16_t    Potentiometer;    
+    uint8_t     Buttons;
+}in_LAB_2t;
+
+/*
+//------------------------------------------------------------------------------
+
+typedef struct __attribute__((__packed__))
+{
+    uint8_t       Out_0:1;
+    uint8_t       Out_1:1;
+    uint8_t       Out_2:1;
+    uint8_t       Out_3:1;        
+    uint8_t       PAD:4;
+}out_EL2004t;
+
+//------------------------------------------------------------------------------
+
+typedef struct __attribute__((__packed__))
+{
+    uint8_t       In_0:1;
+    uint8_t       In_1:1;
+    uint8_t       In_2:1;
+    uint8_t       In_3:1;        
+    uint8_t       PAD:4;
+}in_EL1014t;
+
+
+//------------------------------------------------------------------------------
+*/
+
+out_LAB_1t    *out_LAB_1;
+in_LAB_1t     *in_LAB_1;
+
+out_LAB_2t    *out_LAB_2;
+in_LAB_2t     *in_LAB_2;
+
+/*
+out_EL2004t         *out_EL2004;
+
+in_EL1014t          *in_EL1014;
+*/
+
+//------------------------------------------------------------------------------
+
+char IOmap[IO_MAP_SIZE];
+char IOmapSafe[IO_MAP_SIZE];   
+
+
+void MapLocalStructures (void)
+{
+    out_LAB_1 = (out_LAB_1t*)((char *)ec_slave[LAB_1].outputs - &IOmap[0] + &IOmapSafe[0]);
+    in_LAB_1 =  (in_LAB_1t*)((char *)ec_slave[LAB_1].inputs - &IOmap[0] + &IOmapSafe[0]);                
+    
+    out_LAB_2 = (out_LAB_2t*)((char *)ec_slave[LAB_2].outputs - &IOmap[0] + &IOmapSafe[0]);
+    in_LAB_2 =  (in_LAB_2t*)((char *)ec_slave[LAB_2].inputs - &IOmap[0] + &IOmapSafe[0]);                
+    
+/*                    
+    out_EL2004 = (out_EL2004t*)((char *)ec_slave[EL2004].outputs - &IOmap[0] + &IOmapSafe[0]);                  
+
+    in_EL1014 = (in_EL1014t*)((char *)ec_slave[EL1014].inputs - &IOmap[0] + &IOmapSafe[0]);
+*/
+}
+
+
+
+//------------------------------------------------------------------------------
+
+uint32_t network_configuration(void)
+{
+
+   if (ec_slavecount != SLAVE_NUM)                          // check if the number of slaves matches what we expect
+      return 0;                                             
+                                                            
+   if (strcmp(ec_slave[LAB_1].name,"LAB_1"))                // verify slave by slave that the slave names are correct  
+      return 0;
+   
+   else if (strcmp(ec_slave[LAB_2].name,"LAB_2"))
+      return 0;
+      
+/*      
+   else if (strcmp(ec_slave[EK1100].name,"EK1100"))
+      return 0;      
+      
+    else if (strcmp(ec_slave[EL2004].name,"EL2004"))
+      return 0;
+      
+   else if (strcmp(ec_slave[EL1014].name,"EL1014"))
+      return 0;           
+*/  
+      
+  return 1;
+}
+
+
+#endif
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main.cpp	Tue Jun 11 10:19:08 2019 +0000
@@ -0,0 +1,1525 @@
+//********************************************************************************************    
+//                                                                                           *
+// This software is distributed as an example, "AS IS", in the hope that it could            *
+// be useful, WITHOUT ANY WARRANTY of any kind, express or implied, included, but            *
+// not limited,  to the warranties of merchantability, fitness for a particular              *
+// purpose, and non infringiment. In no event shall the authors be liable for any            *    
+// claim, damages or other liability, arising from, or in connection with this software.     *
+//                                                                                           *
+//******************************************************************************************** 
+
+
+// The AB&T EasyCAT LAB is a complete experimental EtherCAT® system, composed by
+// one master and two slaves.
+// The EasyCAT LAB software is provided free of charge and its pourpose is to allow
+// makers and educational institutes to experiment with the EtherCAT® protocol.
+//
+// The EasyCAT LAB is developed by "AB&T Tecnologie Informatiche" Via dell'About 2A Ivrea Italy.
+// www.bausano.net
+// www.easycatshield.com
+//
+// The EasyCAT LAB uses the SOEM library by rt:labs
+// https://rt-labs.com/products/soem-ethercat-master-stack/
+// 
+// EtherCAT® is a registered trademark and patented technology, licensed by Beckhoff Automation GmbH.
+// www.beckhoff.com
+// www.ethercat.org     
+
+
+#define ETH_TXBUFNB 16
+#define ETH_RXBUFNB 16
+
+#include "mbed.h"    
+
+#ifndef __align
+#define __align MBED_ALIGN
+#endif
+
+#include "config.h"  
+#include "SPI_TFT_ILI9341.h"
+#include "Arial12x12.h"
+#include "Arial24x23.h"
+#include "Arial28x28.h"
+#include "font_big.h"
+#include "soem_start.h"
+
+#define CYCLE_TIME 1000                 // master cycle time in uS                    
+                                        // 1000 = 1mS 
+
+                                  
+#define SysMilliS() (uint32_t)Kernel::get_ms_count()                                      
+
+Serial pc(USBTX,USBRX,115200);          // set the debug serial line speed to 115200
+
+
+//---- TFT with resistive touchscreen pins -------------------------------------
+
+// the display used is the SeeedStudio 2.8 inch TFT v2.0
+// http://wiki.seeedstudio.com/2.8inch_TFT_Touch_Shield_v2.0/ 
+
+#define PIN_YP          A3                          // resistive touchscreen
+#define PIN_YM          A1                          //
+#define PIN_XM          A2                          //
+#define PIN_XP          A0                          //
+
+#define PIN_MOSI        D11                         // TFT display SPI
+#define PIN_MISO        D12                         //
+#define PIN_SCLK        D13                         //
+#define PIN_CS_TFT      D5                          //
+#define PIN_DC_TFT      D6                          //
+
+
+//---- touchscreen parameters --------------------------------------------------
+
+#define TOUCH_SAMPLES 8                                                     
+#define TOUCH_WINDOW 0.05
+#define TOUCH_THRESHOLD 0.2
+#define TOUCH_MAX_ROUNDS 16
+#define TIME_TOUCH_RELEASED 300
+
+#define TOUCH_X_OFFSET 0.118
+#define TOUCH_X_GAIN 402
+
+#define TOUCH_Y_OFFSET 0.090
+#define TOUCH_Y_GAIN 302
+
+
+//---- side menu parameters ----------------------------------------------------
+
+#define MENU_Y 0                    
+#define MENU_WIDTH 83               
+#define MENU_HEIGHT 42                  
+#define MENU_X (319-MENU_WIDTH)     
+
+
+//---- slave parameters - LAB_1 EasyCAT with multifunction shield --------------
+
+#define TERMO_X 55                
+#define TERMO_Y 185                
+                                  
+#define ALARM_X 140                
+#define ALARM_Y 0                  
+#define ALARM_WIDTH 83              
+#define ALARM_HEIGHT 42  
+
+#define TIME_BLINK 1000
+#define TIME_AUTO_REP_START 1000
+#define TIME_AUTO_REP_REPEAT 200    
+
+
+//---- slave parameters - LAB_2 EasyCAT with multifunction shield --------------
+
+#define SEG_X 0                     
+#define SEG_Y 0                     
+#define SEG_WIDTH 42                
+#define SEG_HEIGHT 16               
+#define SEG_STEP 60                     
+
+#define BUTTONS_X 38
+#define BUTTONS_Y 80 
+#define BUTTONS_WIDTH 26
+#define BUTTONS_R 3
+#define BUTTONS_STEP 60 
+
+#define ANALOG_X 0
+#define ANALOG_Y 120 
+#define ANALOG_WIDTH 222
+#define ANALOG_HEIGHT 80 
+
+#define TIME_REP_SEG 300
+#define TIME_POTENTIOMETER 100
+
+/*
+//---- slave 3 parameters - EL2004 ---------------------------------------------
+
+#define OUT_X 0                     
+#define OUT_Y 65                     
+#define OUT_WIDTH 42                
+#define OUT_HEIGHT 42               
+#define OUT_STEP 60                     
+
+
+//---- slave 4 parameters - EL1014 ---------------------------------------------
+
+#define INPUTS_X 12
+#define INPUTS_Y 84 
+#define INPUTS_WIDTH 26
+#define INPUTS_R 3
+#define INPUTS_STEP 60 
+*/
+
+
+//---- local functions ---------------------------------------------------------
+
+void DrawBanner();
+void DrawSlaveFixedParts();
+void DrawSideMenu (uint8_t Slave);
+
+void DrawTemperatureValue(float fValue);
+void DrawAlarmSettings(float fThreshold, bool OnOff, bool MinMax);
+void DrawOnlyThreshold(float fThreshold, bool OnOff, bool MinMax);
+void DrawAlarmStatus(bool Alarm);
+void DisplayInRect(int X, int Y, int X_off, int Y_off, char* Value, int BackColor, unsigned char* Font);
+
+void DrawButtonsValue(uint8_t Value);
+void DrawSegmentsValue(uint8_t Value);
+void DrawPotentiometerValue(uint16_t PotValue);
+
+void DrawOutputsValue(uint8_t Value);
+
+void DrawInputsValue(uint8_t Value);
+
+
+
+float ReadAnalog(AnalogIn Ana);
+
+
+void Application();   
+
+void TouchScreenManagement();
+bool TouchRead(uint16_t* X, uint16_t* Y);
+uint16_t TouchRead_X();
+uint16_t TouchRead_Y();
+bool TouchRead_Z();
+
+
+
+//---- global variables --------------------------------------------------------
+
+
+bool TouchWasReleased;
+
+bool FirstRound;
+
+//------------------------------------------------------------------------------
+
+SPI_TFT_ILI9341 TFT(PIN_MOSI, PIN_MISO, PIN_SCLK, PIN_CS_TFT, NC, PIN_DC_TFT);
+
+Ticker SampleTicker;
+Thread thread;
+
+DigitalOut Test_1(D1);          // debug test points
+DigitalOut Test_2(D2);          //
+DigitalOut Test_3(D3);          //
+DigitalOut Test_4(D4);          //
+
+
+//------------------------------------------------------------------------------
+
+float fAlarmThreshold;
+bool AlarmOnOff;
+bool AlarmMinMax;
+
+
+uint16_t PotValue;
+uint16_t PrevPotValue;
+uint8_t Buttons; 
+uint8_t PrevButtons;
+uint8_t Segments;
+uint8_t PrevSegments;
+int16_t Graph_x;
+
+uint8_t Outputs;
+uint8_t PrevOutputs;
+
+uint8_t Inputs;
+uint8_t PrevInputs;
+
+    
+int8_t VisuSlave;
+    
+   
+//------------------------------------------------------------------------------   
+    
+uint32_t Time;    
+uint32_t TimeBlink; 
+uint32_t TimeAutoRepeat;
+uint32_t TimeAutoRepRepeat;
+uint32_t TimePotentiometer;
+uint32_t TimeTouchReleased;
+
+bool Blink;
+
+uint8_t TouchAxes = 0;
+uint8_t Action = 0;
+
+uint16_t X;
+uint16_t Y;
+
+float fTemperature;   
+float static fPrevTemperature;  
+
+int ExpectWorkCounter;
+int WorkCounter; 
+int WorkCounterSafe; 
+bool NetworkError;  
+bool NetworkErrorSafe; 
+
+#define DATA_EXCHANGE_FLAG      (1UL << 0)
+#define APPLICATION_FLAG        (1UL << 1)
+
+EventFlags event_flags;
+
+Mutex IO_data;
+
+
+//---- data exchange thread ----------------------------------------------------
+
+void ExchangeMaster()
+{
+    while (true)
+    {
+                
+        
+        event_flags.wait_any(DATA_EXCHANGE_FLAG);   // the thread waits for the synchronization flag
+    
+        //Test_1 = 1;
+    
+        IO_data.lock();                             // Ethercat data exchange
+        ec_send_processdata();                      // 
+        WorkCounter = ec_receive_processdata(EC_TIMEOUTRET);  
+        
+                            if (WorkCounter != ExpectWorkCounter)
+                                NetworkError = true;
+                            else
+                                NetworkError = false;    
+                            
+        IO_data.unlock();                           //
+        event_flags.set(APPLICATION_FLAG);          // synchronize the application    
+        
+        //Test_1 = 0;                    
+    }
+}
+
+
+//----- thicker generated sample time ------------------------------------------
+
+void SampleIsr()                                    // set the event that starts
+{                                                   // the data exchange
+    event_flags.set(DATA_EXCHANGE_FLAG);            //
+}                                                   //
+    
+    
+//****** initialization ********************************************************
+
+int main()
+{      
+    int i;
+    
+    printf("Start \n");
+    
+    Test_1 = 0; 
+    Test_2 = 0;
+    Test_3 = 0;
+    Test_4 = 0;      
+  
+    TFT.background(Black);                                          // init TFT  
+    TFT.cls();                                                      //
+    TFT.set_orientation(3);                                         // 
+
+    DrawBanner();     
+        
+    NetworkError = false;  
+    VisuSlave = LAB_1; 
+    
+    AlarmOnOff = true;
+    AlarmMinMax = false;
+    fAlarmThreshold = 28.8;
+    fTemperature = 0;
+    TouchWasReleased = true;
+    
+
+    if (ec_init(NULL))                                              // init SOEM
+    {
+        printf("ec_init succeeded.\n");     
+        printf("Scanning the network\n");
+
+        TFT.cls();  
+                                                            
+        TFT.set_font((unsigned char*) Arial12x12);
+        TFT.foreground(Green);
+        TFT.locate(0, 0);
+        
+        TFT.printf("Scanning the network\n");           
+          
+        if (network_scanning())
+        {   
+            if (network_configuration())                            // check network configuration
+            {
+                ec_config_map(&IOmap);                              // map the I/O
+                MapLocalStructures();                 
+
+                printf("\nSlaves mapped, state to SAFE_OP.\n");     
+                                                                    // wait for all slaves to reach SAFE_OP state         
+                ec_statecheck(0, EC_STATE_SAFE_OP,  EC_TIMEOUTSTATE);
+    
+                printf("Request operational state for all slaves\n");
+                ec_slave[0].state = EC_STATE_OPERATIONAL;           
+           
+                ec_send_processdata();                              // send one valid process data to make outputs in slaves happy
+                ExpectWorkCounter = ec_receive_processdata(EC_TIMEOUTRET);          
+               
+                ec_writestate(0);                                   // request OP state for all slaves 
+                
+                                                                    // wait for all slaves to reach OP state 
+                ec_statecheck(0, EC_STATE_OPERATIONAL,  EC_TIMEOUTSTATE);
+                if (ec_slave[0].state == EC_STATE_OPERATIONAL )
+                {
+                    printf("Operational state reached for all slaves.\n");
+                }
+                else
+                {
+                    printf("Not all slaves reached operational state.\n");
+                    ec_readstate();
+                    for(i = 1; i<=ec_slavecount ; i++)
+                    {
+                        if(ec_slave[i].state != EC_STATE_OPERATIONAL)
+                        {
+                            printf("Slave %d State=0x%04x StatusCode=0x%04x\n",
+                                    i, ec_slave[i].state, ec_slave[i].ALstatuscode);
+                        }
+                    }
+                    
+                    TFT.foreground(Red);                                                     
+                    TFT.locate(0, 0);      
+                    TFT.printf("Not all slaves reached operational state!");                           
+                    while(1){}  
+                }   
+                
+                DrawSlaveFixedParts(); 
+                                
+                thread.start(ExchangeMaster);
+                thread.set_priority(osPriorityRealtime); 
+
+                SampleTicker.attach_us(&SampleIsr, CYCLE_TIME);  
+              
+                Application();
+            }
+            
+            else
+            {
+                printf("Mismatch of network units!\n");            
+                TFT.foreground(Red);                                                     
+                TFT.locate(0, 0);      
+                TFT.printf("Mismatch of network units!");
+    
+                while(1){}  
+            }                       
+        }       
+        
+        else
+        {
+            printf("No slaves found!\n");
+            TFT.foreground(Red);                                                          
+            TFT.printf("No slaves found!");             
+        
+            while(1){}  
+        }           
+    }
+    else
+    {
+        printf("Ethernet interface init failed!");
+        TFT.foreground(Red);                                                     
+        TFT.locate(0, 0);      
+        TFT.printf("Ethernet interface init failed!");                
+        while(1){}
+    }    
+}             
+                                 
+              
+//****** user master application  **********************************************
+      
+void Application()      
+{        
+     
+    while(1)
+    {          
+        event_flags.wait_any(APPLICATION_FLAG);                     // the application waits for the synchronization flag
+        
+        //Test_2 = 1;     
+     
+        IO_data.lock();                                             // copy the Ethercat data to a safe buffer
+        memcpy(&IOmapSafe[0], &IOmap[0], IO_MAP_SIZE);              //
+                                                                    //                
+        if (NetworkError)                                           //    
+        {                                                           //
+            NetworkErrorSafe = NetworkError;                        //    
+            WorkCounterSafe = WorkCounter;                          //
+        }                                                           //
+        IO_data.unlock();                                           // 
+              
+        if (NetworkErrorSafe)
+        {           
+            TFT.rect(35,50, 285, 182, Red);    
+            TFT.fillrect(36,51, 284, 181, Black);
+            TFT.foreground(Red);
+            TFT.set_font((unsigned char*) Arial28x28);        
+            TFT.locate(58, 65); 
+            TFT.printf("Network error!");
+            printf("Network error!\n");                  
+            TFT.foreground(Magenta);
+            TFT.set_font((unsigned char*) Arial12x12);        
+            TFT.locate(58, 106);
+            
+            if(WorkCounterSafe >= 0)
+            {
+                TFT.printf("Expected working counter %d", ExpectWorkCounter);                                         
+                TFT.locate(58, 118);                                                   
+                TFT.printf("Actual working counter %d", WorkCounterSafe); 
+                printf("Expected working counter %d\n", ExpectWorkCounter);                                                    
+                printf("Actual working counter %d\n", WorkCounterSafe); 
+            } 
+            else
+            {
+                TFT.printf("Timeout");                 
+                printf("Timeout\n");   
+            }                        
+                
+            TFT.foreground(Green);    
+            TFT.locate(58, 142);                                                   
+            TFT.printf("Please fix the error and");
+            TFT.locate(58, 154);                      
+            TFT.printf("press the reset button");                              
+            printf("Please fix the error and press the reset button \n"); 
+            
+            SampleTicker.detach();                                  // stop the sample interrupt 
+            while(1){}                                              // and loop for ever                           
+        }     
+        
+                                                                    //----- slave 1 data management ------------            
+          
+        fTemperature = in_LAB_1->Temperature;                       // read the temperature 
+     
+        if (fTemperature != fPrevTemperature)                       // check if the temperature has changed
+        {
+            fPrevTemperature = fTemperature;                        // remember the current temperature value
+                        
+            if (VisuSlave == LAB_1)                                 // if the HMI is setted to slave 1
+            {                                                       // visualize it            
+                DrawTemperatureValue (fTemperature);                //                               
+            }                                                        
+        }                                                                           
+    
+        bool AlarmStatus;                                
+        if (AlarmOnOff)                                             // check if we are in alarm 
+        {                                                           //
+            if ((AlarmMinMax && (fTemperature < fAlarmThreshold)) || (!AlarmMinMax && (fTemperature > fAlarmThreshold)))
+            {
+                out_LAB_1->Alarm = 0x01;                            // signal the alarm condition to the slave                                                                        
+                AlarmStatus = true;                                 // and to remember it                     
+            }
+            else
+            {     
+                out_LAB_1->Alarm = 0x00;                            // signal the no alarm condition to the slave
+                AlarmStatus = false;                                // and remember it              
+            }            
+        } 
+        else 
+        {
+            out_LAB_1->Alarm = 0x00;                                // signal the no alarm condition to the slave      
+            AlarmStatus = false;                                    // and remember it                       
+        }   
+                                                                
+        if (VisuSlave == LAB_1)                                     // if the HMI is set to slave 1                       
+        {                                                           // 
+            DrawAlarmStatus(AlarmStatus);                           // update the alarm status on the TFT
+        }  
+    
+        if (VisuSlave == LAB_1 && FirstRound)                       // if the HMI is set to slave 1                                                        
+        {                                                           // and it is the first time 
+            FirstRound = false;                                     //    
+            DrawTemperatureValue (fTemperature);                    // draw the current temperature value 
+            DrawAlarmSettings(fAlarmThreshold, AlarmOnOff, AlarmMinMax); // draw the alarm settings                                   
+        }                                                                        
+    
+                                                                    //----- end LAB_1 ------------------------     
+                                                                                           
+                                                                                                                                  
+                                                                    //----- slave 2 data management ------------  
+                                                                
+        PotValue = in_LAB_2->Potentiometer;                         // read the potentiometer value   
+    
+        if (VisuSlave == LAB_2)                                     // if the HMI is setted to slave 2
+        {                                                           // 
+            Time = SysMilliS();                                     // and the visualization timer is elapsed             
+            if (Time-TimePotentiometer > TIME_POTENTIOMETER)        //    
+            {                                                       // draw the potentiometer value
+                TimePotentiometer = Time;                           //            
+                DrawPotentiometerValue(PotValue);                   //                                                                                               
+            }                                                       //
+        }      
+        
+        Buttons = in_LAB_2->Buttons;                                // read the buttons status from the slave  
+                   
+        if (Buttons != PrevButtons)                                 // check if the buttons value has changed
+        {
+            PrevButtons = Buttons;                                  // remember the current buttons value  
+            
+            if (VisuSlave == LAB_2)                                 // if the HMI is setted to slave 2
+            {                                                       //    
+                DrawButtonsValue(Buttons);                          // draw the current buttons value                                                                                              
+            }                                                       //
+        }
+        
+        if (Segments != PrevSegments)                               // check if the segments value has changed
+        {                                                           
+            PrevSegments = Segments;                                // remember the current segments value 
+            
+            if (VisuSlave == LAB_2)                                 // if the HMI is setted to slave 2
+            {                                                       //    
+                DrawSegmentsValue(Segments);                        // draw the current segments value                                                                                              
+            }                                                       //            
+        }
+               
+        if (VisuSlave == LAB_2 && FirstRound)                       // if the HMI is set to slave 2                                                        
+        {                                                           // and it is the first time 
+            FirstRound = false;                                     //                
+                                                                    //
+            DrawButtonsValue(Buttons);                              // draw the current buttons value         
+            DrawSegmentsValue(Segments);                            // draw the current segments value            
+        }    
+         
+           out_LAB_2->Segments = Segments;                          // send the segments status to the slave 
+    
+                                                            
+                                                                    //----- end LAB_2 ------------------------                                                                        
+                                                                          
+    
+    /*
+                                                                    //----- slave 3 EL2004 data management -----  
+                                                                
+        if (Outputs != PrevOutputs)                                 // check if the outputs value has changed
+        {                                                           
+            PrevOutputs = Outputs;                                  // remember the current outputs value 
+            
+            if (VisuSlave == EL2004)                                // if the HMI is setted to slave 3
+            {                                                       //    
+                DrawOutputsValue(Outputs);                          // draw the current Outputs value                                                                                              
+            }                                                       //            
+        }
+               
+        if (VisuSlave == EL2004 && FirstRound)                      // if the HMI is set to slave 3                                                        
+        {                                                           // and it is the first time 
+            FirstRound = false;                                     //                
+                                                                    //       
+            DrawOutputsValue(Outputs);                              // draw the current Outputs value            
+        }    
+                    
+        if (Outputs & 0x01)                                         // send the Outputs status to the slave        
+            out_EL2004->Out_0 = 1;                                  //   
+        else                                                        //        
+            out_EL2004->Out_0 = 0;                                  //
+        if (Outputs & 0x02)                                         //
+            out_EL2004->Out_1 = 1;                                  //
+        else                                                        //
+            out_EL2004->Out_1 = 0;                                  //
+        if (Outputs & 0x04)                                         //
+            out_EL2004->Out_2 = 1;                                  //
+        else                                                        //
+            out_EL2004->Out_2 = 0;                                  //
+        if (Outputs & 0x08)                                         //
+            out_EL2004->Out_3 = 1;                                  //
+        else                                                        //
+            out_EL2004->Out_3 = 0;                                  //
+        
+                                                                    //----- end EL2004 -----------------------               
+              
+            
+                                                                    //----- slave 4 EL1014 data management -----              
+        Inputs = 0;
+        if (in_EL1014->In_0)                                        // read the inputs status from the slave 
+            Inputs |= 0x01;                                         //                
+        if (in_EL1014->In_1)                                        //      
+            Inputs |= 0x02;                                         //    
+        if (in_EL1014->In_2)                                        //
+            Inputs |= 0x04;                                         //
+        if (in_EL1014->In_3)                                        //
+            Inputs |= 0x08;                                         //     
+                   
+        if (Inputs != PrevInputs)                                   // check if the inputs value has changed
+        {
+            PrevInputs = Inputs;                                    // remember the current inputs value  
+            
+            if (VisuSlave == EL1014)                                // if the HMI is setted to slave 4
+            {                                                       //    
+                DrawInputsValue(Inputs);                            // draw the current inputs value                                                                                              
+            }                                                       //
+        }                                               
+                                                                    //----- end EL1014 -----------------------         
+    */               
+            
+        TouchScreenManagement();                                    // check if the touchscreen is tapped 
+                                                                    // and handle it       
+        
+        Time = SysMilliS();                                         // toggle the variable Blink every
+        if ((Time-TimeBlink) > TIME_BLINK)                          // TIME_BLINK mS
+        {                                                           //
+          TimeBlink = Time;                                         // we use it to blink a field on the TFT
+          Blink = !Blink;                                           //              
+        }                        
+                                
+        IO_data.lock();                                             // copy the IO data from the safe area
+        memcpy(&IOmap[0], &IOmapSafe[0], IO_MAP_SIZE);              // to the EtherCAT buffer
+        IO_data.unlock();                                           //    
+        
+        //Test_2 = 0;                             
+    }       
+}      
+  
+
+//******************************************************************************
+
+
+
+//******* general functions ****************************************************
+
+
+//------ touchscreen management ------------------------------------------------
+
+void TouchScreenManagement()
+{                   
+    uint16_t X;
+    uint16_t Y;
+    
+    int i;
+            
+               
+    if (TouchRead(&X, &Y))                                          // the touchscreen has been tapped
+    {    
+        TimeTouchReleased = SysMilliS();
+                                                                    // check if it is the side menu
+                                                                    // decrement slave button
+        if ((X>MENU_X) && (X>MENU_X+MENU_WIDTH/2) && (Y>MENU_HEIGHT) && (Y<MENU_HEIGHT*2))
+        {                                                           //
+            VisuSlave--;
+            
+            if (VisuSlave == 0)                                     //
+                VisuSlave = SLAVE_NUM;                              //      
+                                                                    // the visualized slave has changed 
+            DrawSlaveFixedParts();                                  // draw the new slave fixed parts   
+        }                                                           //
+                     
+                                                                    // check if it is the side menu
+                                                                    // increment slave button                                                                        
+        if ((X>MENU_X) && (X<MENU_X+MENU_WIDTH/2) && (Y>MENU_HEIGHT) && (Y<MENU_HEIGHT*2))
+        {                                                           //
+            VisuSlave++;
+            
+            if (VisuSlave > SLAVE_NUM)                              //    
+                VisuSlave = LAB_1;                                  //                
+                                                                    // the visualized slave has changed 
+            DrawSlaveFixedParts();                                  // draw the new slave fixed parts     
+        }                                                           //
+        
+        switch (VisuSlave)                                          // check which slave is visualized on the TFT
+        {                      
+            case (LAB_1):     //-------------- slave 1 -----------------------
+            
+            
+                if(TouchWasReleased)                                // first check if the touch was
+                {                                                   // not tapped in the previous rounds  
+                    TouchWasReleased = false;                       // because for the following fields 
+                    TimeTouchReleased = SysMilliS();                // we don't want autorepeat         
+                    
+                    TimeAutoRepeat = SysMilliS();                   // reload the autorepeat time      
+            
+                                                                    // handle taps on the ">" and "<" buttons
+                    if ((X>ALARM_X) && (X<ALARM_X+(ALARM_WIDTH/2)) && (Y>ALARM_Y+ALARM_HEIGHT) && (Y<ALARM_Y+ALARM_HEIGHT*2))       
+                    {                                               //                      
+                        AlarmMinMax = true;                         //
+                        DrawAlarmSettings(fAlarmThreshold, AlarmOnOff, AlarmMinMax);  
+                    }                                               //    
+                    if ((X>ALARM_X+(ALARM_WIDTH/2)) && (X<ALARM_X+(ALARM_WIDTH)) && (Y>ALARM_Y+ALARM_HEIGHT) && (Y<ALARM_Y+ALARM_HEIGHT*2))                                                               //
+                    {                                               // 
+                        AlarmMinMax = false;                        //
+                        DrawAlarmSettings(fAlarmThreshold, AlarmOnOff, AlarmMinMax);  
+                    }                                               
+                    
+                                                                    // handle taps on the "ON" and "OFF" buttons
+                    if ((X>ALARM_X) && (X<ALARM_X+(ALARM_WIDTH/2)) && (Y>ALARM_Y) && (Y<ALARM_Y+ALARM_HEIGHT))       
+                    {                                               //                      
+                        AlarmOnOff = true;                          //
+                        DrawAlarmSettings(fAlarmThreshold, AlarmOnOff, AlarmMinMax);  
+                    }                                               //    
+                    if ((X>ALARM_X+(ALARM_WIDTH/2)) && (X<ALARM_X+(ALARM_WIDTH)) && (Y>ALARM_Y) && (Y<ALARM_Y+ALARM_HEIGHT))                                                              
+                    {                                               // 
+                        AlarmOnOff = false;                         //
+                        DrawAlarmSettings(fAlarmThreshold, AlarmOnOff, AlarmMinMax);  
+                    }            
+                                
+                                                                    // handle taps on the "+" and "-" buttons
+                                                                    // here we don't use the autorepeat to increment
+                                                                    // or decrement the threshold by 0.1    
+                    if ((X>ALARM_X) && (X<ALARM_X+(ALARM_WIDTH/2)) && (Y>ALARM_Y+(ALARM_HEIGHT*3)) && (Y<ALARM_Y+(ALARM_HEIGHT*4)))       
+                    {                                               //                      
+                        fAlarmThreshold += 0.1;                     //
+                        DrawOnlyThreshold(fAlarmThreshold, AlarmOnOff, AlarmMinMax);  
+                    }                                               //    
+                    if ((X>ALARM_X+(ALARM_WIDTH/2)) && (X<ALARM_X+(ALARM_WIDTH)) && (Y>ALARM_Y+(ALARM_HEIGHT*3)) && (Y<ALARM_Y+(ALARM_HEIGHT*4)))                                                         
+                    {                                               // 
+                        fAlarmThreshold -= 0.1;                     //
+                        DrawOnlyThreshold(fAlarmThreshold, AlarmOnOff, AlarmMinMax);  
+                    }                                               //
+                }
+                
+                else                                                // autorepeat management
+                {                                                   //
+                    Time = SysMilliS();                             //            
+                    if (Time-TimeAutoRepeat > TIME_AUTO_REP_START)  //    
+                    {                                               //
+                        if (Time-TimeAutoRepRepeat > TIME_AUTO_REP_REPEAT)
+                        {                                           //
+                            TimeAutoRepRepeat = Time;               //
+                                                                    
+                                                                    // handle taps on the "+" and "-" buttons
+                                                                    // here we use the autorepeat to increment
+                                                                    // or decrement the threshold by 1                                                                       
+                            if ((X>ALARM_X) && (X<ALARM_X+(ALARM_WIDTH/2)) && (Y>ALARM_Y+(ALARM_HEIGHT*3)) && (Y<ALARM_Y+(ALARM_HEIGHT*4)))       
+                            {                                       //                      
+                                fAlarmThreshold += 1;               //
+                                DrawOnlyThreshold(fAlarmThreshold, AlarmOnOff, AlarmMinMax);  
+                            }                                       //    
+                            if ((X>ALARM_X+(ALARM_WIDTH/2)) && (X<ALARM_X+(ALARM_WIDTH)) && (Y>ALARM_Y+(ALARM_HEIGHT*3)) && (Y<ALARM_Y+(ALARM_HEIGHT*4)))                                                         
+                            {                                       // 
+                                fAlarmThreshold -= 1;               //
+                                DrawOnlyThreshold(fAlarmThreshold, AlarmOnOff, AlarmMinMax);  
+                            }   
+                        }       
+                    }
+                }     
+                 
+            break;              //-------------- end slave 1 -------------------
+                
+         
+                
+            case (LAB_2):     //-------------- slave 2 -----------------------
+            
+                if(TouchWasReleased)                                // first check if the touch was
+                {                                                   // not tapped in the previous rounds  
+                    TouchWasReleased = false;                       // because for the following fields
+                    TimeTouchReleased = SysMilliS();                // we don't want autorepeat
+                    
+                    uint8_t Mask = 0x08;                            // check if one of the segment is tapped
+                                                                    //
+                    for (i=0; i<4; i++)                             //
+                    {                                               //        
+                        if ((X>SEG_X+(i*SEG_STEP)) && (X<SEG_X+(i*SEG_STEP)+SEG_WIDTH) && (Y>SEG_Y) && (Y<SEG_Y+SEG_HEIGHT*3))                
+                        {                                           //
+                            Segments ^= Mask >> i;                  //
+                        }                                           //  
+                    }                                               //
+                }                                                       
+            break;              //----------------- end slave 2 ----------------                          
+
+      
+ /*       
+            case (EL2004):     //-------------- slave 3 EL2004 ----------------        
+        
+        
+                if(TouchWasReleased)                                // first check if the touch was
+                {                                                   // not tapped in the previous rounds  
+                    TouchWasReleased = false;                       // because for the following fields
+                    TimeTouchReleased = SysMilliS();                // we don't want autorepeat
+                    
+                    uint8_t Mask = 0x08;                            // check if one of the output is tapped
+                                                                    //
+                    for (i=0; i<4; i++)                             //
+                    {                                               //        
+                        if ((X>OUT_X+(i*OUT_STEP)) && (X<OUT_X+(i*OUT_STEP)+OUT_WIDTH) && (Y>OUT_Y) && (Y<OUT_Y+OUT_HEIGHT))                
+                        {                                           //
+                            Outputs ^= Mask >> i;                   //
+                        }                                           //  
+                    }                                               //
+                }  
+*/                                                                                                  
+        }             
+    }
+    
+    else                                                               
+    {       
+        if ( SysMilliS()-TimeTouchReleased > TIME_TOUCH_RELEASED)   // if the touchscreen was not
+        {                                                           // tapped for enought time            
+            TouchWasReleased = true;                                // remember it
+            
+        }      
+    }          
+}    
+
+ 
+//----- draw the fixed part of the visualized slave ----------------------------
+
+void DrawSlaveFixedParts()
+{
+    int i;
+    int Offset;
+    
+    TFT.cls();                                                      // clear screen
+    DrawSideMenu(VisuSlave);                                        // draw the side menu
+                   
+    switch (VisuSlave)                                              // check which slave is visualized on the TFT                                 
+    {                      
+        case (LAB_1):     //-------------- slave 1 ---------------------------
+                                                       
+            TFT.foreground(Yellow);                                 // draw the thermometer
+        
+            TFT.circle(TERMO_X, TERMO_Y, 18, Yellow);               // bowl
+            TFT.fillcircle(TERMO_X, TERMO_Y, 17, Red);              //
+                            
+                                                                    // tube
+            TFT.rect(TERMO_X-8, TERMO_Y-185, TERMO_X+8, TERMO_Y-18, Yellow);    
+            TFT.fillrect(TERMO_X-7, TERMO_Y-18, TERMO_X+7, TERMO_Y-16, Red);   
+                       
+            for (i=0; i<8; i++)                                     // scale      
+            {                                                       //  
+                if (i <1)                                           //            
+                    Offset = 5;                                     //
+                else                                                //
+                    Offset = 0;                                     //    
+                                                                    //        
+                TFT.line(TERMO_X-8, (TERMO_Y-28)-(i *20), TERMO_X-18,(TERMO_Y-28)-(i*20), Yellow);
+                TFT.locate(TERMO_X-48+Offset, TERMO_Y-(i*20)-32);   //
+                TFT.set_font((unsigned char*) Arial12x12);          //
+                TFT.printf("%3d",(i*10)-10);                        //
+            }                                                         
+   
+            TFT.foreground(Green);                                  //
+            TFT.set_font((unsigned char*) Arial28x28);              //            
+            TFT.locate(TERMO_X+25, TERMO_Y+28);                     //  
+            TFT.printf("C");                                        //
+                             
+                                                                    // draw the alarm control panel
+                                                                    
+                                                                    // frame
+            TFT.rect(ALARM_X, ALARM_Y, ALARM_X+ALARM_WIDTH, ALARM_Y+(ALARM_HEIGHT*4), Magenta); 
+                                                                    //
+            for (i=0; i<3; i++)                                     //    
+            {                                                       //
+                TFT.line(ALARM_X, ALARM_Y+((i+1)*ALARM_HEIGHT) , ALARM_X+ALARM_WIDTH, ALARM_Y+((i+1)*ALARM_HEIGHT), Magenta);
+            }                                                       //    
+                                                                    //
+            TFT.line(ALARM_X+(ALARM_WIDTH/2), ALARM_Y, ALARM_X+(ALARM_WIDTH/2), ALARM_Y+(ALARM_HEIGHT*2), Magenta);
+            TFT.line(ALARM_X+(ALARM_WIDTH/2), ALARM_Y+(ALARM_HEIGHT*3), ALARM_X+(ALARM_WIDTH/2), ALARM_Y+(ALARM_HEIGHT*4), Magenta);           
+   
+            TFT.set_font((unsigned char*) Arial28x28);              // "+" and "-"
+            TFT.foreground(Green);                                  //                   
+            TFT.locate(ALARM_X+11, ALARM_Y+(ALARM_HEIGHT*3)+9);     //
+            TFT.printf("+");                                        //    
+            TFT.locate(ALARM_X+(ALARM_WIDTH/2)+14, ALARM_Y+(ALARM_HEIGHT*3)+9);    
+            TFT.printf("-");                                        //
+  
+                                                                    // alarm bar  
+            TFT.rect(ALARM_X-30, ALARM_Y, ALARM_X-25, ALARM_Y+168, Yellow);                                 
+                       
+            TFT.set_font((unsigned char*) Arial12x12);              // draw "ALARM SETTINGS"   
+            TFT.foreground(Yellow);                                 //                          
+            TFT.locate(ALARM_X-35, ALARM_Y+14+(ALARM_HEIGHT*4));    //
+            TFT.printf("ALARM SETTINGS");                           //                  
+            
+        break;        
+           
+   
+           
+        
+        case (LAB_2):     //-------------- slave 2 ---------------------------
+                
+            TFT.foreground(Yellow);   
+
+            for (i=0; i<4; i++)                                     // draw the segments fixed parts
+            {                                                       //
+                TFT.rect(SEG_X+(i*SEG_STEP), SEG_Y, SEG_X+SEG_WIDTH+(i*SEG_STEP), SEG_Y+SEG_HEIGHT, Yellow);                            
+                                                                    //
+                TFT.set_font((unsigned char*) Arial12x12);          // 
+                TFT.locate(SEG_X+44, SEG_Y+SEG_HEIGHT+12);          // 
+                TFT.printf("MIDDLE SEGMENTS");                      //
+            }                                                       //
+               
+            for (i=0; i<3; i++)                                     // draw the buttons fixed parts        
+            {                                                       //    
+                TFT.circle(BUTTONS_X+(i*BUTTONS_STEP), BUTTONS_Y, BUTTONS_R, Red);                
+                TFT.circle(BUTTONS_X+BUTTONS_WIDTH+(i*BUTTONS_STEP), BUTTONS_Y, BUTTONS_R, Red); 
+                                                                    //
+                TFT.line(BUTTONS_X+(i*BUTTONS_STEP)-BUTTONS_R, BUTTONS_Y, BUTTONS_X+(i*BUTTONS_STEP)-BUTTONS_R-4, BUTTONS_Y, Red);  
+                TFT.line(BUTTONS_X+BUTTONS_WIDTH+(i*BUTTONS_STEP)+BUTTONS_R, BUTTONS_Y, BUTTONS_X+BUTTONS_WIDTH+(i*BUTTONS_STEP)+BUTTONS_R+4 , BUTTONS_Y, Red);                          
+            }                                                       //
+                                                                    //   
+            TFT.set_font((unsigned char*) Arial12x12);              // 
+            TFT.locate(BUTTONS_X+38, BUTTONS_Y+12);                 // 
+            TFT.printf("BUTTONS");                                  //               
+      
+                                                                    // draw the potentiometer window fixed parts
+            TFT.rect(ANALOG_X, ANALOG_Y, ANALOG_X+ANALOG_WIDTH, ANALOG_Y+ANALOG_HEIGHT, Magenta); 
+                                                                    //
+            TFT.set_font((unsigned char*) Arial12x12);              // 
+            TFT.locate(ANALOG_X+35, ANALOG_Y+ANALOG_HEIGHT+12);     // 
+            TFT.printf("POTENTIOMETER");                            // 
+            
+            Graph_x = 0;                    
+            
+            DrawSegmentsValue(Segments);                            // draw the segments status 
+            DrawButtonsValue (Buttons);                             // draw the buttons status  
+            DrawPotentiometerValue(PotValue);                       // draw the potentiometer value                  
+        break;     
+
+/*
+        case (EK1100):     //-------------- slave 3 EK1100 --------------------
+
+            TFT.foreground(Red);   
+            TFT.set_font((unsigned char*) Arial12x12);
+            TFT.locate(0, MENU_Y+(MENU_HEIGHT*2)+12);
+
+            TFT.printf("This slave has no Input/Output:");
+
+        break;
+
+     
+        case (EL2004):     //-------------- slave 4 EL2004 --------------------
+                
+            TFT.foreground(Yellow);   
+
+            for (i=0; i<4; i++)                                     // draw the outputs fixed parts
+            {                                                       //
+                TFT.rect(OUT_X+(i*OUT_STEP), OUT_Y, OUT_X+OUT_WIDTH+(i*OUT_STEP), OUT_Y+OUT_HEIGHT, Yellow);                            
+                                                                    //
+                TFT.set_font((unsigned char*) Arial12x12);          // 
+                TFT.locate(OUT_X+45, OUT_Y+OUT_HEIGHT+40);          // 
+                TFT.printf("DIGITAL OUTPUTS");                      //
+            }                                                       //
+            
+            DrawOutputsValue(Outputs);                              // draw the outputs status           
+        break;           
+        
+        case (EL1014):     //-------------- slave 5 EL1014 --------------------
+                   
+            TFT.foreground(Yellow);   
+
+               
+            for (i=0; i<4; i++)                                     // draw the inputs fixed parts        
+            {                                                       //    
+                TFT.circle(INPUTS_X+(i*INPUTS_STEP), INPUTS_Y, INPUTS_R, Red);                
+                TFT.circle(INPUTS_X+INPUTS_WIDTH+(i*INPUTS_STEP), INPUTS_Y, INPUTS_R, Red); 
+                                                                    //
+                TFT.line(INPUTS_X+(i*INPUTS_STEP)-INPUTS_R, INPUTS_Y, INPUTS_X+(i*INPUTS_STEP)-INPUTS_R-4, INPUTS_Y, Red);  
+                TFT.line(INPUTS_X+INPUTS_WIDTH+(i*INPUTS_STEP)+INPUTS_R, INPUTS_Y, INPUTS_X+INPUTS_WIDTH+(i*INPUTS_STEP)+INPUTS_R+4 , INPUTS_Y, Red);                          
+            }                                                       //
+                                                                    //   
+            TFT.set_font((unsigned char*) Arial12x12);              // 
+            TFT.locate(INPUTS_X+45, INPUTS_Y+40);                   // 
+            TFT.printf("DIGITAL INPUTS");                           //               
+
+            DrawInputsValue (Inputs);                               // draw the inputs status               
+        break;      
+*/       
+                          
+    }    
+    
+    FirstRound = true;                
+}    
+
+
+//---- draw the menu on the upper right part of the TFT ------------------------
+
+void DrawSideMenu(uint8_t Slave)
+{                                                                   // draw the side menu frame
+    TFT.rect(MENU_X, MENU_Y, MENU_X+MENU_WIDTH, MENU_Y+MENU_HEIGHT*2, Green);  
+    TFT.line(MENU_X, MENU_HEIGHT, MENU_X+MENU_WIDTH, MENU_HEIGHT, Green);    
+    TFT.line(MENU_X+(MENU_WIDTH/2), MENU_Y+MENU_HEIGHT, MENU_X+(MENU_WIDTH/2), MENU_Y+(MENU_HEIGHT*2), Green);
+                                                                                
+                                                                    // draw the slave number
+    TFT.fillrect(MENU_X+1, MENU_Y+1, MENU_X+MENU_WIDTH-1, MENU_Y+MENU_HEIGHT-1, Red);    
+    TFT.set_font((unsigned char*) Arial12x12);                      //
+    TFT.foreground(Yellow);                                         //        
+    TFT.locate(MENU_X+8 , MENU_Y+18);                               //
+    TFT.background(Red);                                            //
+    TFT.printf("SLAVE %d", Slave);                                  //
+    TFT.background(Black);                                          //
+                                                                              
+    TFT.set_font((unsigned char*) Arial28x28);                      // draw "+" and "-"   
+    TFT.foreground(Green);                                          //                                                                                   
+    TFT.locate (MENU_X+11, MENU_Y+MENU_HEIGHT+9);                   //      
+    TFT.printf("+");                                                //                                                                                
+    TFT.locate (MENU_X+(MENU_WIDTH/2)+14, MENU_Y+MENU_HEIGHT+9);    //
+    TFT.printf("-");                                                //
+ 
+    TFT.set_font((unsigned char*) Arial12x12);                      // draw the slave name
+    TFT.foreground(Red);                                            //
+                                                                    //
+    TFT.locate(MENU_X, MENU_Y+(MENU_HEIGHT*2)+12);                  //
+    TFT.printf("%.9s", ec_slave[Slave].name);                       //
+}  
+
+
+//---- draw the starting banner ------------------------------------------------
+
+void DrawBanner()
+{
+    TFT.set_font((unsigned char*) Arial24x23);
+    TFT.foreground(Red);
+    TFT.locate(30, 50);
+    TFT.printf("EasyCAT");
+    TFT.locate(30, 80);
+    TFT.printf("SOEM MASTER");    
+    
+    TFT.set_font((unsigned char*) Arial12x12);
+    TFT.foreground(Green);
+    TFT.locate(30, 140);
+    TFT.printf("www.bausano.net");
+
+    TFT.foreground(Green);
+    TFT.locate(30, 160);
+    TFT.printf("www.easycatshield.com"); 
+    
+    TFT.locate(30, 180);
+    TFT.printf("https://openethercatsociety.github.io/");     
+}  
+
+
+
+//****** slave 1 functions *****************************************************
+
+//---- draw the temperature value both in analog and in digital form ------------
+
+void DrawTemperatureValue(float fValue)
+{                 
+    TFT.set_font((unsigned char*) Arial28x28);                      // digital visualization       
+    TFT.foreground(Green);                                          //
+    TFT.locate(TERMO_X-45, TERMO_Y+28);                             //
+    TFT.printf("%4.1f", fValue);                                    //
+        
+    if (fValue > 68)                                                // limit the value for the
+        fValue = 68;                                                // analog visualization    
+                                                                    //
+    if (fValue < -15)                                               //
+        fValue = -15;                                               //
+            
+    int LenColonnina = fValue * 2;                                  // analog visualization   
+    TFT.fillrect(TERMO_X-7, TERMO_Y-184, TERMO_X+7, TERMO_Y-18-LenColonnina-30-1, Black);                             
+    TFT.fillrect(TERMO_X-7, TERMO_Y-18-LenColonnina-30, TERMO_X+7, TERMO_Y-18, Red);
+}   
+
+
+//---- draw the current parameter of the temperature alarm ---------------------
+
+void DrawAlarmSettings(float fThreshold, bool OnOff, bool MinMax)
+{ 
+   
+    if (OnOff)                                                      // alarm on    
+    {                                                               // draw "ON" on red background
+                                                                    // and "OFF"" on black background
+        DisplayInRect (ALARM_X, ALARM_Y, 10, 14, "ON", Red, (unsigned char*)Arial12x12);       
+        DisplayInRect (ALARM_X+(ALARM_WIDTH/2), ALARM_Y, 10, 14, "OFF", Black, (unsigned char*)Arial12x12);
+    }                                                                   
+    else                                                            // alarm off             
+    {                                                               // and "ON"" on black background 
+                                                                    // draw "OFF" on red background
+        DisplayInRect (ALARM_X, ALARM_Y, 10, 14, "ON", Black, (unsigned char*)Arial12x12);       
+        DisplayInRect (ALARM_X+(ALARM_WIDTH/2), ALARM_Y, 10, 14, "OFF", Red, (unsigned char*)Arial12x12);                    
+    }      
+    
+    
+    if (MinMax)                                                     // alarm when temperature < threshold
+    {                                                               // draw ">" on red background
+                                                                    // and ">"" on black background 
+        DisplayInRect (ALARM_X, ALARM_Y+ALARM_HEIGHT, 10, 9, ">", Red, (unsigned char*)Arial28x28);       
+        DisplayInRect (ALARM_X+(ALARM_WIDTH/2), ALARM_Y+ALARM_HEIGHT, 10, 9, "<", Black, (unsigned char*)Arial28x28);
+    }                                                               
+    else                                                            // alarm when temperature > threshold
+    {                                                               // draw ">" on black background
+                                                                    // and ">"" on red background
+        DisplayInRect (ALARM_X, ALARM_Y+ALARM_HEIGHT, 10, 9, ">", Black, (unsigned char*)Arial28x28);       
+        DisplayInRect (ALARM_X+(ALARM_WIDTH/2), ALARM_Y+ALARM_HEIGHT, 10, 9, "<", Red, (unsigned char*)Arial28x28);
+    }        
+
+    DrawOnlyThreshold(fThreshold, OnOff, MinMax);
+}    
+
+
+//---- draw only the threshold setting -----------------------------------------
+
+void DrawOnlyThreshold(float fThreshold, bool OnOff, bool MinMax)   // this function is used to update
+{                                                                   // only the threshold value not to
+                                                                    // flicker the TFT, when "+" or
+                                                                    // "-" are tapped   
+                                                                    
+    TFT.foreground(Yellow);                                         // draw the alarm threshold    
+    TFT.set_font((unsigned char*) Arial12x12);                      // in digital form
+    TFT.locate(ALARM_X+26, ALARM_Y+14+(ALARM_HEIGHT*2));            // 
+    TFT.printf("     ");                                            //    
+    TFT.locate(ALARM_X+26, ALARM_Y+14+(ALARM_HEIGHT*2));            // 
+    TFT.printf("%+3.1f", fThreshold);                               //                                                                        
+                                                                    
+    if (fThreshold > 68)                                            // limit the value for the
+        fThreshold = 68;                                            // analog visualization
+                                                                    //
+    if (fThreshold < -15)                                           //
+        fThreshold = -15;                                           //                        
+                                                                                                                                        
+    int LenAlarmBar = fThreshold * 2;    
+  
+    if (OnOff)                                                      // alarm on    
+    {                                                               //    
+        if (MinMax)                                                 // fill the threshold bar
+        {                                                           // in accordance with the MinMax setting
+            TFT.fillrect(ALARM_X-29, ALARM_Y+1, ALARM_X-26, ALARM_Y+167-LenAlarmBar-30, Green);          
+            TFT.fillrect(ALARM_X-29, ALARM_Y+167-LenAlarmBar-30, ALARM_X-26, ALARM_Y+167, Red);               
+        }                                                           //                                                           
+        else                                                        //    
+        {                                                           //
+            TFT.fillrect(ALARM_X-29, ALARM_Y+1, ALARM_X-26, ALARM_Y+167-LenAlarmBar-30, Red);          
+            TFT.fillrect(ALARM_X-29, ALARM_Y+167-LenAlarmBar-30, ALARM_X-26, ALARM_Y+167, Green);              
+        }                                                           //                                                      
+    }                                                                   
+                                                                     
+    else                                                            // alarm off             
+    {                                                               //
+                                                                    // clear the threshold bar
+        TFT.fillrect(ALARM_X-29, ALARM_Y+1, ALARM_X-26, ALARM_Y+167, Black);                      
+    }                                                               //                        
+}    
+
+
+//---- draw the blinking alarm signal ------------------------------------------
+
+void DrawAlarmStatus(bool Alarm)
+{              
+    bool static PrevAlarmVisu;
+                                            
+    TFT.set_font((unsigned char*) Arial28x28);  
+    TFT.foreground(Red);                        
+    TFT.locate(TERMO_X+90, TERMO_Y+28); 
+             
+    if (Alarm && Blink && !PrevAlarmVisu)
+    {
+        TFT.printf("ALARM !");                         
+        PrevAlarmVisu = true;               
+    }
+    else if ((!Alarm && PrevAlarmVisu) || (Alarm && !Blink && PrevAlarmVisu))
+    {
+        TFT.printf("       ");
+        PrevAlarmVisu = false;         
+    }             
+}    
+
+
+//----- draw a rectangle with text and colored background ----------------------
+   
+void DisplayInRect (int X, int Y, int X_off, int Y_off, char* Value, int BackColor, unsigned char* Font)
+{
+    TFT.set_font(Font);
+    TFT.foreground(Green);     
+ 
+    TFT.fillrect(X+1, Y+1, X+(MENU_WIDTH/2)-1, Y+MENU_HEIGHT-1, BackColor);
+    
+    TFT.locate(X+X_off , Y+Y_off); 
+    TFT.background(BackColor); 
+    TFT.printf("%s", Value );  
+    TFT.background(Black);     
+}    
+
+
+
+
+//****** slave 2 functions *****************************************************
+
+//------------------------------------------------------------------------------
+
+void DrawButtonsValue (uint8_t Value)
+{
+    uint8_t Slope;
+    int i;
+  
+    for (i=0; i<3; i++)
+    {
+        if ((Value & 0x04) == 0x04)
+            Slope = BUTTONS_R;
+        else
+            Slope = 16;          
+    
+        TFT.fillrect(BUTTONS_X+(i*BUTTONS_STEP), BUTTONS_Y-16-1, BUTTONS_X+BUTTONS_WIDTH+(i*BUTTONS_STEP), BUTTONS_Y-BUTTONS_R-1, Black);
+    
+        TFT.line(BUTTONS_X+(i*BUTTONS_STEP), BUTTONS_Y-BUTTONS_R-1, BUTTONS_X+BUTTONS_WIDTH+(i*BUTTONS_STEP), BUTTONS_Y-Slope-1, Red); 
+
+        Value = Value << 1;    
+    } 
+}    
+
+
+//------------------------------------------------------------------------------
+
+void DrawSegmentsValue(uint8_t Value)
+{
+    int i;
+    int Color;
+   
+    for (i=0; i<4; i++)
+    {
+        if ((Value & 0x08) == 0x08)
+            Color = Red;
+        else
+            Color = Black;    
+        
+        TFT.fillrect(SEG_X+(i*SEG_STEP)+1, SEG_Y+1, SEG_X+SEG_WIDTH+(i*SEG_STEP)-1, SEG_Y+SEG_HEIGHT-1, Color);   
+    
+        Value = Value << 1;   
+    } 
+}    
+
+
+//---- draw the potentiometer value --------------------------------------------
+
+void DrawPotentiometerValue (uint16_t PotValue)
+{
+    
+    if (PotValue != PrevPotValue)
+    {
+        PrevPotValue = PotValue;
+ 
+        TFT.set_font((unsigned char*) Arial12x12);              // 
+        TFT.foreground(Green);                                  //            
+        TFT.locate(ANALOG_X+170, ANALOG_Y+ANALOG_HEIGHT+12);    // 
+        TFT.printf("%4d", (int)PotValue);                  //  
+    }             
+    
+    
+    if (++Graph_x > ANALOG_WIDTH-3)
+    {
+        Graph_x = 0;     
+    }
+    
+    PotValue = PotValue / (1023/(ANALOG_HEIGHT-2)); 
+                  
+    TFT.pixel(ANALOG_X+Graph_x+1, ANALOG_Y+ANALOG_HEIGHT-PotValue-1, Green);       
+    
+    int Overflow = (ANALOG_WIDTH-2) - (Graph_x+16);
+    if (Overflow < 0)
+    {
+        TFT.fillrect(ANALOG_X+Graph_x+2, ANALOG_Y+1, ANALOG_X+Graph_x+16+Overflow, ANALOG_Y+ANALOG_HEIGHT-1, Black);              
+        TFT.fillrect(ANALOG_X+1, ANALOG_Y+1, ANALOG_X+1-Overflow, ANALOG_Y+ANALOG_HEIGHT-1, Black);                    
+    } 
+    else
+    {
+        TFT.fillrect(ANALOG_X+Graph_x+2, ANALOG_Y+1, ANALOG_X+Graph_x+16, ANALOG_Y+ANALOG_HEIGHT-1, Black);                 
+    }                                                       
+}
+
+
+//****** slave 3 functions EL2004 **********************************************
+/*
+
+//------------------------------------------------------------------------------
+
+void DrawOutputsValue(uint8_t Value)
+{
+    int i;
+    int Color;
+   
+    for (i=0; i<4; i++)
+    {
+        if ((Value & 0x08) == 0x08)
+            Color = Green;
+        else
+            Color = Black;    
+        
+        TFT.fillrect(OUT_X+(i*OUT_STEP)+1, OUT_Y+1, OUT_X+OUT_WIDTH+(i*OUT_STEP)-1, OUT_Y+OUT_HEIGHT-1, Color);   
+    
+        Value = Value << 1;   
+    } 
+}    
+
+//****** slave 4 functions *****************************************************
+
+//------------------------------------------------------------------------------
+
+void DrawInputsValue (uint8_t Value)
+{
+    uint8_t Slope;
+    int i;
+  
+    for (i=0; i<4; i++)
+    {
+        if ((Value & 0x08) == 0x08)
+            Slope = INPUTS_R;
+        else
+            Slope = 16;          
+    
+        TFT.fillrect(INPUTS_X+(i*INPUTS_STEP), INPUTS_Y-16-1, INPUTS_X+INPUTS_WIDTH+(i*INPUTS_STEP), INPUTS_Y-INPUTS_R-1, Black);
+    
+        TFT.line(INPUTS_X+(i*INPUTS_STEP), INPUTS_Y-INPUTS_R-1, INPUTS_X+INPUTS_WIDTH+(i*INPUTS_STEP), INPUTS_Y-Slope-1, Red); 
+
+        Value = Value << 1;    
+    } 
+}    
+*/
+
+
+//****** touchscreen functions *************************************************
+
+//----- read touchscreen status ------------------------------------------------
+
+bool TouchRead(uint16_t* X, uint16_t* Y)                        
+{
+    bool Result = false;
+      
+    {                                       
+        if (TouchRead_Z())                      // if the touchscreen is tapped
+        {                                       //
+            *X = TouchRead_X();                 // read also the X and Y axis
+            *Y = TouchRead_Y();                 //
+            
+            wait_us (1000);
+                                            
+            if (TouchRead_Z())                  // if the touchscreen is still tapped
+            {                                   // we assume that the result is good        
+                Result = true;                  //
+                
+              //  TFT.pixel (*X, *Y, White);      //debug - draw the touched point on the TFT
+            }
+        }                          
+    }              
+
+   return Result;                         
+}   
+
+
+//----- read touchscreen X axis ------------------------------------------------
+
+uint16_t TouchRead_X()
+{ 
+    float fValue;
+      
+    DigitalIn  Ym(PIN_YM);                      // set the I/O
+    Ym.mode(PullDown);                          //
+                                                //
+    DigitalOut Xp(PIN_XP);                      //
+    Xp = 1;                                     //
+                                                //        
+    DigitalOut Xm(PIN_XM);                      //
+    Xm = 0;                                     //
+                                                //
+    AnalogIn Yp(PIN_YP);                        //
+            
+    fValue = ReadAnalog(Yp);                    // read the axis
+    
+    fValue -= TOUCH_X_OFFSET;                   // rectify offsett and gain
+    if (fValue <0 )                             //
+        fValue = 0;                             //
+                                                //
+    fValue *= TOUCH_X_GAIN;                     //
+                                         
+    return (uint16_t)fValue;
+}
+
+
+//----- read touchscreen Y axis ------------------------------------------------
+
+uint16_t TouchRead_Y()
+{ 
+    float fValue;
+      
+    DigitalIn Xm(PIN_XM);                       // set the I/O
+    Xm.mode(PullDown);                          //
+                                                //            
+    DigitalOut Yp(PIN_YP);                      //
+    Yp = 1;                                     //
+                                                //
+    DigitalOut Ym(PIN_YM);                      //
+    Ym = 0;                                     //
+                                                //
+    AnalogIn Xp(PIN_XP);                        //
+                
+    fValue = ReadAnalog(Xp);                    // read the axis
+                      
+    fValue -= TOUCH_Y_OFFSET;                   // rectify offset and gain 
+    if (fValue <0 )                             // 
+        fValue = 0;                             //
+                                                //
+    fValue *= TOUCH_Y_GAIN;                     //    
+                                 
+    return (uint16_t)fValue;
+}
+
+
+//----- read touchscreen Z axis ------------------------------------------------
+
+bool TouchRead_Z()                              // read the Z axis to see if the 
+{                                               // touchscreen has been tapped            
+    float fValue = 0;    
+    bool Result;
+                                                  
+    DigitalIn  Yp(PIN_YP);                      // set the I/O
+    Yp.mode(PullUp);                            //
+                                                //
+    AnalogIn Ym(PIN_YM);                        //
+                                                //
+    DigitalOut Xm(PIN_XM);                      //    
+    Xm = 0;                                     //
+                                                //    
+    DigitalOut Xp(PIN_XP);                      //
+    Xp = 0;                                     //
+                                                   
+    for (int i = 0; i<TOUCH_SAMPLES; i++)       // read the axis several times    
+    {                                           // and average the result
+        wait_us(10);                            //
+                                                //
+        fValue += Ym.read();                    //
+    }                                           //
+    fValue /= TOUCH_SAMPLES;                    //
+    
+    if (fValue < TOUCH_THRESHOLD)               // compare the result with
+        Result = true;                          // the threshold
+    else                                        //
+        Result = false;                         //
+
+    return Result;                              //
+}
+
+
+//----- read touchscreen X or Y axis with a window filter ----------------------
+
+float ReadAnalog (AnalogIn AnaCh)               // check that consecutive readings
+{                                               // fall in the acceptance window
+    float fArray[TOUCH_SAMPLES];                // 
+    float fResult;
+    float fDiff;
+    
+    int Rounds = TOUCH_MAX_ROUNDS;              // maximum number of attempts
+        
+    for (int i=0; i<TOUCH_SAMPLES; i++)         
+    {        
+        wait_us(10);
+    
+        fResult = AnaCh.read(); 
+       
+        if (i>0)
+        {
+            fDiff = abs(fResult - fArray[i-1]);
+                    
+            if (fDiff > TOUCH_WINDOW)
+                i= -1;
+            else
+                fArray[i] = fResult; 
+                                
+            if (Rounds-- < 0) 
+            { 
+                fResult = 0;
+                return fResult;       
+            }    
+        }                   
+        
+        else
+        {
+            fArray[i] = fResult;          
+        }         
+    }             
+         
+    fResult =0;
+    
+    for (int i=0; i<TOUCH_SAMPLES; i++)
+    {        
+        fResult += fArray[i];       
+    }            
+
+    return fResult /= TOUCH_SAMPLES;    
+}    
+
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mbed-os.lib	Tue Jun 11 10:19:08 2019 +0000
@@ -0,0 +1,1 @@
+https://github.com/ARMmbed/mbed-os/#88fb9b162d93a10e0d97f151c91bf2faf69e1b9e
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mbed_app.json	Tue Jun 11 10:19:08 2019 +0000
@@ -0,0 +1,9 @@
+{                                   
+    "target_overrides": 
+    {
+        "NUCLEO_F767ZI":  
+        {
+            "target.d11_configuration": "PB_5"
+        }
+    }
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/note_marco.txt	Tue Jun 11 10:19:08 2019 +0000
@@ -0,0 +1,26 @@
+Libreria usata per il display SeeedStudio
+
+https://os.mbed.com/users/Rhyme/code/SPI_TFT_ILI9341/file/61fa73a34834/SPI_TFT_ILI9341.cpp/
+
+Motoo Tanaka /  SPI_TFT_ILI9341
+
+----------------------------------------------------------------------
+
+The SPI_TFT_IL9341 lib is deprecated. 
+Please use the UniGraphic lib for new projects. 
+https://developer.mbed.org/teams/GraphicsDisplay/code/UniGraphic/ 
+This has fixed font declarations. You can also use them in your project.
+
+--------------------------------------------------------------------------------
+
+network driver
+
+https://os.mbed.com/users/mbed_official/code/lwip-eth/file/9de8bd8ca1c8/arch/TARGET_STM/stm32f4_emac.c/
+
+//------------------------------------------------------------------------------
+
+wiki TFT SeeedStudio
+http://wiki.seeedstudio.com/2.8inch_TFT_Touch_Shield_v2.0/
+
+schema TFT SeeedStudio
+https://github.com/SeeedDocument/TFT_Touch_Shield_V2/raw/master/resources/TFT%20Touch.pdf
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/soem_start.cpp	Tue Jun 11 10:19:08 2019 +0000
@@ -0,0 +1,705 @@
+
+#include "soem_start.h"
+#include "ethercat.h"
+#include "string.h"
+#include "oshw.h"
+
+#include "mbed.h"
+
+#include <inttypes.h>
+
+#include "SPI_TFT_ILI9341.h"
+
+extern SPI_TFT_ILI9341 TFT;
+
+boolean printSDO = TRUE;			
+boolean printMAP = TRUE;			
+
+ec_ODlistt ODlist;
+ec_OElistt OElist;
+char usdo[128];
+char hstr[1024];
+
+char extern IOmap[];
+
+uint32_t error_counter = 0;
+
+
+char* dtype2string(uint16 dtype);
+char* SDO2string(uint16 slave, uint16 index, uint8 subidx, uint16 dtype);
+int si_PDOassign(uint16 slave, uint16 PDOassign, int mapoffset, int bitoffset);
+void si_sdo(int cnt);
+int si_map_sdo(int slave);
+int si_siiPDO(uint16 slave, uint8 t, int mapoffset, int bitoffset);
+bool network_scanning(void);
+
+
+
+//---- convert Ethercat types to string ----------------------------------------
+
+
+char* dtype2string(uint16 dtype)
+{
+    switch(dtype)
+    {
+        case ECT_BOOLEAN:
+            sprintf(hstr, "BOOLEAN");
+            break;
+        case ECT_INTEGER8:
+            sprintf(hstr, "INTEGER8");
+            break;
+        case ECT_INTEGER16:
+            sprintf(hstr, "INTEGER16");
+            break;
+        case ECT_INTEGER32:
+            sprintf(hstr, "INTEGER32");
+            break;
+        case ECT_INTEGER24:
+            sprintf(hstr, "INTEGER24");
+            break;
+        case ECT_INTEGER64:
+            sprintf(hstr, "INTEGER64");
+            break;
+        case ECT_UNSIGNED8:
+            sprintf(hstr, "UNSIGNED8");
+            break;
+        case ECT_UNSIGNED16:
+            sprintf(hstr, "UNSIGNED16");
+            break;
+        case ECT_UNSIGNED32:
+            sprintf(hstr, "UNSIGNED32");
+            break;
+        case ECT_UNSIGNED24:
+            sprintf(hstr, "UNSIGNED24");
+            break;
+        case ECT_UNSIGNED64:
+            sprintf(hstr, "UNSIGNED64");
+            break;
+        case ECT_REAL32:
+            sprintf(hstr, "REAL32");
+            break;
+        case ECT_REAL64:
+            sprintf(hstr, "REAL64");
+            break;
+        case ECT_BIT1:
+            sprintf(hstr, "BIT1");
+            break;
+        case ECT_BIT2:
+            sprintf(hstr, "BIT2");
+            break;
+        case ECT_BIT3:
+            sprintf(hstr, "BIT3");
+            break;
+        case ECT_BIT4:
+            sprintf(hstr, "BIT4");
+            break;
+        case ECT_BIT5:
+            sprintf(hstr, "BIT5");
+            break;
+        case ECT_BIT6:
+            sprintf(hstr, "BIT6");
+            break;
+        case ECT_BIT7:
+            sprintf(hstr, "BIT7");
+            break;
+        case ECT_BIT8:
+            sprintf(hstr, "BIT8");
+            break;
+        case ECT_VISIBLE_STRING:
+            sprintf(hstr, "VISIBLE_STRING");
+            break;
+        case ECT_OCTET_STRING:
+            sprintf(hstr, "OCTET_STRING");
+            break;
+        default:
+            sprintf(hstr, "Type 0x%4.4X", dtype);
+    }
+    return hstr;
+}
+
+
+//------------------------------------------------------------------------------
+
+char* SDO2string(uint16 slave, uint16 index, uint8 subidx, uint16 dtype)
+{
+   int l = sizeof(usdo) - 1, i;
+   uint8 *u8;
+   int8 *i8;
+   uint16 *u16;
+   int16 *i16;
+   uint32 *u32;
+   int32 *i32;
+   uint64 *u64;
+   int64 *i64;
+   float *sr;
+   double *dr;
+   char es[32];
+
+   memset(&usdo, 0, 128);
+   ec_SDOread(slave, index, subidx, FALSE, &l, &usdo, EC_TIMEOUTRXM);
+   if (EcatError)
+   {
+      return ec_elist2string();
+   }
+   else
+   {
+      switch(dtype)
+      {
+         case ECT_BOOLEAN:
+            u8 = (uint8*) &usdo[0];
+            if (*u8) sprintf(hstr, "TRUE");
+             else sprintf(hstr, "FALSE");
+            break;
+         case ECT_INTEGER8:
+            i8 = (int8*) &usdo[0];
+            sprintf(hstr, "0x%2.2x %d", *i8, *i8);
+            break;
+         case ECT_INTEGER16:
+            i16 = (int16*) &usdo[0];
+            sprintf(hstr, "0x%4.4x %d", *i16, *i16);
+            break;
+         case ECT_INTEGER32:
+         case ECT_INTEGER24:
+            i32 = (int32*) &usdo[0];
+            sprintf(hstr, "0x%8.8x %d", *i32, *i32);
+            break;
+         case ECT_INTEGER64:
+            i64 = (int64*) &usdo[0];
+            sprintf(hstr, "0x%16.16"PRIx64" %"PRId64, *i64, *i64);
+            break;
+         case ECT_UNSIGNED8:
+            u8 = (uint8*) &usdo[0];
+            sprintf(hstr, "0x%2.2x %u", *u8, *u8);
+            break;
+         case ECT_UNSIGNED16:
+            u16 = (uint16*) &usdo[0];
+            sprintf(hstr, "0x%4.4x %u", *u16, *u16);
+            break;
+         case ECT_UNSIGNED32:
+         case ECT_UNSIGNED24:
+            u32 = (uint32*) &usdo[0];
+            sprintf(hstr, "0x%8.8x %u", *u32, *u32);
+            break;
+         case ECT_UNSIGNED64:
+            u64 = (uint64*) &usdo[0];
+            sprintf(hstr, "0x%16.16"PRIx64" %"PRIu64, *u64, *u64);
+            break;
+         case ECT_REAL32:
+            sr = (float*) &usdo[0];
+            sprintf(hstr, "%f", *sr);
+            break;
+         case ECT_REAL64:
+            dr = (double*) &usdo[0];
+            sprintf(hstr, "%f", *dr);
+            break;
+         case ECT_BIT1:
+         case ECT_BIT2:
+         case ECT_BIT3:
+         case ECT_BIT4:
+         case ECT_BIT5:
+         case ECT_BIT6:
+         case ECT_BIT7:
+         case ECT_BIT8:
+            u8 = (uint8*) &usdo[0];
+            sprintf(hstr, "0x%x", *u8);
+            break;
+         case ECT_VISIBLE_STRING:
+            strcpy(hstr, usdo);
+            break;
+         case ECT_OCTET_STRING:
+            hstr[0] = 0x00;
+            for (i = 0 ; i < l ; i++)
+            {
+               sprintf(es, "0x%2.2x ", usdo[i]);
+               strcat( hstr, es);
+            }
+            break;
+         default:
+            sprintf(hstr, "Unknown type");
+      }
+      return hstr;
+   }
+}
+
+
+//----- read PDO assign structure ----------------------------------------------
+
+int si_PDOassign(uint16 slave, uint16 PDOassign, int mapoffset, int bitoffset)
+{
+    uint16 idxloop, nidx, subidxloop, rdat, idx, subidx;
+    uint8 subcnt;
+    int wkc, bsize = 0, rdl;
+    int32 rdat2;
+    uint8 bitlen, obj_subidx;
+    uint16 obj_idx;
+    int abs_offset, abs_bit;
+
+    rdl = sizeof(rdat); rdat = 0;
+    															// read PDO assign subindex 0 (= number of PDO's)
+    wkc = ec_SDOread(slave, PDOassign, 0x00, FALSE, &rdl, &rdat, EC_TIMEOUTRXM);
+    rdat = etohs(rdat);
+
+    if ((wkc > 0) && (rdat > 0))							    // positive result from slave?
+    {
+        nidx = rdat;											// number of available sub indexes 
+        bsize = 0;
+        
+        for (idxloop = 1; idxloop <= nidx; idxloop++)			// read all PDO's 
+        {
+            rdl = sizeof(rdat); rdat = 0;
+            													// read PDO assign 
+            wkc = ec_SDOread(slave, PDOassign, (uint8)idxloop, FALSE, &rdl, &rdat, EC_TIMEOUTRXM);
+            										
+            idx = etohl(rdat);									// result is index of PDO
+            if (idx > 0)
+            {
+                rdl = sizeof(subcnt); subcnt = 0;
+                												// read number of subindexes of PDO
+                wkc = ec_SDOread(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 = ec_SDOread(slave, idx, (uint8)subidxloop, FALSE, &rdl, &rdat2, EC_TIMEOUTRXM);
+                    rdat2 = etohl(rdat2);
+                    
+                    bitlen = LO_BYTE(rdat2);					// extract bitlength of SDO
+                    bsize += bitlen;
+                    obj_idx = (uint16)(rdat2 >> 16);
+                    obj_subidx = (uint8)((rdat2 >> 8) & 0x000000ff);
+                    abs_offset = mapoffset + (bitoffset / 8);
+                    abs_bit = bitoffset % 8;
+                    ODlist.Slave = slave;
+                    ODlist.Index[0] = obj_idx;
+                    OElist.Entries = 0;
+                    wkc = 0;
+                    											
+                    if(obj_idx || obj_subidx)					// read object entry from dictionary if not a filler (0x0000:0x00)
+                        wkc = ec_readOEsingle(0, obj_subidx, &ODlist, &OElist);
+                    printf("  [0x%4.4X.%1d] 0x%4.4X:0x%2.2X 0x%2.2X", abs_offset, abs_bit, obj_idx, obj_subidx, bitlen);
+                    
+                    if((wkc > 0) && OElist.Entries)
+                    {
+                        printf(" %-12s %s\n", dtype2string(OElist.DataType[obj_subidx]), OElist.Name[obj_subidx]);
+                    }
+                    else
+                        printf("\n");
+                    bitoffset += bitlen;
+                };
+            };
+        };
+    };
+
+    return bsize;											    // return total found bitlength (PDO)
+}
+
+
+//---- PDO mapping according to CoE --------------------------------------------
+
+int si_map_sdo(int slave)
+{
+    int wkc, rdl;
+    int retVal = 0;
+    uint8 nSM, iSM, tSM;
+    int Tsize, outputs_bo, inputs_bo;
+    uint8 SMt_bug_add;
+
+    printf("PDO mapping according to CoE :\n\n");
+    SMt_bug_add = 0;
+    outputs_bo = 0;
+    inputs_bo = 0;
+    rdl = sizeof(nSM); nSM = 0;
+    														// read SyncManager Communication Type object count
+    wkc = ec_SDOread(slave, ECT_SDO_SMCOMMTYPE, 0x00, FALSE, &rdl, &nSM, EC_TIMEOUTRXM);
+    														
+    if ((wkc > 0) && (nSM > 2))								// positive result from slave ? 
+    {   
+        nSM--;               								// make nSM equal to number of defined SM 
+        
+        if (nSM > EC_MAXSM)									// limit to maximum number of SM defined,
+            nSM = EC_MAXSM;									// if true the slave can't be configured 
+        
+        for (iSM = 2 ; iSM <= nSM ; iSM++)					// iterate for every SM type defined 		
+        {
+            rdl = sizeof(tSM); tSM = 0;
+            												// read SyncManager Communication Type 
+            wkc = ec_SDOread(slave, ECT_SDO_SMCOMMTYPE, iSM + 1, FALSE, &rdl, &tSM, EC_TIMEOUTRXM);
+            if (wkc > 0)
+            {
+                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
+                    printf("Activated SM type workaround, possible incorrect mapping.\n");
+                }
+                
+                if(tSM)										// only add if SMt > 0
+                    tSM += SMt_bug_add; 					
+
+                if (tSM == 3) 								// outputs
+                {											// read the assign RXPDO
+                 
+                    printf("  SM%1d outputs\n        addr b   index: sub bitl data_type    name\n", iSM);
+                    Tsize = si_PDOassign(slave, ECT_SDO_PDOASSIGN + iSM, (int)(ec_slave[slave].outputs - (uint8 *)&IOmap[0]), outputs_bo );
+                    outputs_bo += Tsize;
+                }
+                
+                if (tSM == 4) 								// inputs
+                {											// read the assign TXPDO 
+                    
+                    printf("  SM%1d inputs\n     addr b   index: sub bitl data_type    name\n", iSM);
+                    Tsize = si_PDOassign(slave, ECT_SDO_PDOASSIGN + iSM, (int)(ec_slave[slave].inputs - (uint8 *)&IOmap[0]), inputs_bo );
+                    inputs_bo += Tsize;
+                }
+            }
+        }
+    }
+
+    if ((outputs_bo > 0) || (inputs_bo > 0))				// found some I/O bits? 
+        retVal = 1;
+    return retVal;
+}
+
+
+//---- CoE objects description -------------------------------------------------
+
+void si_sdo(int cnt)
+{
+    int i, j;
+
+    ODlist.Entries = 0;
+    memset(&ODlist, 0, sizeof(ODlist));
+    if( ec_readODlist(cnt, &ODlist))
+    {
+        printf(" CoE Object Description found, %d entries.\n",ODlist.Entries);
+        for( i = 0 ; i < ODlist.Entries ; i++)
+        {
+            ec_readODdescription(i, &ODlist);
+            while(EcatError) printf("%s", ec_elist2string());
+            printf(" Index: %4.4x Datatype: %4.4x Objectcode: %2.2x Name: %s\n",
+                ODlist.Index[i], ODlist.DataType[i], ODlist.ObjectCode[i], ODlist.Name[i]);
+            memset(&OElist, 0, sizeof(OElist));
+            ec_readOE(i, &ODlist, &OElist);
+            while(EcatError) printf("%s", ec_elist2string());
+            for( j = 0 ; j < ODlist.MaxSub[i]+1 ; j++)
+            {
+                if ((OElist.DataType[j] > 0) && (OElist.BitLength[j] > 0))
+                {
+                    printf("  Sub: %2.2x Datatype: %4.4x Bitlength: %4.4x Obj.access: %4.4x Name: %s\n",
+                        j, OElist.DataType[j], OElist.BitLength[j], OElist.ObjAccess[j], OElist.Name[j]);
+                    if ((OElist.ObjAccess[j] & 0x0007))
+                    {
+                        printf("          Value :%s\n", SDO2string(cnt, ODlist.Index[i], j, OElist.DataType[j]));
+                    }
+                }
+            }
+        }
+    }
+    else
+    {
+        while(EcatError) printf("%s", ec_elist2string());
+    }
+}
+
+
+//---- PDO mapping according to SII --------------------------------------------
+
+int si_map_sii(int slave)
+{
+    int retVal = 0;
+    int Tsize, outputs_bo, inputs_bo;
+
+    printf("\nPDO mapping according to SII :\n\n");
+
+    outputs_bo = 0;
+    inputs_bo = 0;
+    														// read the assign RXPDOs 
+    Tsize = si_siiPDO(slave, 1, (int)(ec_slave[slave].outputs - (uint8*)&IOmap), outputs_bo );
+    outputs_bo += Tsize;
+    														// read the assign TXPDOs 
+    Tsize = si_siiPDO(slave, 0, (int)(ec_slave[slave].inputs - (uint8*)&IOmap), inputs_bo );
+    inputs_bo += Tsize;
+    
+    if ((outputs_bo > 0) || (inputs_bo > 0))				// found some I/O bits ? 
+        retVal = 1;
+    return retVal;
+}
+
+
+//------------------------------------------------------------------------------
+
+int si_siiPDO(uint16 slave, uint8 t, int mapoffset, int bitoffset)
+{
+    uint16 a , w, c, e, er, Size;
+    uint8 eectl;
+    uint16 obj_idx;
+    uint8 obj_subidx;
+    uint8 obj_name;
+    uint8 obj_datatype;
+    uint8 bitlen;
+    int totalsize;
+    ec_eepromPDOt eepPDO;
+    ec_eepromPDOt *PDO;
+    int abs_offset, abs_bit;
+    char str_name[EC_MAXNAME + 1];
+
+    eectl = ec_slave[slave].eep_pdi;
+    Size = 0;
+    totalsize = 0;
+    PDO = &eepPDO;
+    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 = ec_siifind(slave, ECT_SII_PDO + t);
+  
+    if (PDO->Startpos > 0)
+    {
+        a = PDO->Startpos;
+        w = ec_siigetbyte(slave, a++);
+        w += (ec_siigetbyte(slave, a++) << 8);
+        PDO->Length = w;
+        c = 1;
+        
+        do													// traverse through all PDOs			
+        {
+            PDO->nPDO++;
+            PDO->Index[PDO->nPDO] = ec_siigetbyte(slave, a++);
+            PDO->Index[PDO->nPDO] += (ec_siigetbyte(slave, a++) << 8);
+            PDO->BitSize[PDO->nPDO] = 0;
+            c++;
+            
+            e = ec_siigetbyte(slave, a++);					// number of entries in PDO 
+            PDO->SyncM[PDO->nPDO] = ec_siigetbyte(slave, a++);
+            a++;
+            obj_name = ec_siigetbyte(slave, a++);
+            a += 2;
+            c += 2;
+            
+            if (PDO->SyncM[PDO->nPDO] < EC_MAXSM) 			// active and in range SM? 
+            {
+                str_name[0] = 0;
+                if(obj_name)
+                	ec_siistring(str_name, slave, obj_name);
+                	
+                if (t)
+                  	printf("  SM%1d RXPDO 0x%4.4X %s\n", PDO->SyncM[PDO->nPDO], PDO->Index[PDO->nPDO], str_name);
+                else
+                  	printf("  SM%1d TXPDO 0x%4.4X %s\n", PDO->SyncM[PDO->nPDO], PDO->Index[PDO->nPDO], str_name);
+                
+                printf("     addr b   index: sub  bitl data_type    name\n");
+                
+                for (er = 1; er <= e; er++)					// read all entries defined in PDO 
+                {
+                    c += 4;
+                    obj_idx = ec_siigetbyte(slave, a++);
+                    obj_idx += (ec_siigetbyte(slave, a++) << 8);
+                    obj_subidx = ec_siigetbyte(slave, a++);
+                    obj_name = ec_siigetbyte(slave, a++);
+                    obj_datatype = ec_siigetbyte(slave, a++);
+                    bitlen = ec_siigetbyte(slave, a++);
+                    abs_offset = mapoffset + (bitoffset / 8);
+                    abs_bit = bitoffset % 8;
+
+                    PDO->BitSize[PDO->nPDO] += bitlen;
+                    a += 2;
+
+                    if(obj_idx || obj_subidx)				// skip entry if filler (0x0000:0x00)
+                    {
+                       str_name[0] = 0;
+                       if(obj_name)
+                          ec_siistring(str_name, slave, obj_name);
+
+                       printf("  [0x%4.4X.%1d] 0x%4.4X:0x%2.2X 0x%2.2X", abs_offset, abs_bit, obj_idx, obj_subidx, bitlen);
+                       printf(" %-12s %s\n", dtype2string(obj_datatype), str_name);
+                    }
+                    bitoffset += bitlen;
+                    totalsize += bitlen;
+                }
+                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++;
+            }
+            												// limit number of PDO entries in buffer 
+            if (PDO->nPDO >= (EC_MAXEEPDO - 1)) c = PDO->Length; 
+        }
+        while (c < PDO->Length);
+    }
+                                        
+    if (eectl) 
+    	ec_eeprom2pdi(slave); 								// if eeprom control was previously pdi then restore 
+    
+    return totalsize;
+}
+
+
+//------------------------------------------------------------------------------
+
+bool network_scanning(void)
+
+{
+    int expectedWKC;
+	int cnt, i, j, nSM;
+   	uint16 ssigen;	     	
+
+    if ( ec_config(FALSE, &IOmap) > 0 )  					// find and configure the slaves	
+  	{
+     	ec_configdc();
+     	while(EcatError) printf("%s", ec_elist2string());
+     	
+     	printf("%d slaves found and configured.\n",ec_slavecount);
+ 	 	TFT.printf("%d slaves found and configured.\n\n",ec_slavecount);	     	
+     	
+     	expectedWKC = (ec_group[0].outputsWKC * 2) + ec_group[0].inputsWKC;
+     	printf("Calculated workcounter %d\n", expectedWKC);
+     														// wait for all slaves to reach SAFE_OP state 
+     	ec_statecheck(0, EC_STATE_SAFE_OP,  EC_TIMEOUTSTATE * 3);
+     	
+     	if (ec_slave[0].state != EC_STATE_SAFE_OP )
+     	{
+        	printf("Not all slaves reached safe operational state.\n");
+        	ec_readstate();
+        	for(i = 1; i<=ec_slavecount ; i++)
+        	{
+           		if(ec_slave[i].state != EC_STATE_SAFE_OP)
+           		{
+              		printf("Slave %d State=%2x StatusCode=%4x : %s\n",
+                	i, ec_slave[i].state, ec_slave[i].ALstatuscode, ec_ALstatuscode2string(ec_slave[i].ALstatuscode));
+           		}
+         	}  		
+        }
+
+		ec_readstate();			
+				        				        
+		for( cnt = 1 ; cnt <= ec_slavecount ; cnt++)
+		{  
+            printf("\nSlave:%d -------------------------------------------------\nName:%s\n Output size: %dbits\n Input size: %dbits\n State: %d\n Delay: %d[ns]\n Has DC: %d\n",
+                  	cnt, ec_slave[cnt].name, ec_slave[cnt].Obits, ec_slave[cnt].Ibits,
+                  	ec_slave[cnt].state, ec_slave[cnt].pdelay, ec_slave[cnt].hasdc);         
+
+			int Line = cnt-1; 
+
+			TFT.foreground(Green);							// print Name, Manufacturer, ID and Revision
+			TFT.locate(0, 12*(((Line % 6)*3)+3));			// of the slaves found on the TFT
+            TFT.printf("Slave %d ", cnt); 					//
+			TFT.foreground(Yellow);							//	
+															//
+			TFT.foreground(Magenta);						//
+			TFT.locate(60, 12*(((Line % 6)*3)+3));			//	
+            TFT.printf("Name"); 							//
+            TFT.foreground(Yellow);							//
+			TFT.locate(104, 12*(((Line % 6)*3)+3));         //  	 
+            TFT.printf("%.14s", ec_slave[cnt].name);  		//	             
+                											//
+			TFT.foreground(Magenta); 						//
+			TFT.locate(220, 12*(((Line % 6)*3)+3));			//  	                    
+            TFT.printf("Man");								//
+            TFT.foreground(Yellow);  						//
+			TFT.locate(250, 12*(((Line % 6)*3)+3));	        //      
+            TFT.printf("%8.8X", (int)ec_slave[cnt].eep_man);//
+               												//		
+			TFT.foreground(Magenta);   						//
+			TFT.locate(60, 12*(((Line % 6)*3)+4));			//                    
+            TFT.printf("ID");            					//
+            TFT.foreground(Yellow);							//
+			TFT.locate(104, 12*(((Line % 6)*3)+4));         //                 
+            TFT.printf("%8.8X", (int)ec_slave[cnt].eep_id); //       
+ 															//
+			TFT.foreground(Magenta);   						//
+			TFT.locate(220, 12*(((Line % 6)*3)+4));			//                    
+            TFT.printf("Rev ");            					//
+            TFT.foreground(Yellow);   						//
+			TFT.locate(250,  12*(((Line % 6)*3)+4));	    //                 
+            TFT.printf("%8.8X", (int)ec_slave[cnt].eep_rev);//    
+                                                             
+                          
+            if (ec_slave[cnt].hasdc) printf(" DCParentport:%d\n", ec_slave[cnt].parentport);
+            printf(" Activeports:%d.%d.%d.%d\n", (ec_slave[cnt].activeports & 0x01) > 0 ,
+                                         (ec_slave[cnt].activeports & 0x02) > 0 ,
+                                         (ec_slave[cnt].activeports & 0x04) > 0 ,
+                                         (ec_slave[cnt].activeports & 0x08) > 0 );
+                                         
+            printf(" Configured address: %4.4X\n", ec_slave[cnt].configadr);
+        	printf(" Outputs address: %X\n", ec_slave[cnt].outputs);
+    		printf(" Inputs address: %X\n", ec_slave[cnt].inputs);            
+            
+            printf(" Man: %8.8x ID: %8.8x Rev: %8.8x\n", (int)ec_slave[cnt].eep_man, (int)ec_slave[cnt].eep_id, (int)ec_slave[cnt].eep_rev);
+            for(nSM = 0 ; nSM < EC_MAXSM ; nSM++)
+            {
+               if(ec_slave[cnt].SM[nSM].StartAddr > 0)
+                  printf(" SM%1d A:%4.4x L:%4d F:%8.8x Type:%d\n",nSM, ec_slave[cnt].SM[nSM].StartAddr, ec_slave[cnt].SM[nSM].SMlength,
+                         (int)ec_slave[cnt].SM[nSM].SMflags, ec_slave[cnt].SMtype[nSM]);
+            }
+            for(j = 0 ; j < ec_slave[cnt].FMMUunused ; j++)
+            {
+               printf(" FMMU%1d Ls:%8.8x Ll:%4d Lsb:%d Leb:%d Ps:%4.4x Psb:%d Ty:%2.2x Act:%2.2x\n", j,
+                       (int)ec_slave[cnt].FMMU[j].LogStart, ec_slave[cnt].FMMU[j].LogLength, ec_slave[cnt].FMMU[j].LogStartbit,
+                       ec_slave[cnt].FMMU[j].LogEndbit, ec_slave[cnt].FMMU[j].PhysStart, ec_slave[cnt].FMMU[j].PhysStartBit,
+                       ec_slave[cnt].FMMU[j].FMMUtype, ec_slave[cnt].FMMU[j].FMMUactive);
+            }
+            printf(" FMMUfunc 0:%d 1:%d 2:%d 3:%d\n",
+                     ec_slave[cnt].FMMU0func, ec_slave[cnt].FMMU1func, ec_slave[cnt].FMMU2func, ec_slave[cnt].FMMU3func);
+            printf(" MBX length wr: %d rd: %d MBX protocols : %2.2x\n", ec_slave[cnt].mbx_l, ec_slave[cnt].mbx_rl, ec_slave[cnt].mbx_proto);
+            ssigen = ec_siifind(cnt, ECT_SII_GENERAL);
+            	
+            if (ssigen)										 // SII general section 
+            {
+               ec_slave[cnt].CoEdetails = ec_siigetbyte(cnt, ssigen + 0x07);
+               ec_slave[cnt].FoEdetails = ec_siigetbyte(cnt, ssigen + 0x08);
+               ec_slave[cnt].EoEdetails = ec_siigetbyte(cnt, ssigen + 0x09);
+               ec_slave[cnt].SoEdetails = ec_siigetbyte(cnt, ssigen + 0x0a);
+               if((ec_siigetbyte(cnt, ssigen + 0x0d) & 0x02) > 0)
+               {
+                  ec_slave[cnt].blockLRW = 1;
+                  ec_slave[0].blockLRW++;
+               }
+               ec_slave[cnt].Ebuscurrent = ec_siigetbyte(cnt, ssigen + 0x0e);
+               ec_slave[cnt].Ebuscurrent += ec_siigetbyte(cnt, ssigen + 0x0f) << 8;
+               ec_slave[0].Ebuscurrent += ec_slave[cnt].Ebuscurrent;
+    		}
+            printf(" CoE details: %2.2x FoE details: %2.2x EoE details: %2.2x SoE details: %2.2x\n",
+                    ec_slave[cnt].CoEdetails, ec_slave[cnt].FoEdetails, ec_slave[cnt].EoEdetails, ec_slave[cnt].SoEdetails);
+            printf(" Ebus current: %d[mA]\n only LRD/LWR:%d\n",
+                    ec_slave[cnt].Ebuscurrent, ec_slave[cnt].blockLRW);
+                    
+            if ((ec_slave[cnt].mbx_proto & ECT_MBXPROT_COE) && printSDO)
+            	si_sdo(cnt);
+                    
+            if(printMAP)
+            {
+        		if (ec_slave[cnt].mbx_proto & ECT_MBXPROT_COE)   
+                	si_map_sdo(cnt);
+                else
+                	si_map_sii(cnt);
+            }
+		}		
+		return 1;
+	}
+	
+	else
+	{	
+  	//	ec_close();		//******// 
+  		
+  		return 0;											// no slaves found
+	}	
+}
+
+
+
+
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/soem_start.h	Tue Jun 11 10:19:08 2019 +0000
@@ -0,0 +1,9 @@
+
+#ifndef soem_start_H
+#define soem_start_H
+
+
+bool network_scanning(void);
+
+
+#endif