]> git.feebdaed.xyz Git - 0xmirror/SOEM.git/commitdiff
Add ERIKA Enterprise RTOS support
authorClaudio Scordino <claudio@evidence.eu.com>
Mon, 1 Apr 2019 13:11:01 +0000 (15:11 +0200)
committernakarlsson <nakarlsson@users.noreply.github.com>
Fri, 10 May 2019 06:43:18 +0000 (08:43 +0200)
This patch adds support to SOEM for the ERIKA Enterprise RTOS
(erika-enterprise.com).

Current requirements for running SOEM on ERIKA RTOS:
 - x86-64 platform with 2+ cores
 - Xen hypervisor
 - Intel i210 PCIe Ethernet controller

Signed-off-by: Claudio Scordino <claudio@evidence.eu.com>
Signed-off-by: Luca Cuomo <l.cuomo@evidence.eu.com>
README.md
osal/erika/osal.c [new file with mode: 0644]
osal/erika/osal_defs.h [new file with mode: 0644]
oshw/erika/nicdrv.c [new file with mode: 0644]
oshw/erika/nicdrv.h [new file with mode: 0644]
oshw/erika/oshw.c [new file with mode: 0644]
oshw/erika/oshw.h [new file with mode: 0644]

index 84cc1524c20bf8abdde93ec2e2f16ebfdfc49613..875df1358ae3e3584aaf093a6cb7f2d0963f069f 100644 (file)
--- a/README.md
+++ b/README.md
@@ -29,6 +29,11 @@ Linux
    * `cmake ..`
    * `make`
 
+ERIKA Enterprise RTOS
+---------------------
+
+ * Refer to http://www.erika-enterprise.com/wiki/index.php?title=EtherCAT_Master
+
 Documentation
 -------------
 
diff --git a/osal/erika/osal.c b/osal/erika/osal.c
new file mode 100644 (file)
index 0000000..a921c5f
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ * Licensed under the GNU General Public License version 2 with exceptions. See
+ * LICENSE file in the project root for full license information
+ */
+
+#include <time.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <osal.h>
+
+#include "ee_x86_64_tsc.h"
+
+#define USECS_PER_SEC     1000000
+#define NSECS_PER_SEC     1000000000
+
+uint64_t osEE_x86_64_tsc_read(void);
+
+void ee_usleep(uint32 usec);
+
+inline int osal_usleep (uint32 usec)
+{
+       ee_usleep(usec);
+       return 0;
+}
+
+int osal_gettimeofday(struct timeval *tv, struct timezone *tz)
+{
+       uint64_t time = osEE_x86_64_tsc_read();
+       tv->tv_sec = time/NSECS_PER_SEC;
+       tv->tv_sec += 946684800UL;  /* EtherCAT uses 2000-01-01 as epoch start */
+       tv->tv_usec = (time%NSECS_PER_SEC)/1000;
+       return 0;
+}
+
+ec_timet osal_current_time(void)
+{
+       struct timeval current_time;
+       ec_timet ret;
+
+       osal_gettimeofday(&current_time, 0);
+       ret.sec = current_time.tv_sec;
+       ret.usec = current_time.tv_usec;
+       return ret;
+}
+
+void osal_time_diff(ec_timet *start, ec_timet *end, ec_timet *diff)
+{
+       if (end->usec < start->usec) {
+               diff->sec = end->sec - start->sec - 1;
+               diff->usec = end->usec + USECS_PER_SEC - start->usec;
+       } else {
+               diff->sec = end->sec - start->sec;
+               diff->usec = end->usec - start->usec;
+       }
+}
+
+void osal_timer_start(osal_timert *self, uint32 timeout_usec)
+{
+       struct timeval start_time;
+       struct timeval timeout;
+       struct timeval stop_time;
+
+       osal_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;
+
+       osal_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, <);
+/*     OSEE_PRINT("current: %d:%d -- expire: %d:%d -- result: %d\n", */
+/*                     current_time.tv_sec, */
+/*                     current_time.tv_usec, */
+/*                     stop_time.tv_sec, */
+/*                     stop_time.tv_usec, */
+/*                     is_not_yet_expired); */
+
+       return is_not_yet_expired == FALSE;
+}
+
+void *osal_malloc(size_t size)
+{
+       return malloc(size);
+}
+
+void osal_free(void *ptr)
+{
+       free(ptr);
+}
+
diff --git a/osal/erika/osal_defs.h b/osal/erika/osal_defs.h
new file mode 100644 (file)
index 0000000..99d0ac3
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * 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
+
+#include <sys/time.h>
+#include <stdlib.h>
+#include <ee.h>
+
+// define if debug print is needed
+#define EC_DEBUG
+
+#ifdef EC_DEBUG
+#define EC_PRINT OSEE_PRINT
+#else
+#define EC_PRINT(...) do {} while (0)
+#endif
+
+#ifndef PACKED
+#define PACKED_BEGIN
+#define PACKED  __attribute__((__packed__))
+#define PACKED_END
+#endif
+
+int osal_gettimeofday(struct timeval *tv, struct timezone *tz);
+void *osal_malloc(size_t size);
+void osal_free(void *ptr);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/oshw/erika/nicdrv.c b/oshw/erika/nicdrv.c
new file mode 100644 (file)
index 0000000..bfe4c7c
--- /dev/null
@@ -0,0 +1,561 @@
+/*
+ * 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 if fully transparent for the higher layers.
+ */
+
+#include <sys/time.h>
+#include <time.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <string.h>
+#include <assert.h>
+
+#include "oshw.h"
+#include "osal.h"
+#include "nicdrv.h"
+#include "ee.h"
+#include "intel_i210.h"
+#include "ee_x86_64_tsc.h"
+
+/** 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] = { 0x0201, 0x0101, 0x0101 };
+/** Secondary source MAC address used for EtherCAT. */
+const uint16 secMAC[3] = { 0x0604, 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]
+
+void ee_port_lock(void);
+void ee_port_unlock(void);
+
+static inline 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 d;
+       struct eth_device *dev;
+       OSEE_PRINT("ecx_setupnic() searching %s...\n", ifname);
+
+       for (d = 0;; ++d) {
+               dev = eth_get_device(d);
+
+               if (dev == NULL)
+                       break; // ERROR: device not found
+
+               if (!strncmp(dev->name, ifname, MAX_DEVICE_NAME)){
+                       // Device found
+                       int i;
+
+                       eth_setup_device(d, 1, 100);
+                       port->dev_id            = d;
+
+                       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]));
+
+                       /* 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));
+
+                       break; // device found
+               }
+       }
+
+       return (dev != NULL);
+}
+
+/** Close sockets used
+ * @param[in] port        = port context struct
+ * @return 0
+ */
+inline int ecx_closenic(ecx_portt *port)
+{
+       // Nothing to do
+       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 = 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 = 0;
+
+       ee_port_lock();
+
+       idx = port->lastidx + 1;
+       /* index can't be larger than buffer array */
+       if (idx >= EC_MAXBUF)
+               idx = 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;
+
+       ee_port_unlock();
+
+       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;
+       ec_stackT *stack;
+
+       if (!stacknumber)
+               stack = &(port->stack);
+       else
+               stack = &(port->redport->stack);
+       lp = (*stack->txbuflength)[idx];
+       (*stack->rxbufstat)[idx] = EC_BUF_TX;
+
+       eth_send_packet(port->dev_id, (*stack->txbuf)[idx], lp, 1);
+
+       return 1;
+}
+
+/** 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) {
+               ee_port_lock();
+               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 */
+               port->redport->rxbufstat[idx] = EC_BUF_TX;
+               eth_send_packet(port->dev_id, &(port->txbuf2), port->txbuflength2, 1);
+               ee_port_unlock();
+       }
+
+       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 = eth_receive_packet(port->dev_id, (*stack->tempbuf), lp, 1, 0);
+       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;
+       int     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 {
+               ee_port_lock();
+               /* non blocking call to retrieve frame from socket */
+
+
+               while (1) {
+                       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);
+                                               break;
+                                       } else 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);
+                                               break;
+                                               } else {
+                                               OSEE_PRINT("ecx_inframe(): WARNING: strange things happened\n");
+                                               /* strange things happened */
+                                               }
+                               } else {
+                                       OSEE_PRINT("ecx_inframe(): WARNING it is NOT an EtherCAT frame!\n");
+                               }
+                       } else {
+                               // WARNING: no messages received.
+                               break;
+                       }
+               }
+
+               ee_port_unlock();
+
+       }
+
+       /* 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)
+{
+       osal_timert timer2;
+       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));
+       /* 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)) ) {
+                       /* 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 (&timer2, EC_TIMEOUTRET);
+                       /* 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(&timer2));
+                       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 timer1, timer2;
+
+       osal_timer_start (&timer1, timeout);
+       do  {
+               /* tx frame on primary and if in redundant mode a dummy on secondary */
+               ecx_outframe_red(port, idx);
+               if (timeout < EC_TIMEOUTRET) {
+                       osal_timer_start (&timer2, timeout);
+               } else {
+                       /* normally use partial timeout for rx */
+                       osal_timer_start (&timer2, EC_TIMEOUTRET);
+               }
+               /* get frame from primary or if in redundant mode possibly
+                  from secondary */
+               wkc = ecx_waitinframe_red(port, idx, &timer2);
+               /* wait for answer with WKC>=0 or otherwise retry until timeout */
+       } while ((wkc <= EC_NOFRAME) && !osal_timer_is_expired (&timer1));
+
+
+       return wkc;
+}
+
+
+#ifdef EC_VER1
+int ec_setupnic(const char *ifname, int secondary)
+{
+   return ecx_setupnic(&ecx_port, ifname, secondary);
+}
+
+int ec_closenic(void)
+{
+   return ecx_closenic(&ecx_port);
+}
+
+int ec_getindex(void)
+{
+   return ecx_getindex(&ecx_port);
+}
+
+void ec_setbufstat(int idx, int bufstat)
+{
+   ecx_setbufstat(&ecx_port, idx, bufstat);
+}
+
+int ec_outframe(int idx, int stacknumber)
+{
+   return ecx_outframe(&ecx_port, idx, stacknumber);
+}
+
+int ec_outframe_red(int idx)
+{
+   return ecx_outframe_red(&ecx_port, idx);
+}
+
+int ec_inframe(int idx, int stacknumber)
+{
+   return ecx_inframe(&ecx_port, idx, stacknumber);
+}
+
+int ec_waitinframe(int idx, int timeout)
+{
+   return ecx_waitinframe(&ecx_port, idx, timeout);
+}
+
+int ec_srconfirm(int idx, int timeout)
+{
+   return ecx_srconfirm(&ecx_port, idx, timeout);
+}
+#endif
diff --git a/oshw/erika/nicdrv.h b/oshw/erika/nicdrv.h
new file mode 100644 (file)
index 0000000..2398040
--- /dev/null
@@ -0,0 +1,122 @@
+/*
+ * 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_
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+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;
+
+       /** Device id in the device pool */
+       int dev_id;
+
+       // TODO: add mutex support
+} 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 sock);
+int ec_outframe_red(int idx);
+int ec_waitinframe(int idx, int timeout);
+int ec_srconfirm(int idx,int timeout);
+int ec_inframe(int idx, int stacknumber);
+#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 sock);
+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);
+
+int ecx_inframe(ecx_portt *port, int idx, int stacknumber);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/oshw/erika/oshw.c b/oshw/erika/oshw.c
new file mode 100644 (file)
index 0000000..256c75c
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * Licensed under the GNU General Public License version 2 with exceptions. See
+ * LICENSE file in the project root for full license information
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <inttypes.h>
+
+#include "oshw.h"
+#include "intel_i210.h"
+#include "ethercat.h"
+
+#if !defined(__gnu_linux__)
+#include <machine/endian.h>
+#else
+#include <endian.h>
+#define __htons(x) htobe16(x)
+#define __ntohs(x) be16toh(x)
+#endif
+
+ec_adaptert    adapters [DEVS_MAX_NB];
+
+/**
+ * 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.
+ */
+inline uint16 oshw_htons(uint16 host)
+{
+       // __htons() is provided by the bare-metal x86 compiler
+       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.
+ */
+inline uint16 oshw_ntohs(uint16 network)
+{
+       // __ntohs() is provided by the bare-metal x86 compiler
+       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 = NULL;
+       if (eth_discover_devices() >= 0) {
+               for (int i = 0;; ++i) {
+                       struct eth_device *dev = eth_get_device(i);
+                       if (dev == NULL) {
+                               adapters[i-1].next = NULL;
+                               break;
+                       }
+                       strncpy(adapters[i].name, dev->name, MAX_DEVICE_NAME);
+                       adapters[i].next = &adapters[i+1];
+               }
+               ret = &(adapters[0]);
+       }
+       return ret;
+}
+
+/** 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)
+{
+}
+
+extern int ec_slavecount;
+
diff --git a/oshw/erika/oshw.h b/oshw/erika/oshw.h
new file mode 100644 (file)
index 0000000..03a83c3
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * 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 _oshw_
+#define _oshw_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "ethercattype.h"
+#include "nicdrv.h"
+#include "ethercatmain.h"
+
+uint16 oshw_htons(uint16 hostshort);
+uint16 oshw_ntohs(uint16 networkshort);
+ec_adaptert* oshw_find_adapters(void);
+void oshw_free_adapters(ec_adaptert * adapter);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif