vxworks
Add VxWorks reference driver, interrupt driven Mux driver.
This commit is contained in:
parent
ea931518a1
commit
addb1204b4
|
@ -0,0 +1,217 @@
|
|||
/*
|
||||
* Simple Open EtherCAT Master Library
|
||||
*
|
||||
* File : osal.c
|
||||
* Version : 1.3.1
|
||||
* Date : 10-01-2017
|
||||
* 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) 2012-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, Eiserstraße 5, D-33415 Verl, Germany
|
||||
* (www.beckhoff.com).
|
||||
*/
|
||||
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <osal.h>
|
||||
#include <vxWorks.h>
|
||||
#include <taskLib.h>
|
||||
|
||||
|
||||
#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 USECS_PER_SEC 1000000
|
||||
|
||||
/* OBS! config worker threads must have higher prio that task running ec_configuration */
|
||||
#define ECAT_TASK_PRIO_HIGH 20 /* Priority for high performance network task */
|
||||
#define ECAT_TASK_PRIO_LOW 80 /* Priority for high performance network task */
|
||||
#define ECAT_STACK_SIZE 10000 /* Stack size for high performance task */
|
||||
static int ecatTaskOptions = VX_SUPERVISOR_MODE | VX_UNBREAKABLE;
|
||||
static int ecatTaskIndex = 0;
|
||||
|
||||
#ifndef use_task_delay
|
||||
#define use_task_delay 1
|
||||
#endif
|
||||
|
||||
int osal_usleep (uint32 usec)
|
||||
{
|
||||
#if (use_task_delay == 1)
|
||||
/* Task delay 0 only yields */
|
||||
taskDelay(usec / 1000);
|
||||
return 0;
|
||||
#else
|
||||
/* The suspension may be longer than requested due to the rounding up of
|
||||
* the request to the timer's resolution or to other scheduling activities
|
||||
* (e.g., a higher priority task intervenes).
|
||||
*/
|
||||
struct timespec ts;
|
||||
ts.tv_sec = usec / USECS_PER_SEC;
|
||||
ts.tv_nsec = (usec % USECS_PER_SEC) * 1000;
|
||||
return nanosleep(&ts, NULL);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
int osal_gettimeofday(struct timeval *tv, struct timezone *tz)
|
||||
{
|
||||
int return_value;
|
||||
|
||||
return_value = gettimeofday(tv, tz);
|
||||
|
||||
return return_value;
|
||||
}
|
||||
|
||||
ec_timet osal_current_time(void)
|
||||
{
|
||||
struct timeval current_time;
|
||||
ec_timet return_value;
|
||||
|
||||
osal_gettimeofday(¤t_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)
|
||||
{
|
||||
diff->sec = end->sec - start->sec;
|
||||
diff->usec = end->usec - start->usec;
|
||||
if (diff->usec < 0) {
|
||||
--diff->sec;
|
||||
diff->usec += 1000000;
|
||||
}
|
||||
}
|
||||
|
||||
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(¤t_time, 0);
|
||||
stop_time.tv_sec = self->stop_time.sec;
|
||||
stop_time.tv_usec = self->stop_time.usec;
|
||||
is_not_yet_expired = timercmp(¤t_time, &stop_time, <);
|
||||
|
||||
return is_not_yet_expired == FALSE;
|
||||
}
|
||||
|
||||
void *osal_malloc(size_t size)
|
||||
{
|
||||
return malloc(size);
|
||||
}
|
||||
|
||||
void osal_free(void *ptr)
|
||||
{
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
int osal_thread_create(void *thandle, int stacksize, void *func, void *param)
|
||||
{
|
||||
char task_name[20];
|
||||
TASK_ID * tid = (TASK_ID *)thandle;
|
||||
FUNCPTR func_ptr = func;
|
||||
_Vx_usr_arg_t arg1 = (_Vx_usr_arg_t)param;
|
||||
|
||||
snprintf(task_name,sizeof(task_name),"worker_%d",ecatTaskIndex++);
|
||||
|
||||
*tid = taskSpawn (task_name, ECAT_TASK_PRIO_LOW,
|
||||
ecatTaskOptions, ECAT_STACK_SIZE,
|
||||
func_ptr, arg1, 0, 0, 0, 0, 0, 0, 0, 0, 0);
|
||||
if(*tid == TASK_ID_ERROR)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int osal_thread_create_rt(void *thandle, int stacksize, void *func, void *param)
|
||||
{
|
||||
char task_name[20];
|
||||
TASK_ID * tid = (TASK_ID *)thandle;
|
||||
FUNCPTR func_ptr = func;
|
||||
_Vx_usr_arg_t arg1 = (_Vx_usr_arg_t)param;
|
||||
|
||||
snprintf(task_name,sizeof(task_name),"worker_rt_%d",ecatTaskIndex++);
|
||||
|
||||
*tid = taskSpawn (task_name, ECAT_TASK_PRIO_HIGH,
|
||||
ecatTaskOptions, ECAT_STACK_SIZE,
|
||||
func_ptr, arg1, 0, 0, 0, 0, 0, 0, 0, 0, 0);
|
||||
|
||||
if(*tid == TASK_ID_ERROR)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* Simple Open EtherCAT Master Library
|
||||
*
|
||||
* File : osal_defs.h
|
||||
* Version : 1.3.1
|
||||
* Date : 10-01-2017
|
||||
* 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) 2012-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, Eiserstraße 5, D-33415 Verl, Germany
|
||||
* (www.beckhoff.com).
|
||||
*/
|
||||
|
||||
#ifndef _osal_defs_
|
||||
#define _osal_defs_
|
||||
|
||||
#ifndef PACKED
|
||||
#define PACKED_BEGIN
|
||||
#define PACKED __attribute__((__packed__))
|
||||
#define PACKED_END
|
||||
#endif
|
||||
|
||||
#define OSAL_THREAD_HANDLE TASK_ID
|
||||
#define OSAL_THREAD_FUNC void
|
||||
#define OSAL_THREAD_FUNC_RT void
|
||||
|
||||
#endif
|
|
@ -0,0 +1,956 @@
|
|||
/*
|
||||
* Simple Open EtherCAT Master Library
|
||||
*
|
||||
* File : nicdrv.c
|
||||
* Version : 1.3.1
|
||||
* Date : 10-01-2017
|
||||
* 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) 2014-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, Eiserstraße 5, D-33415 Verl, Germany
|
||||
* (www.beckhoff.com).
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \brief
|
||||
* EtherCAT VxWorks SNARF Mux 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 recieved.
|
||||
* If there is a match the packet can be combined with the transmit packet
|
||||
* and returned to the higher level function.
|
||||
*
|
||||
* If EtherCAT is run in parallel with normal IP traffic and EtherCAT have a
|
||||
* dedicated NIC, instatiate an extra tNetX task and redirect the NIC workQ
|
||||
* to be handle by the extra tNetX task, if needed raise the tNetX task prio.
|
||||
* This prevents from having tNet0 becoming a bootleneck.
|
||||
*
|
||||
* The "redundant" option will configure two Mux drivers 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 resend through interface B.
|
||||
* This layer if fully transparent for the higher layers.
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
#include <ipProto.h>
|
||||
#include <vxWorks.h>
|
||||
#include <wvLib.h>
|
||||
|
||||
#include "oshw.h"
|
||||
#include "osal.h"
|
||||
#include "nicdrv.h"
|
||||
|
||||
#define NIC_DEBUG /* Print debugging info? */
|
||||
|
||||
// wvEvent flags
|
||||
#define ECAT_RECV_FAILED 0x664
|
||||
#define ECAT_RECV_OK 0x665
|
||||
#define ECAT_RECV_RETRY_OK 0x666
|
||||
#define ECAT_STACK_RECV 0x667
|
||||
#define ECAT_SEND_START 0x675
|
||||
#define ECAT_SEND_COMPLETE 0x676
|
||||
#define ECAT_SEND_FAILED 0x677
|
||||
|
||||
#ifdef NIC_DEBUG
|
||||
|
||||
#define NIC_LOGMSG(x,a,b,c,d,e,f) \
|
||||
do { \
|
||||
logMsg (x,a,b,c,d,e,f); \
|
||||
} while (0)
|
||||
#define NIC_WVEVENT(a,b,c) \
|
||||
do { \
|
||||
wvEvent(a, b, c); \
|
||||
} while (0)
|
||||
|
||||
|
||||
#else
|
||||
#define NIC_LOGMSG(x,a,b,c,d,e,f)
|
||||
#define NIC_WVEVENT(a,b,c)
|
||||
#endif /* NIC_DEBUG */
|
||||
|
||||
#define IF_NAME_SIZE 8
|
||||
|
||||
/** Redundancy modes */
|
||||
enum
|
||||
{
|
||||
/** No redundancy, single NIC mode */
|
||||
ECT_RED_NONE,
|
||||
/** Double redundant NIC connecetion */
|
||||
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]
|
||||
|
||||
/** Receive hook called by Mux driver. */
|
||||
static int mux_rx_callback(void* pCookie, long type, M_BLK_ID pMblk, LL_HDR_INFO *llHdrInfo, void *muxUserArg);
|
||||
|
||||
static void ecx_clear_rxbufstat(int *rxbufstat)
|
||||
{
|
||||
int i;
|
||||
for(i = 0; i < EC_MAXBUF; i++)
|
||||
{
|
||||
rxbufstat[i] = EC_BUF_EMPTY;
|
||||
}
|
||||
}
|
||||
|
||||
void print_nicversion(void)
|
||||
{
|
||||
printf("Generic is used\n");
|
||||
}
|
||||
|
||||
/** Basic setup to connect NIC to socket.
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] ifname = Name of NIC device, f.e. "gei0"
|
||||
* @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;
|
||||
char ifn[IF_NAME_SIZE];
|
||||
int unit_no = -1;
|
||||
ETHERCAT_PKT_DEV * pPktDev;
|
||||
|
||||
/* Make refrerece to packet device struct, keep track if the packet
|
||||
* device is the redundant or not.
|
||||
*/
|
||||
if (secondary)
|
||||
{
|
||||
pPktDev = &(port->redport->pktDev);
|
||||
pPktDev->redundant = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
pPktDev = &(port->pktDev);
|
||||
pPktDev->redundant = 0;
|
||||
}
|
||||
|
||||
/* Clear frame counters*/
|
||||
pPktDev->tx_count = 0;
|
||||
pPktDev->rx_count = 0;
|
||||
pPktDev->overrun_count = 0;
|
||||
|
||||
/* Create multi-thread support semaphores */
|
||||
port->sem_get_index = semBCreate(SEM_Q_FIFO, SEM_FULL);
|
||||
|
||||
/* Get the dev name and unit from ifname
|
||||
* We assume form gei1, fei0...
|
||||
*/
|
||||
memset(ifn,0x0,sizeof(ifn));
|
||||
|
||||
for(i=0; i < strlen(ifname);i++)
|
||||
{
|
||||
if(isdigit(ifname[i]))
|
||||
{
|
||||
strncpy(ifn, ifname, i);
|
||||
unit_no = atoi(&ifname[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Detach IP stack */
|
||||
//ipDetach(pktDev.unit,pktDev.name);
|
||||
|
||||
pPktDev->port = port;
|
||||
|
||||
/* Bind to mux driver for given interface, include ethercat driver pointer
|
||||
* as user reference
|
||||
*/
|
||||
/* Bind to mux */
|
||||
pPktDev->pCookie = muxBind(ifn, unit_no, mux_rx_callback, NULL, NULL, NULL, MUX_PROTO_SNARF, "ECAT SNARF", pPktDev);
|
||||
|
||||
if (pPktDev->pCookie == NULL)
|
||||
{
|
||||
/* fail */
|
||||
NIC_LOGMSG("ecx_setupnic: muxBind init for gei: %d failed\n", unit_no, 2, 3, 4, 5, 6);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* Get reference tp END obje */
|
||||
pPktDev->endObj = endFindByName(ifn, unit_no);
|
||||
|
||||
if (port->pktDev.endObj == NULL)
|
||||
{
|
||||
/* fail */
|
||||
NIC_LOGMSG("error_hook: endFindByName failed, device gei: %d not found\n",
|
||||
unit_no, 2, 3, 4, 5, 6);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (secondary)
|
||||
{
|
||||
/* secondary port struct available? */
|
||||
if (port->redport)
|
||||
{
|
||||
port->redstate = ECT_RED_DOUBLE;
|
||||
port->redport->stack.txbuf = &(port->txbuf);
|
||||
port->redport->stack.txbuflength = &(port->txbuflength);
|
||||
port->redport->stack.rxbuf = &(port->redport->rxbuf);
|
||||
port->redport->stack.rxbufstat = &(port->redport->rxbufstat);
|
||||
port->redport->stack.rxsa = &(port->redport->rxsa);
|
||||
/* Create mailboxes for each potential EtherCAT frame index */
|
||||
for (i = 0; i < EC_MAXBUF; i++)
|
||||
{
|
||||
port->redport->msgQId[i] = msgQCreate(1, sizeof(M_BLK_ID), MSG_Q_FIFO);
|
||||
if (port->redport->msgQId[i] == MSG_Q_ID_NULL)
|
||||
{
|
||||
NIC_LOGMSG("ecx_setupnic: Failed to create redundant MsgQ[%d]",
|
||||
i, 2, 3, 4, 5, 6);
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
ecx_clear_rxbufstat(&(port->redport->rxbufstat[0]));
|
||||
}
|
||||
else
|
||||
{
|
||||
/* fail */
|
||||
NIC_LOGMSG("ecx_setupnic: Redundant port not allocated",
|
||||
unit_no, 2, 3, 4, 5, 6);
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
port->lastidx = 0;
|
||||
port->redstate = ECT_RED_NONE;
|
||||
port->stack.txbuf = &(port->txbuf);
|
||||
port->stack.txbuflength = &(port->txbuflength);
|
||||
port->stack.rxbuf = &(port->rxbuf);
|
||||
port->stack.rxbufstat = &(port->rxbufstat);
|
||||
port->stack.rxsa = &(port->rxsa);
|
||||
/* Create mailboxes for each potential EtherCAT frame index */
|
||||
for (i = 0; i < EC_MAXBUF; i++)
|
||||
{
|
||||
port->msgQId[i] = msgQCreate(1, sizeof(M_BLK_ID), MSG_Q_FIFO);
|
||||
if (port->msgQId[i] == MSG_Q_ID_NULL)
|
||||
{
|
||||
NIC_LOGMSG("ecx_setupnic: Failed to create MsgQ[%d]",
|
||||
i, 2, 3, 4, 5, 6);
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
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));
|
||||
|
||||
return 1;
|
||||
|
||||
exit:
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
/** Close sockets used
|
||||
* @param[in] port = port context struct
|
||||
* @return 0
|
||||
*/
|
||||
int ecx_closenic(ecx_portt *port)
|
||||
{
|
||||
int i;
|
||||
ETHERCAT_PKT_DEV * pPktDev;
|
||||
|
||||
pPktDev = &(port->pktDev);
|
||||
|
||||
for (i = 0; i < EC_MAXBUF; i++)
|
||||
{
|
||||
if (port->msgQId[i] != MSG_Q_ID_NULL)
|
||||
{
|
||||
msgQDelete(port->msgQId[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (pPktDev->pCookie != NULL)
|
||||
{
|
||||
muxUnbind(pPktDev->pCookie, MUX_PROTO_SNARF, mux_rx_callback);
|
||||
}
|
||||
|
||||
/* Clean redundant resources if available*/
|
||||
if (port->redport)
|
||||
{
|
||||
pPktDev = &(port->redport->pktDev);
|
||||
for (i = 0; i < EC_MAXBUF; i++)
|
||||
{
|
||||
if (port->redport->msgQId[i] != MSG_Q_ID_NULL)
|
||||
{
|
||||
msgQDelete(port->redport->msgQId[i]);
|
||||
}
|
||||
}
|
||||
if (pPktDev->pCookie != NULL)
|
||||
{
|
||||
muxUnbind(pPktDev->pCookie, MUX_PROTO_SNARF, mux_rx_callback);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Fill buffer with ethernet header structure.
|
||||
* Destination MAC is allways broadcast.
|
||||
* Ethertype is allways ETH_P_ECAT.
|
||||
* @param[out] p = buffer
|
||||
*/
|
||||
void ec_setupheader(void *p)
|
||||
{
|
||||
ec_etherheadert *bp;
|
||||
bp = p;
|
||||
bp->da0 = htons(0xffff);
|
||||
bp->da1 = htons(0xffff);
|
||||
bp->da2 = htons(0xffff);
|
||||
bp->sa0 = htons(priMAC[0]);
|
||||
bp->sa1 = htons(priMAC[1]);
|
||||
bp->sa2 = htons(priMAC[2]);
|
||||
bp->etype = 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;
|
||||
MSG_Q_ID msgQId;
|
||||
M_BLK_ID trash_can;
|
||||
|
||||
semTake(port->sem_get_index, WAIT_FOREVER);
|
||||
|
||||
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;
|
||||
|
||||
/* Clean up any abandoned frames */
|
||||
msgQId = port->msgQId[idx];
|
||||
if (msgQReceive(msgQId, (char *)&trash_can, sizeof(M_BLK_ID), NO_WAIT) == OK)
|
||||
{
|
||||
/* Free resources */
|
||||
netMblkClChainFree(trash_can);
|
||||
}
|
||||
|
||||
if (port->redstate != ECT_RED_NONE)
|
||||
{
|
||||
port->redport->rxbufstat[idx] = EC_BUF_ALLOC;
|
||||
/* Clean up any abandoned frames */
|
||||
msgQId = port->redport->msgQId[idx];
|
||||
if (msgQReceive(msgQId, (char *)&trash_can, sizeof(M_BLK_ID), NO_WAIT) == OK)
|
||||
{
|
||||
/* Free resources */
|
||||
netMblkClChainFree(trash_can);
|
||||
}
|
||||
}
|
||||
|
||||
port->lastidx = idx;
|
||||
|
||||
semGive(port->sem_get_index);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/** Low level transmit buffer over mux layer 2 driver
|
||||
*
|
||||
* @param[in] pPktDev = packet device to send buffer over
|
||||
* @param[in] buf = buff to send
|
||||
* @param[in] len = bytes to send
|
||||
* @return driver send result
|
||||
*/
|
||||
static int ec_outfram_send(ETHERCAT_PKT_DEV * pPktDev, void * buf, int len)
|
||||
{
|
||||
STATUS status = OK;
|
||||
M_BLK_ID pPacket;
|
||||
int rval = 0;
|
||||
|
||||
/* Allocate m_blk to send */
|
||||
if ((pPacket = netTupleGet(pPktDev->endObj->pNetPool,
|
||||
len,
|
||||
M_DONTWAIT,
|
||||
MT_DATA,
|
||||
TRUE)) == NULL)
|
||||
{
|
||||
NIC_LOGMSG("ec_outfram_send: Could not allocate MBLK memory!\n", 1, 2, 3, 4, 5, 6);
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
pPacket->mBlkHdr.mLen = len;
|
||||
pPacket->mBlkHdr.mFlags |= M_HEADER;
|
||||
pPacket->mBlkHdr.mData = pPacket->pClBlk->clNode.pClBuf;
|
||||
|
||||
netMblkFromBufCopy(pPacket, buf, 0, pPacket->mBlkHdr.mLen, M_DONTWAIT, NULL);
|
||||
status = muxTkSend(pPktDev->pCookie, pPacket, NULL, htons(ETH_P_ECAT), NULL);
|
||||
|
||||
if (status == OK)
|
||||
{
|
||||
rval = len;
|
||||
NIC_WVEVENT(ECAT_SEND_COMPLETE, (char *)&rval, sizeof(rval));
|
||||
}
|
||||
else
|
||||
{
|
||||
netMblkClChainFree(pPacket);
|
||||
/* fail */
|
||||
static int print_once;
|
||||
if (print_once == 0)
|
||||
{
|
||||
NIC_LOGMSG("ec_outfram_send: failed\n",
|
||||
1, 2, 3, 4, 5, 6);
|
||||
print_once = 1;
|
||||
}
|
||||
NIC_WVEVENT(ECAT_SEND_FAILED, (char *)&rval, sizeof(rval));
|
||||
}
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
||||
/** High level transmit buffer over mux layer 2 driver, passing buffer
|
||||
* and packet device to send on as arguments
|
||||
* @param[in] port = port context holding reference to packet device
|
||||
* @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 rval = 0;
|
||||
ec_stackT *stack;
|
||||
ETHERCAT_PKT_DEV * pPktDev;
|
||||
|
||||
if (!stacknumber)
|
||||
{
|
||||
stack = &(port->stack);
|
||||
pPktDev = &(port->pktDev);
|
||||
}
|
||||
else
|
||||
{
|
||||
stack = &(port->redport->stack);
|
||||
pPktDev = &(port->redport->pktDev);
|
||||
}
|
||||
|
||||
rval = ec_outfram_send(pPktDev, (char*)(*stack->txbuf)[idx],
|
||||
(*stack->txbuflength)[idx]);
|
||||
if (rval > 0)
|
||||
{
|
||||
(*stack->rxbufstat)[idx] = EC_BUF_TX;
|
||||
port->pktDev.tx_count++;
|
||||
}
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
||||
/** High level transmit frame to send as index.
|
||||
* @param[in] port = port context
|
||||
* @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 = 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 = htons(secMAC[1]);
|
||||
/* transmit over secondary interface */
|
||||
rval = ec_outfram_send(&(port->redport->pktDev), &(port->txbuf2), port->txbuflength2);
|
||||
port->redport->rxbufstat[idx] = EC_BUF_TX;
|
||||
}
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
||||
|
||||
/** Call back routine registered as hook with mux layer 2 driver
|
||||
* @param[in] pCookie = Mux cookie
|
||||
* @param[in] type = recived type
|
||||
* @param[in] pMblk = the received packet reference
|
||||
* @param[in] llHdrInfo = header info
|
||||
* @param[in] muxUserArg = assigned reference to packet device when init called
|
||||
* @return TRUE if frame was succesfully read and passed to MsgQ
|
||||
*/
|
||||
static int mux_rx_callback(void* pCookie, long type, M_BLK_ID pMblk, LL_HDR_INFO *llHdrInfo, void *muxUserArg)
|
||||
{
|
||||
BOOL ret = FALSE;
|
||||
int idxf;
|
||||
ec_comt *ecp;
|
||||
ec_bufT * tempbuf;
|
||||
ecx_portt * port;
|
||||
MSG_Q_ID msgQId;
|
||||
ETHERCAT_PKT_DEV * pPktDev;
|
||||
int length;
|
||||
|
||||
/* check if it is an EtherCAT frame */
|
||||
if (type == ETH_P_ECAT)
|
||||
{
|
||||
length = pMblk->mBlkHdr.mLen;
|
||||
tempbuf = (ec_bufT *)pMblk->mBlkHdr.mData;
|
||||
pPktDev = (ETHERCAT_PKT_DEV *)muxUserArg;
|
||||
port = pPktDev->port;
|
||||
|
||||
/* Get ethercat frame header */
|
||||
ecp = (ec_comt*)&(*tempbuf)[ETH_HEADERSIZE];
|
||||
idxf = ecp->index;
|
||||
if (idxf >= EC_MAXBUF)
|
||||
{
|
||||
NIC_LOGMSG("mux_rx_callback: idx %d out of bounds\n", idxf,
|
||||
2, 3, 4, 5, 6);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Check if it is the redundant port or not */
|
||||
if (pPktDev->redundant == 1)
|
||||
{
|
||||
msgQId = port->redport->msgQId[idxf];
|
||||
}
|
||||
else
|
||||
{
|
||||
msgQId = port->msgQId[idxf];
|
||||
}
|
||||
|
||||
if (length > 0)
|
||||
{
|
||||
/* Post the frame to the reqceive Q for the EtherCAT stack */
|
||||
STATUS status;
|
||||
status = msgQSend(msgQId, (char *)&pMblk, sizeof(M_BLK_ID),
|
||||
NO_WAIT, MSG_PRI_NORMAL);
|
||||
if (status == OK)
|
||||
{
|
||||
NIC_WVEVENT(ECAT_RECV_OK, (char *)&length, sizeof(length));
|
||||
ret = TRUE;
|
||||
}
|
||||
else if ((status == ERROR) && (errno == S_objLib_OBJ_UNAVAILABLE))
|
||||
{
|
||||
/* Try to empty the MSGQ since we for some strange reason
|
||||
* already have a frame in the MsqQ,
|
||||
* is it due to timeout when receiving?
|
||||
* We want the latest received frame in the buffer
|
||||
*/
|
||||
port->pktDev.overrun_count++;
|
||||
NIC_LOGMSG("mux_rx_callback: idx %d MsgQ overrun\n", idxf,
|
||||
2, 3, 4, 5, 6);
|
||||
M_BLK_ID trash_can;
|
||||
if (msgQReceive(msgQId, (char *)&trash_can,
|
||||
sizeof(M_BLK_ID), NO_WAIT) == OK)
|
||||
{
|
||||
/* Free resources */
|
||||
netMblkClChainFree(trash_can);
|
||||
}
|
||||
|
||||
status = msgQSend(msgQId, (char *)&pMblk, sizeof(M_BLK_ID),
|
||||
NO_WAIT, MSG_PRI_NORMAL);
|
||||
if (status == OK)
|
||||
{
|
||||
NIC_WVEVENT(ECAT_RECV_RETRY_OK, (char *)&length, sizeof(length));
|
||||
ret = TRUE;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
NIC_WVEVENT(ECAT_RECV_FAILED, (char *)&length, sizeof(length));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** Non blocking or blocking read, if we use timeout we pass minimum 1 tick.
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] pMblk = mBlk for received frame
|
||||
* @param[in] timeout = timeout in us
|
||||
* @return >0 if frame is available and read
|
||||
*/
|
||||
static int ecx_recvpkt(ecx_portt *port, int idx, int stacknumber, M_BLK_ID * pMblk, int timeout)
|
||||
{
|
||||
int bytesrx = 0;
|
||||
MSG_Q_ID msgQId;
|
||||
int tick_timeout = max((timeout / 1000), 1);
|
||||
|
||||
if (stacknumber == 1)
|
||||
{
|
||||
msgQId = port->redport->msgQId[idx];
|
||||
}
|
||||
else
|
||||
{
|
||||
msgQId = port->msgQId[idx];
|
||||
}
|
||||
|
||||
if (timeout == 0)
|
||||
{
|
||||
bytesrx = msgQReceive(msgQId, (void *)pMblk,
|
||||
sizeof(M_BLK_ID), NO_WAIT);
|
||||
}
|
||||
else
|
||||
{
|
||||
bytesrx = msgQReceive(msgQId, (void *)pMblk,
|
||||
sizeof(M_BLK_ID), tick_timeout);
|
||||
}
|
||||
|
||||
if (bytesrx > 0)
|
||||
{
|
||||
bytesrx = (*pMblk)->mBlkHdr.mLen;
|
||||
NIC_WVEVENT(ECAT_STACK_RECV, (char *)&bytesrx, sizeof(bytesrx));
|
||||
}
|
||||
|
||||
|
||||
return (bytesrx > 0);
|
||||
}
|
||||
|
||||
/** Non blocking receive frame function. Uses RX buffer and index to combine
|
||||
* read frame with transmitted frame. Frames are received by separate receiver
|
||||
* task tNet0 (default), tNet0 fetch what frame index and store a reference to the
|
||||
* received frame in matching MsgQ. The stack user tasks fetch the frame
|
||||
* reference and copies the frame the the RX buffer, when done it free
|
||||
* the frame buffer alloctaed by the Mux.
|
||||
*
|
||||
* @param[in] port = port context struct
|
||||
* @param[in] idx = requested index of frame
|
||||
* @param[in] stacknumber = 0=primary 1=secondary stack
|
||||
* @param[in] timeout = timeout in us
|
||||
* @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, int timeout)
|
||||
{
|
||||
uint16 l;
|
||||
int rval;
|
||||
ec_etherheadert *ehp;
|
||||
ec_comt *ecp;
|
||||
ec_stackT *stack;
|
||||
ec_bufT *rxbuf;
|
||||
ec_bufT *tempinbuf;
|
||||
M_BLK_ID pMblk;
|
||||
|
||||
if (!stacknumber)
|
||||
{
|
||||
stack = &(port->stack);
|
||||
}
|
||||
else
|
||||
{
|
||||
stack = &(port->redport->stack);
|
||||
}
|
||||
rval = EC_NOFRAME;
|
||||
rxbuf = &(*stack->rxbuf)[idx];
|
||||
/* (non-) blocking call to retrieve frame from Muxlayer */
|
||||
if (ecx_recvpkt(port, idx, stacknumber, &pMblk, timeout))
|
||||
{
|
||||
rval = EC_OTHERFRAME;
|
||||
|
||||
/* Get pointer to the frame */
|
||||
tempinbuf = (ec_bufT *)pMblk->mBlkHdr.mData;
|
||||
/* Get pointer to the Ethernet header */
|
||||
ehp = (ec_etherheadert*)tempinbuf;
|
||||
/* Get pointer to the EtherCAT frame as ethernet payload */
|
||||
ecp = (ec_comt*)&(*tempinbuf)[ETH_HEADERSIZE];
|
||||
l = etohs(ecp->elength) & 0x0fff;
|
||||
/* yes, put it in the buffer array (strip ethernet header) */
|
||||
netMblkOffsetToBufCopy(pMblk, ETH_HEADERSIZE,(void *) rxbuf,
|
||||
(*stack->txbuflength)[idx] - ETH_HEADERSIZE, NULL);
|
||||
|
||||
/* 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] = ntohs(ehp->sa1);
|
||||
netMblkClChainFree(pMblk);
|
||||
port->pktDev.rx_count++;
|
||||
}
|
||||
|
||||
/* 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
|
||||
* @param[in] timeout = timeout in us
|
||||
* @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 timeout)
|
||||
{
|
||||
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, timeout);
|
||||
}
|
||||
/* 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, timeout);
|
||||
}
|
||||
}
|
||||
/* 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, timeout);
|
||||
} 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, timeout);
|
||||
|
||||
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, timeout);
|
||||
/* 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
|
|
@ -0,0 +1,166 @@
|
|||
/*
|
||||
* Simple Open EtherCAT Master Library
|
||||
*
|
||||
* File : nicdrv.h
|
||||
* Version : 1.3.1
|
||||
* Date : 10-01-2017
|
||||
* 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) 2014-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, Eiserstraße 5, D-33415 Verl, Germany
|
||||
* (www.beckhoff.com).
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \brief
|
||||
* Headerfile for nicdrv.c
|
||||
*/
|
||||
|
||||
#ifndef _nicdrvh_
|
||||
#define _nicdrvh_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
#include <vxWorks.h>
|
||||
#include <muxLib.h>
|
||||
|
||||
/** structure to connect EtherCAT stack and VxWorks device */
|
||||
typedef struct ETHERCAT_PKT_DEV
|
||||
{
|
||||
struct ecx_port *port;
|
||||
void *pCookie;
|
||||
END_OBJ *endObj;
|
||||
UINT32 redundant;
|
||||
UINT32 tx_count;
|
||||
UINT32 rx_count;
|
||||
UINT32 overrun_count;
|
||||
}ETHERCAT_PKT_DEV;
|
||||
|
||||
/** pointer structure to Tx and Rx stacks */
|
||||
typedef struct
|
||||
{
|
||||
/** tx buffer */
|
||||
ec_bufT (*txbuf)[EC_MAXBUF];
|
||||
/** tx buffer lengths */
|
||||
int (*txbuflength)[EC_MAXBUF];
|
||||
/** 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 ecx_redport
|
||||
{
|
||||
/** Stack reference */
|
||||
ec_stackT stack;
|
||||
/** Packet device instance */
|
||||
ETHERCAT_PKT_DEV pktDev;
|
||||
/** rx buffers */
|
||||
ec_bufT rxbuf[EC_MAXBUF];
|
||||
/** rx buffer status */
|
||||
int rxbufstat[EC_MAXBUF];
|
||||
/** rx MAC source address */
|
||||
int rxsa[EC_MAXBUF];
|
||||
/** MSG Q for receive callbacks to post into */
|
||||
MSG_Q_ID msgQId[EC_MAXBUF];
|
||||
} ecx_redportt;
|
||||
|
||||
/** pointer structure to buffers, vars and mutexes for port instantiation */
|
||||
typedef struct ecx_port
|
||||
{
|
||||
/** Stack reference */
|
||||
ec_stackT stack;
|
||||
/** Packet device instance */
|
||||
ETHERCAT_PKT_DEV pktDev;
|
||||
/** rx buffers */
|
||||
ec_bufT rxbuf[EC_MAXBUF];
|
||||
/** rx buffer status */
|
||||
int rxbufstat[EC_MAXBUF];
|
||||
/** rx MAC source address */
|
||||
int rxsa[EC_MAXBUF];
|
||||
/** transmit buffers */
|
||||
ec_bufT txbuf[EC_MAXBUF];
|
||||
/** transmit buffer lenghts */
|
||||
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;
|
||||
/** Semaphore to protect single resources */
|
||||
SEM_ID sem_get_index;
|
||||
/** MSG Q for receive callbacks to post into */
|
||||
MSG_Q_ID msgQId[EC_MAXBUF];
|
||||
} 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
|
|
@ -0,0 +1,96 @@
|
|||
/*
|
||||
* Simple Open EtherCAT Master Library
|
||||
*
|
||||
* File : oshw.c
|
||||
* Version : 1.3.1
|
||||
* Date : 10-01-2017
|
||||
* 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) 2012-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, Eiserstraße 5, D-33415 Verl, Germany
|
||||
* (www.beckhoff.com).
|
||||
*/
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
#include <net/if.h>
|
||||
#include <sys/socket.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.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)
|
||||
{
|
||||
ec_adaptert * ret_adapter = NULL;
|
||||
/* Not implemented */
|
||||
assert(0);
|
||||
|
||||
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)
|
||||
{
|
||||
/* Not implemented */
|
||||
assert(0);
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* Simple Open EtherCAT Master Library
|
||||
*
|
||||
* File : oshw.h
|
||||
* Version : 1.3.1
|
||||
* Date : 10-01-2017
|
||||
* 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) 2012-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, Eiserstraße 5, D-33415 Verl, Germany
|
||||
* (www.beckhoff.com).
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \brief
|
||||
* Headerfile for oshw.c
|
||||
*/
|
||||
|
||||
#ifndef _oshw_
|
||||
#define _oshw_
|
||||
|
||||
#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);
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue