Added circle implementation.

pull/289/head
Davide Pippa 2019-03-29 10:25:37 +01:00
parent a75da437c5
commit b0f8ea5a24
10 changed files with 1107 additions and 4 deletions

View File

@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 2.8.4)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules")
project(SOEM C)
project(SOEM C CXX)
if (CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
# Default to installing in SOEM source directory
@ -43,13 +43,23 @@ elseif(${CMAKE_SYSTEM_NAME} MATCHES "rtems")
set(OS "rtems")
set(SOEM_LIB_INSTALL_DIR ${LIB_DIR})
set(BUILD_TESTS FALSE)
elseif(${CMAKE_SYSTEM_NAME} MATCHES "circle")
set(OS "circle")
set(CMAKE_C_COMPILER arm-none-eabi-gcc)
set(CMAKE_CXX_COMPILER arm-none-eabi-g++)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DAARCH=32 -march=armv8-a -mtune=cortex-a53 -marm -mfpu=neon-fp-armv8 -mfloat-abi=hard")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DAARCH=32 -march=armv8-a -mtune=cortex-a53 -marm -mfpu=neon-fp-armv8 -mfloat-abi=hard")
include_directories(./libs/circle-stdlib/libs/circle/include)
include_directories(./libs/circle-stdlib/libs/circle-newlib/include)
include_directories(./libs/circle-stdlib/libs/circle-newlib/newlib/libc/include)
include_directories(./libs/circle-stdlib/libs/circle-newlib/newlib/libm/include)
endif()
message("OS is ${OS}")
file(GLOB SOEM_SOURCES soem/*.c)
file(GLOB OSAL_SOURCES osal/${OS}/*.c)
file(GLOB OSHW_SOURCES oshw/${OS}/*.c)
file(GLOB OSAL_SOURCES osal/${OS}/*.c osal/${OS}/*.cpp)
file(GLOB OSHW_SOURCES oshw/${OS}/*.c oshw/${OS}/*.cpp)
file(GLOB SOEM_HEADERS soem/*.h)
file(GLOB OSAL_HEADERS osal/osal.h osal/${OS}/*.h)
@ -76,7 +86,9 @@ install(FILES
${OSHW_HEADERS}
DESTINATION ${SOEM_INCLUDE_INSTALL_DIR})
if(BUILD_TESTS)
if(${OS} MATCHES "circle")
elseif(BUILD_TESTS)
add_subdirectory(test/linux/slaveinfo)
add_subdirectory(test/linux/eepromtool)
add_subdirectory(test/linux/simple_test)

View File

@ -0,0 +1,137 @@
#include "cethercatmaster.h"
#include <circle/sched/scheduler.h>
#include <circle/synchronize.h>
#include <circle/timer.h>
#include <circle/types.h>
#include <circle/alloc.h>
#include <assert.h>
#include <osal.h>
#include <time.h>
CEtherCATMaster *_instance = 0L;
CEtherCATMaster::CEtherCATMaster(bool use_scheduler)
: _use_scheduler(use_scheduler)
{
// Cannot create more than one instance
assert(_instance == 0L);
_instance = this;
}
bool CEtherCATMaster::IsSchedulerFriendly() const
{
return _use_scheduler;
}
bool CEtherCATMaster::IsActive()
{
return (_instance != 0L);
}
CEtherCATMaster *CEtherCATMaster::Get()
{
assert(_instance != 0L);
return _instance;
}
int osal_usleep(uint32 usec)
{
if (!CScheduler::IsActive()) {
// Busy-waiting sleep
CTimer::SimpleusDelay(usec);
} else if (!CEtherCATMaster::IsActive()) {
// Busy-waiting sleep
CTimer::SimpleusDelay(usec);
} else if (!CEtherCATMaster::Get()->IsSchedulerFriendly()) {
// Busy-waiting sleep
CTimer::SimpleusDelay(usec);
} else {
// Scheduler-friendly sleep
CScheduler::Get()->usSleep(usec);
}
return 0;
}
static long long get_monotonic_clock()
{
// Copied from circle's timer, using both words to get 64bit monotonic clock
InstructionSyncBarrier();
u32 nCNTPCTLow, nCNTPCTHigh;
asm volatile ("mrrc p15, 0, %0, %1, c14" : "=r" (nCNTPCTLow), "=r" (nCNTPCTHigh));
return (((long long)nCNTPCTHigh) << 32) | nCNTPCTLow;
}
int osal_gettimeofday(struct timeval *tv, struct timezone *tz)
{
long long usec = get_monotonic_clock();
tv->tv_sec = usec / 1000000LL;
tv->tv_usec = usec % 1000000LL;
return 0;
}
ec_timet osal_current_time(void)
{
struct timeval current_time;
ec_timet return_value;
osal_gettimeofday(&current_time, 0);
return_value.sec = current_time.tv_sec;
return_value.usec = current_time.tv_usec;
return return_value;
}
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 + 1000000 - 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)
{
long long stop_time_usec = get_monotonic_clock() + (long long)timeout_usec;
self->stop_time.sec = stop_time_usec / 1000000LL;
self->stop_time.usec = stop_time_usec % 1000000LL;
}
boolean osal_timer_is_expired(osal_timert * self)
{
long long now_usec = get_monotonic_clock();
long long stop_time_usec = self->stop_time.sec * 1000000LL + self->stop_time.usec;
return (now_usec >= stop_time_usec);
}
void *osal_malloc(size_t size)
{
// Use circle's malloc
return malloc(size);
}
void osal_free(void *ptr)
{
// Use circle's free
free(ptr);
}
int osal_thread_create(void *thandle, int stacksize, void *func, void *param)
{
// Not supported
return 0;
}
int osal_thread_create_rt(void *thandle, int stacksize, void *func, void *param)
{
// Not supported
return 0;
}

View File

@ -0,0 +1,19 @@
#ifndef CETHERCATMASTER_H
#define CETHERCATMASTER_H
class CEtherCATMaster
{
public:
CEtherCATMaster(bool use_scheduler);
bool IsSchedulerFriendly() const;
static bool IsActive();
static CEtherCATMaster* Get();
private:
bool _use_scheduler;
};
#endif // CETHERCATMASTER_H

View File

@ -0,0 +1,102 @@
#include <circle/sched/scheduler.h>
#include <circle/synchronize.h>
#include <circle/timer.h>
#include <circle/types.h>
#include <circle/alloc.h>
#include <assert.h>
#include <osal.h>
#include <time.h>
int osal_usleep(uint32 usec)
{
if (CScheduler::IsActive()) {
// Scheduler-friendly sleep
CScheduler::Get()->usSleep(usec);
} else {
// Busy-waiting sleep
CTimer::SimpleusDelay(usec);
}
return 0;
}
static long long get_monotonic_clock()
{
// Copied from circle's timer, using both words to get 64bit monotonic clock
InstructionSyncBarrier();
u32 nCNTPCTLow, nCNTPCTHigh;
asm volatile ("mrrc p15, 0, %0, %1, c14" : "=r" (nCNTPCTLow), "=r" (nCNTPCTHigh));
return (((long long)nCNTPCTHigh) << 32) | nCNTPCTLow;
}
int osal_gettimeofday(struct timeval *tv, struct timezone *tz)
{
long long usec = get_monotonic_clock();
tv->tv_sec = usec / 1000000LL;
tv->tv_usec = usec % 1000000LL;
return 0;
}
ec_timet osal_current_time(void)
{
struct timeval current_time;
ec_timet return_value;
osal_gettimeofday(&current_time, 0);
return_value.sec = current_time.tv_sec;
return_value.usec = current_time.tv_usec;
return return_value;
}
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 + 1000000 - 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)
{
long long stop_time_usec = get_monotonic_clock() + (long long)timeout_usec;
self->stop_time.sec = stop_time_usec / 1000000LL;
self->stop_time.usec = stop_time_usec % 1000000LL;
}
boolean osal_timer_is_expired(osal_timert * self)
{
long long now_usec = get_monotonic_clock();
long long stop_time_usec = self->stop_time.sec * 1000000LL + self->stop_time.usec;
return (now_usec >= stop_time_usec);
}
void *osal_malloc(size_t size)
{
// Use circle's malloc
return malloc(size);
}
void osal_free(void *ptr)
{
// Use circle's free
free(ptr);
}
int osal_thread_create(void *thandle, int stacksize, void *func, void *param)
{
// Not supported
return 0;
}
int osal_thread_create_rt(void *thandle, int stacksize, void *func, void *param)
{
// Not supported
return 0;
}

View File

@ -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
*/
#ifndef _osal_defs_
#define _osal_defs_
#include <circle/types.h>
#include <circle/macros.h>
#define _osal_defs_circle_
#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_BEGIN
#define PACKED_BEGIN
#endif
#ifndef PACKED_END
#define PACKED_END
#endif
#define OSAL_THREAD_HANDLE void *
#define OSAL_THREAD_FUNC void
#define OSAL_THREAD_FUNC_RT void
#ifdef __cplusplus
}
#endif
#endif

View File

@ -15,9 +15,12 @@ extern "C"
#include <stdint.h>
/* General types */
#ifndef _osal_defs_circle_
typedef uint8_t boolean;
#define TRUE 1
#define FALSE 0
#endif // _osal_defs_circle_
typedef int8_t int8;
typedef int16_t int16;
typedef int32_t int32;

View File

@ -0,0 +1,579 @@
/*
* 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 "osal.h"
#include "oshw.h"
#include "ethercattype.h"
#include <circle/devicenameservice.h>
#include <circle/usb/netdevice.h>
#include <string.h>
/** Redundancy modes */
enum
{
/** No redundancy, single NIC mode */
ECT_RED_NONE,
/** Double redundant NIC connecetion */
ECT_RED_DOUBLE
};
static void ecx_clear_rxbufstat(int *rxbufstat)
{
int i;
for(i = 0; i < EC_MAXBUF; i++)
{
rxbufstat[i] = EC_BUF_EMPTY;
}
}
/** 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]
/** 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;
CNetDevice *nd = reinterpret_cast< CNetDevice* >(CDeviceNameService::Get()->GetDevice(ifname, FALSE));
if (!nd)
return 0;
// TODO any support for redundancy make sense? rpi has 1 native port, then now no support.
if (secondary)
return 0;
port->device = nd;
port->lastidx = 0;
port->redstate = ECT_RED_NONE;
port->stack.device = &(port->device);
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]));
/* we use RAW packet socket, with packet type ETH_P_ECAT */
/* set flags of NIC interface, here promiscuous and broadcast */
//TODO ifr.ifr_flags = ifr.ifr_flags | IFF_PROMISC | IFF_BROADCAST;
/* 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)
{
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 = reinterpret_cast< 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;
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;
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];
CNetDevice *nd = reinterpret_cast< CNetDevice* >(*stack->device);
boolean rval = nd->SendFrame((*stack->txbuf)[idx], lp);
(*stack->rxbufstat)[idx] = EC_BUF_TX;
return rval ? lp : -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)
{
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 */
CNetDevice *nd = reinterpret_cast< CNetDevice* >(port->redport->device);
nd->SendFrame(&(port->txbuf2), port->txbuflength2);
port->redport->rxbufstat[idx] = EC_BUF_TX;
}
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;
unsigned bytesrx;
ec_stackT *stack;
if (!stacknumber)
{
stack = &(port->stack);
}
else
{
stack = &(port->redport->stack);
}
lp = sizeof(port->tempinbuf);
CNetDevice *nd = reinterpret_cast< CNetDevice* >(*stack->device);
boolean rval = nd->ReceiveFrame((*stack->tempbuf), &bytesrx);
port->tempinbufs = bytesrx;
return rval && (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 retreives 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_buf_circleT *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
{
/* 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 reqested 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 happend */
}
}
}
}
}
/* WKC if mathing 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 reveived MAC source on primary socket */
primrx = 0;
if (wkc > EC_NOFRAME) primrx = port->rxsa[idx];
/* secrx if the reveived 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);
/* if nothing received, clear buffer index status so it can be used again */
if (wkc <= EC_NOFRAME)
{
ecx_setbufstat(port, idx, EC_BUF_EMPTY);
}
return wkc;
}
/** Blocking send and recieve 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 timout 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));
/* if nothing received, clear buffer index status so it can be used again */
if (wkc <= EC_NOFRAME)
{
ecx_setbufstat(port, idx, EC_BUF_EMPTY);
}
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

View File

@ -0,0 +1,120 @@
/*
* 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.cpp
*/
#ifndef _nicdrvh_
#define _nicdrvh_
#ifdef __cplusplus
extern "C"
{
#endif
// CNetDevice requires buffer size 1600
#define FRAME_BUFFER_SIZE 1600
typedef uint8 ec_buf_circleT[FRAME_BUFFER_SIZE];
/** pointer structure to Tx and Rx stacks */
typedef struct
{
/** socket connection used */
void **device;
/** tx buffer */
ec_buf_circleT (*txbuf)[EC_MAXBUF];
/** tx buffer lengths */
int (*txbuflength)[EC_MAXBUF];
/** temporary receive buffer */
ec_buf_circleT *tempbuf;
/** rx buffers */
ec_buf_circleT (*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;
void *device;
/** rx buffers */
ec_buf_circleT rxbuf[EC_MAXBUF];
/** rx buffer status */
int rxbufstat[EC_MAXBUF];
/** rx MAC source address */
int rxsa[EC_MAXBUF];
/** temporary rx buffer */
ec_buf_circleT tempinbuf;
} ecx_redportt;
/** pointer structure to buffers, vars and mutexes for port instantiation */
typedef struct
{
ec_stackT stack;
void *device;
/** rx buffers */
ec_buf_circleT rxbuf[EC_MAXBUF];
/** rx buffer status */
int rxbufstat[EC_MAXBUF];
/** rx MAC source address */
int rxsa[EC_MAXBUF];
/** temporary rx buffer */
ec_buf_circleT tempinbuf;
/** temporary rx buffer status */
int tempinbufs;
/** transmit buffers */
ec_buf_circleT txbuf[EC_MAXBUF];
/** transmit buffer lenghts */
int txbuflength[EC_MAXBUF];
/** temporary tx buffer */
ec_buf_circleT 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;
} 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);
#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);
#ifdef __cplusplus
}
#endif
#endif

56
oshw/circle/oshw.c 100644
View File

@ -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 <machine/endian.h>
#include <osal.h>
#include <oshw.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(uint16 host)
{
uint16 network = __htons(host);
return network;
}
/**
* 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(uint16 network)
{
uint16 host = __ntohs(network);
return host;
}
/** Create list over available network adapters.
* @return First element in linked list of adapters
*/
ec_adaptert *oshw_find_adapters(void)
{
// TODO might use devicenameservice.h to check if eth1,eth2,ethX exist!
// TODO should return a copy of this, const-ness not guaranteed!
static ec_adaptert _s_native_eth = {
"eth0",
"native ethernet",
0L
};
return &_s_native_eth;
}
/** 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)
{
}

31
oshw/circle/oshw.h 100644
View File

@ -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