2015-03-12 04:45:16 +08:00
|
|
|
|
/*
|
|
|
|
|
* Simple Open EtherCAT Master Library
|
|
|
|
|
*
|
|
|
|
|
* File : nicdrv.c
|
|
|
|
|
* Version : 1.3.1
|
|
|
|
|
* Date : 11-03-2015
|
|
|
|
|
* Copyright (C) 2005-2015 Speciaal Machinefabriek Ketels v.o.f.
|
|
|
|
|
* Copyright (C) 2005-2015 Arthur Ketels
|
|
|
|
|
* Copyright (C) 2008-2009 TU/e Technische Universiteit Eindhoven
|
|
|
|
|
* Copyright (C) 2012-2015 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 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,
|
2015-04-16 18:00:00 +08:00
|
|
|
|
* and the send packets always return in the receive buffer.
|
2015-03-12 04:45:16 +08:00
|
|
|
|
* 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
|
2015-04-16 18:00:00 +08:00
|
|
|
|
* EtherCAT header. The index is stored and compared when a frame is received.
|
2015-03-12 04:45:16 +08:00
|
|
|
|
* 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
|
2015-04-16 18:00:00 +08:00
|
|
|
|
* compensate. If needed the packets from interface A are resent through interface B.
|
2015-03-12 04:45:16 +08:00
|
|
|
|
* This layer if fully transparent for the higher layers.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include <ioctl.h>
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
#include <rt.h>
|
|
|
|
|
#include <traceapi.h>
|
|
|
|
|
#include "hpeif2.h"
|
|
|
|
|
|
|
|
|
|
#include "ethercattype.h"
|
|
|
|
|
#include "nicdrv.h"
|
|
|
|
|
#include "oshw.h"
|
|
|
|
|
|
|
|
|
|
/** 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]
|
|
|
|
|
|
|
|
|
|
#ifdef EC_VER1
|
|
|
|
|
|
|
|
|
|
ecx_portt ecx_port;
|
|
|
|
|
ecx_redportt ecx_redport;
|
|
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#define ECAT_PRINT_INFO printf
|
|
|
|
|
#define ECAT_PRINT_WARN printf
|
|
|
|
|
#define ECAT_PRINT_ERROR printf
|
|
|
|
|
|
|
|
|
|
/* HPE port settings */
|
|
|
|
|
const unsigned long interrupt_mode = NO_INTERRUPT;
|
|
|
|
|
const unsigned long phy_settings = SPEED_100 | DUPLEX_FULL;
|
|
|
|
|
|
|
|
|
|
/** Basic setup to connect NIC to socket.
|
|
|
|
|
* @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
|
|
|
|
|
*/
|
2015-11-04 20:02:33 +08:00
|
|
|
|
int ecx_setupnic(ecx_portt *port, const char *ifname, int secondary)
|
2015-03-12 04:45:16 +08:00
|
|
|
|
{
|
|
|
|
|
HPESTATUS status;
|
|
|
|
|
HPEMEDIASTATUS mstat;
|
|
|
|
|
time_t now, t;
|
|
|
|
|
unsigned char mac[6];
|
|
|
|
|
int i;
|
|
|
|
|
int dontWaitForLink = 0;
|
|
|
|
|
int result = 1;
|
|
|
|
|
HPE_CONFIG_OPTIONS conf = { 0, 0, NULL};
|
|
|
|
|
|
|
|
|
|
status = hpeOpen(ifname, phy_settings, interrupt_mode, &(port->handle));
|
2015-11-04 20:02:33 +08:00
|
|
|
|
if (status != E_OK)
|
2015-03-12 04:45:16 +08:00
|
|
|
|
{
|
|
|
|
|
ECAT_PRINT_ERROR("hpeOpen failed with status %04x ", status);
|
|
|
|
|
else if(status == E_PARAM) ECAT_PRINT_ERROR("E_PARAM\n");
|
|
|
|
|
result = 0;
|
|
|
|
|
goto end;
|
|
|
|
|
}
|
2015-11-04 20:02:33 +08:00
|
|
|
|
|
2015-03-12 04:45:16 +08:00
|
|
|
|
conf.option_flags |= OPT_PROMISC;
|
|
|
|
|
|
|
|
|
|
status = hpeConfigOptions(port->handle, &conf, sizeof(conf));
|
2015-11-04 20:02:33 +08:00
|
|
|
|
if (status != E_OK)
|
2015-03-12 04:45:16 +08:00
|
|
|
|
{
|
|
|
|
|
ECAT_PRINT_ERROR("hpeConfigOptions failed with status %04x\n", status);
|
|
|
|
|
// NOTE: HPE driver for Intel 10/100 Mbps device currently doesn't support multicast.
|
|
|
|
|
result = 0;
|
|
|
|
|
goto end;
|
|
|
|
|
}
|
2015-11-04 20:02:33 +08:00
|
|
|
|
|
2015-03-12 04:45:16 +08:00
|
|
|
|
time(&now);
|
2015-11-04 20:02:33 +08:00
|
|
|
|
do
|
2015-03-12 04:45:16 +08:00
|
|
|
|
{
|
|
|
|
|
status = hpeGetMediaStatus(port->handle, &mstat);
|
2015-11-04 20:02:33 +08:00
|
|
|
|
if (status != E_OK)
|
2015-03-12 04:45:16 +08:00
|
|
|
|
{
|
|
|
|
|
ECAT_PRINT_ERROR("hpeGetMediaStatus failed with status %04x\n", status);
|
|
|
|
|
result = 0;
|
|
|
|
|
goto end;
|
|
|
|
|
}
|
2015-11-04 20:02:33 +08:00
|
|
|
|
if (mstat.media_speed == SPEED_NONE)
|
2015-03-12 04:45:16 +08:00
|
|
|
|
{
|
|
|
|
|
RtSleepEx(1000);
|
|
|
|
|
time(&t);
|
|
|
|
|
}
|
|
|
|
|
} while (mstat.media_speed == SPEED_NONE && t < (now+10));
|
|
|
|
|
|
|
|
|
|
if (((mstat.media_speed & phy_settings) == 0) || ((mstat.media_duplex & phy_settings) == 0)) {
|
2015-11-04 20:02:33 +08:00
|
|
|
|
ECAT_PRINT_ERROR("Media not connected as requested: speed=%u, duplex=%u\n",
|
2015-03-12 04:45:16 +08:00
|
|
|
|
mstat.media_speed, mstat.media_duplex);
|
|
|
|
|
result = 0;
|
|
|
|
|
goto end;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
status= hpeGetMacAddress(port->handle, mac);
|
2015-11-04 20:02:33 +08:00
|
|
|
|
if (status != E_OK)
|
2015-03-12 04:45:16 +08:00
|
|
|
|
{
|
|
|
|
|
ECAT_PRINT_ERROR("hpeGetMacAddress failed with status %04x\n", status);
|
|
|
|
|
result = 0;
|
|
|
|
|
goto end;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* allocate 2 receive buffers and attach them */
|
|
|
|
|
status = hpeAllocateReceiveBufferSet(port->handle, &(port->rx_buffers), EC_MAXBUF, EC_BUFSIZE);
|
2015-11-04 20:02:33 +08:00
|
|
|
|
if (status != E_OK)
|
2015-03-12 04:45:16 +08:00
|
|
|
|
{
|
|
|
|
|
ECAT_PRINT_ERROR("hpeAllocateReceiveBufferSet failed with status %04x\n", status);
|
|
|
|
|
result = 0;
|
|
|
|
|
goto end;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
status = hpeAttachReceiveBufferSet(port->handle, port->rx_buffers);
|
2015-11-04 20:02:33 +08:00
|
|
|
|
if (status != E_OK)
|
2015-03-12 04:45:16 +08:00
|
|
|
|
{
|
|
|
|
|
ECAT_PRINT_ERROR("hpeAttachReceiveBufferSet failed with status %04x\n", status);
|
|
|
|
|
result = 0;
|
|
|
|
|
goto end;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (secondary)
|
|
|
|
|
{
|
2015-04-16 18:00:00 +08:00
|
|
|
|
/* secondary port struct available? */
|
2015-03-12 04:45:16 +08:00
|
|
|
|
if (port->redport)
|
|
|
|
|
{
|
|
|
|
|
/* when using secondary socket it is automatically a redundant setup */
|
|
|
|
|
port->redstate = ECT_RED_DOUBLE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
port->redstate = ECT_RED_NONE;
|
|
|
|
|
/* Init regions */
|
|
|
|
|
port->getindex_region = CreateRtRegion (PRIORITY_QUEUING);
|
|
|
|
|
port->rx_region = CreateRtRegion (PRIORITY_QUEUING);
|
|
|
|
|
port->tx_region = CreateRtRegion (PRIORITY_QUEUING);
|
|
|
|
|
port->lastidx = 0;
|
|
|
|
|
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);
|
2015-11-04 20:02:33 +08:00
|
|
|
|
}
|
|
|
|
|
|
2015-03-12 04:45:16 +08:00
|
|
|
|
/* 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;
|
|
|
|
|
// allocate 1 transmit buffer of two fragments and 500 bytes and attach it
|
|
|
|
|
port->tx_buffers[i] = (HPETXBUFFERSET *)AllocateRtMemory(sizeof(HPETXBUFFER) + sizeof(HPETXBUFFERSET));
|
|
|
|
|
port->tx_buffers[i]->buffer_count = 1;
|
|
|
|
|
port->tx_buffers[i]->buffers[0].fragment_count = 1;
|
|
|
|
|
// first fragment is the fixed header, second is dynamic data
|
|
|
|
|
port->tx_buffers[i]->buffers[0].fragments[0].ptr = port->txbuf[i];
|
|
|
|
|
port->tx_buffers[i]->buffers[0].fragments[0].size = EC_BUFSIZE;
|
|
|
|
|
port->tx_buffers[i]->buffers[0].fragments[0].paddr = 0;
|
|
|
|
|
}
|
|
|
|
|
ec_setupheader(&(port->txbuf2));
|
|
|
|
|
|
|
|
|
|
end:
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** Close sockets used
|
|
|
|
|
* @return 0
|
|
|
|
|
*/
|
2015-11-04 20:02:33 +08:00
|
|
|
|
int ecx_closenic(ecx_portt *port)
|
2015-03-12 04:45:16 +08:00
|
|
|
|
{
|
|
|
|
|
HPESTATUS status;
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
if (port->tx_buffers)
|
|
|
|
|
{
|
|
|
|
|
for (i = 0; i < EC_MAXBUF; i++)
|
|
|
|
|
{
|
|
|
|
|
FreeRtMemory(port->tx_buffers[i]);
|
|
|
|
|
port->tx_buffers[i] = NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (port->rx_buffers)
|
|
|
|
|
{
|
|
|
|
|
hpeFreeReceiveBufferSet(port->handle, &(port->rx_buffers));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
status= hpeClose(port->handle);
|
|
|
|
|
if (status != E_OK)
|
|
|
|
|
{
|
|
|
|
|
ECAT_PRINT_ERROR("hpeClose failed with status %04x\n", status);
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** Fill buffer with ethernet header structure.
|
2015-04-16 18:00:00 +08:00
|
|
|
|
* Destination MAC is always broadcast.
|
|
|
|
|
* Ethertype is always ETH_P_ECAT.
|
2015-03-12 04:45:16 +08:00
|
|
|
|
* @param[out] p = buffer
|
|
|
|
|
*/
|
2015-11-04 20:02:33 +08:00
|
|
|
|
void ec_setupheader(void *p)
|
2015-03-12 04:45:16 +08:00
|
|
|
|
{
|
|
|
|
|
ec_etherheadert *bp;
|
|
|
|
|
bp = (ec_etherheadert *)p;
|
|
|
|
|
bp->da0 = oshw_htons(0xffff);
|
|
|
|
|
bp->da1 = oshw_htons(0xffff);
|
|
|
|
|
bp->da2 = oshw_htons(0xffff);
|
|
|
|
|
bp->sa0 = oshw_htons(priMAC[0]);
|
|
|
|
|
bp->sa1 = oshw_htons(priMAC[1]);
|
|
|
|
|
bp->sa2 = oshw_htons(priMAC[2]);
|
|
|
|
|
bp->etype = oshw_htons(ETH_P_ECAT);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** Get new frame identifier index and allocate corresponding rx buffer.
|
|
|
|
|
* @return new index.
|
|
|
|
|
*/
|
|
|
|
|
int ecx_getindex(ecx_portt *port)
|
|
|
|
|
{
|
|
|
|
|
uint8 idx;
|
|
|
|
|
int cnt;
|
|
|
|
|
|
|
|
|
|
WaitForRtControl(port->getindex_region);
|
|
|
|
|
|
|
|
|
|
idx = port->lastidx + 1;
|
|
|
|
|
/* index can't be larger than buffer array */
|
2015-11-04 20:02:33 +08:00
|
|
|
|
if (idx >= EC_MAXBUF)
|
2015-03-12 04:45:16 +08:00
|
|
|
|
{
|
|
|
|
|
idx = 0;
|
|
|
|
|
}
|
|
|
|
|
cnt = 0;
|
|
|
|
|
/* try to find unused index */
|
|
|
|
|
while ((port->rxbufstat[idx] != EC_BUF_EMPTY) && (cnt < EC_MAXBUF))
|
|
|
|
|
{
|
|
|
|
|
idx++;
|
|
|
|
|
cnt++;
|
2015-11-04 20:02:33 +08:00
|
|
|
|
if (idx >= EC_MAXBUF)
|
2015-03-12 04:45:16 +08:00
|
|
|
|
{
|
|
|
|
|
idx = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
port->rxbufstat[idx] = EC_BUF_ALLOC;
|
|
|
|
|
if ( port->redstate != ECT_RED_NONE)
|
2015-11-04 20:02:33 +08:00
|
|
|
|
{
|
2015-03-12 04:45:16 +08:00
|
|
|
|
port->redport->rxbufstat[idx] = EC_BUF_ALLOC;
|
|
|
|
|
}
|
2015-11-04 20:02:33 +08:00
|
|
|
|
|
2015-03-12 04:45:16 +08:00
|
|
|
|
port->lastidx = idx;
|
|
|
|
|
ReleaseRtControl();
|
|
|
|
|
|
|
|
|
|
return idx;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** Set rx buffer status.
|
|
|
|
|
* @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] 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)
|
|
|
|
|
{
|
|
|
|
|
HPESTATUS status;
|
|
|
|
|
DWORD txstate;
|
|
|
|
|
int lp;
|
|
|
|
|
ec_stackT *stack;
|
|
|
|
|
int result = 0;
|
|
|
|
|
int retries = 1024;
|
|
|
|
|
|
|
|
|
|
if (!stacknumber)
|
|
|
|
|
{
|
|
|
|
|
stack = &(port->stack);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
stack = &(port->redport->stack);
|
|
|
|
|
}
|
|
|
|
|
lp = (*stack->txbuflength)[idx];
|
|
|
|
|
port->tx_buffers[idx]->buffer_count = 1;
|
|
|
|
|
port->tx_buffers[idx]->buffers[0].fragments[0].size = lp;
|
|
|
|
|
|
|
|
|
|
// wait for transmit to complete
|
|
|
|
|
do
|
|
|
|
|
{
|
|
|
|
|
result = hpeGetTransmitterState(port->handle, &txstate) == E_OK && txstate == HPE_TXBUSY;
|
|
|
|
|
} while (result && retries-- > 0);
|
|
|
|
|
|
|
|
|
|
if (result)
|
|
|
|
|
{
|
|
|
|
|
result = -1;
|
|
|
|
|
goto end;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
log_RT_event('S',(WORD)2);
|
|
|
|
|
status = hpeAttachTransmitBufferSet(port->handle, port->tx_buffers[idx]);
|
2015-11-04 20:02:33 +08:00
|
|
|
|
if (status != E_OK)
|
2015-03-12 04:45:16 +08:00
|
|
|
|
{
|
|
|
|
|
result = -2;
|
|
|
|
|
goto end;
|
|
|
|
|
}
|
|
|
|
|
log_RT_event('S',(WORD)3);
|
|
|
|
|
|
|
|
|
|
status = hpeStartTransmitter(port->handle);
|
2015-11-04 20:02:33 +08:00
|
|
|
|
if (status != E_OK)
|
2015-03-12 04:45:16 +08:00
|
|
|
|
{
|
|
|
|
|
result = -3;
|
|
|
|
|
goto end;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
log_RT_event('S',(WORD)4);
|
|
|
|
|
(*stack->rxbufstat)[idx] = EC_BUF_TX;
|
|
|
|
|
result = lp;
|
|
|
|
|
|
|
|
|
|
end:
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** Transmit buffer over socket (non blocking).
|
|
|
|
|
* @param[in] idx = index in tx buffer array
|
|
|
|
|
* @return socket send result
|
|
|
|
|
*/
|
|
|
|
|
int ecx_outframe_red(ecx_portt *port, int idx)
|
|
|
|
|
{
|
|
|
|
|
HPESTATUS status;
|
|
|
|
|
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)
|
2015-11-04 20:02:33 +08:00
|
|
|
|
{
|
2015-03-12 04:45:16 +08:00
|
|
|
|
ehp = (ec_etherheadert *)&(port->txbuf2);
|
|
|
|
|
/* use dummy frame for secondary socket transmit (BRD) */
|
|
|
|
|
datagramP = (ec_comt*)&(port->txbuf2)[ETH_HEADERSIZE];
|
|
|
|
|
/* write index to frame */
|
|
|
|
|
datagramP->index = idx;
|
|
|
|
|
/* rewrite MAC source address 1 to secondary */
|
|
|
|
|
ehp->sa1 = oshw_htons(secMAC[1]);
|
|
|
|
|
/* transmit over secondary socket */
|
|
|
|
|
//send(sockhandle2, &ec_txbuf2, ec_txbuflength2 , 0);
|
|
|
|
|
// OBS! redundant not ACTIVE for BFIN, just added to compile
|
|
|
|
|
//ASSERT (0);
|
|
|
|
|
hpeAttachTransmitBufferSet(port->redport->handle, port->tx_buffers[idx]);
|
|
|
|
|
status = hpeStartTransmitter(port->redport->handle);
|
|
|
|
|
port->redport->rxbufstat[idx] = EC_BUF_TX;
|
2015-11-04 20:02:33 +08:00
|
|
|
|
}
|
|
|
|
|
|
2015-03-12 04:45:16 +08:00
|
|
|
|
return rval;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** Non blocking read of socket. Put frame in temporary buffer.
|
|
|
|
|
* @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)
|
|
|
|
|
{
|
|
|
|
|
HPEBUFFER *rxbuffer;
|
|
|
|
|
HPESTATUS status;
|
|
|
|
|
ec_stackT *stack;
|
|
|
|
|
int bytesrx = 0;
|
|
|
|
|
|
|
|
|
|
if (!stacknumber)
|
|
|
|
|
{
|
|
|
|
|
stack = &(port->stack);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
stack = &(port->redport->stack);
|
|
|
|
|
}
|
2015-11-04 20:02:33 +08:00
|
|
|
|
|
2015-03-12 04:45:16 +08:00
|
|
|
|
log_RT_event('R',(WORD)2);
|
|
|
|
|
|
|
|
|
|
status = hpeGetReceiveBuffer(port->handle, &rxbuffer);
|
2015-11-04 20:02:33 +08:00
|
|
|
|
if (status == E_OK)
|
2015-03-12 04:45:16 +08:00
|
|
|
|
{
|
|
|
|
|
memcpy(stack->tempbuf,rxbuffer->ptr, rxbuffer->used);
|
2015-11-04 20:02:33 +08:00
|
|
|
|
bytesrx = rxbuffer->used;
|
2015-03-12 04:45:16 +08:00
|
|
|
|
port->tempinbufs = bytesrx;
|
|
|
|
|
// TODO case no interrupt
|
|
|
|
|
}
|
|
|
|
|
log_RT_event('R',(WORD)3);
|
|
|
|
|
|
|
|
|
|
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 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.
|
2015-11-04 20:02:33 +08:00
|
|
|
|
*
|
2015-03-12 04:45:16 +08:00
|
|
|
|
* @param[in] idx = requested index of frame
|
|
|
|
|
* @param[in] stacknumber = 0=primary 1=secondary stack
|
|
|
|
|
* @return Workcounter if a frame is found with corresponding index, otherwise
|
|
|
|
|
* EC_NOFRAME or EC_OTHERFRAME.
|
|
|
|
|
*/
|
|
|
|
|
int ecx_inframe(ecx_portt *port, int idx, int stacknumber)
|
|
|
|
|
{
|
|
|
|
|
uint16 l;
|
|
|
|
|
int rval;
|
|
|
|
|
uint8 idxf;
|
|
|
|
|
ec_etherheadert *ehp;
|
|
|
|
|
ec_comt *ecp;
|
|
|
|
|
ec_stackT *stack;
|
|
|
|
|
ec_bufT *rxbuf;
|
|
|
|
|
|
|
|
|
|
if (!stacknumber)
|
|
|
|
|
{
|
|
|
|
|
stack = &(port->stack);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
stack = &(port->redport->stack);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
rval = EC_NOFRAME;
|
|
|
|
|
rxbuf = &(*stack->rxbuf)[idx];
|
|
|
|
|
/* check if requested index is already in buffer ? */
|
2015-11-04 20:02:33 +08:00
|
|
|
|
if ((idx < EC_MAXBUF) && ( (*stack->rxbufstat)[idx] == EC_BUF_RCVD))
|
2015-03-12 04:45:16 +08:00
|
|
|
|
{
|
|
|
|
|
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;
|
|
|
|
|
}
|
2015-11-04 20:02:33 +08:00
|
|
|
|
else
|
2015-03-12 04:45:16 +08:00
|
|
|
|
{
|
2016-05-14 21:16:45 +08:00
|
|
|
|
WaitForRtControl(port->rx_region);
|
2015-03-12 04:45:16 +08:00
|
|
|
|
/* non blocking call to retrieve frame from socket */
|
2015-11-04 20:02:33 +08:00
|
|
|
|
if (ecx_recvpkt(port, stacknumber))
|
2015-03-12 04:45:16 +08:00
|
|
|
|
{
|
|
|
|
|
rval = EC_OTHERFRAME;
|
|
|
|
|
ehp = (ec_etherheadert*)(stack->tempbuf);
|
|
|
|
|
/* check if it is an EtherCAT frame */
|
|
|
|
|
if (ehp->etype == oshw_htons(ETH_P_ECAT))
|
|
|
|
|
{
|
2015-11-04 20:02:33 +08:00
|
|
|
|
ecp =(ec_comt*)(&(*stack->tempbuf)[ETH_HEADERSIZE]);
|
2015-03-12 04:45:16 +08:00
|
|
|
|
l = etohs(ecp->elength) & 0x0fff;
|
|
|
|
|
idxf = ecp->index;
|
|
|
|
|
/* found index equals reqested index ? */
|
2015-11-04 20:02:33 +08:00
|
|
|
|
if (idxf == idx)
|
2015-03-12 04:45:16 +08:00
|
|
|
|
{
|
|
|
|
|
/* 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);
|
|
|
|
|
}
|
2015-11-04 20:02:33 +08:00
|
|
|
|
else
|
2015-03-12 04:45:16 +08:00
|
|
|
|
{
|
|
|
|
|
/* 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);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-05-14 21:16:45 +08:00
|
|
|
|
ReleaseRtControl();
|
2015-03-12 04:45:16 +08:00
|
|
|
|
}
|
2015-11-04 20:02:33 +08:00
|
|
|
|
|
2015-03-12 04:45:16 +08:00
|
|
|
|
/* 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,
|
2015-11-04 20:02:33 +08:00
|
|
|
|
* how to reroute the original packet to get the data in an other try.
|
2015-03-12 04:45:16 +08:00
|
|
|
|
*
|
|
|
|
|
* @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;
|
2015-11-04 20:02:33 +08:00
|
|
|
|
|
2015-03-12 04:45:16 +08:00
|
|
|
|
/* if not in redundant mode then always assume secondary is OK */
|
|
|
|
|
if (port->redstate == ECT_RED_NONE)
|
|
|
|
|
{
|
|
|
|
|
wkc2 = 0;
|
|
|
|
|
}
|
2015-11-04 20:02:33 +08:00
|
|
|
|
do
|
2015-03-12 04:45:16 +08:00
|
|
|
|
{
|
|
|
|
|
/* 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)
|
2015-11-04 20:02:33 +08:00
|
|
|
|
{
|
2015-03-12 04:45:16 +08:00
|
|
|
|
/* only read frame if not already in */
|
|
|
|
|
if (wkc2 <= EC_NOFRAME)
|
|
|
|
|
{
|
|
|
|
|
wkc2 = ecx_inframe(port, idx, 1);
|
|
|
|
|
}
|
2015-11-04 20:02:33 +08:00
|
|
|
|
}
|
|
|
|
|
/* wait for both frames to arrive or timeout */
|
2015-03-12 04:45:16 +08:00
|
|
|
|
} 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];
|
2015-11-04 20:02:33 +08:00
|
|
|
|
|
2015-03-12 04:45:16 +08:00
|
|
|
|
/* 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;
|
2015-11-04 20:02:33 +08:00
|
|
|
|
}
|
2015-03-12 04:45:16 +08:00
|
|
|
|
/* primary socket got nothing or primary frame, and secondary socket got secondary frame */
|
2015-11-04 20:02:33 +08:00
|
|
|
|
/* we need to resend TX packet */
|
2015-03-12 04:45:16 +08:00
|
|
|
|
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) )
|
2015-11-04 20:02:33 +08:00
|
|
|
|
{
|
2015-03-12 04:45:16 +08:00
|
|
|
|
/* 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);
|
2015-11-04 20:02:33 +08:00
|
|
|
|
do
|
2015-03-12 04:45:16 +08:00
|
|
|
|
{
|
|
|
|
|
/* retrieve frame */
|
|
|
|
|
wkc2 = ecx_inframe(port, idx, 1);
|
|
|
|
|
} while ((wkc2 <= EC_NOFRAME) && !osal_timer_is_expired(&timer2));
|
|
|
|
|
if (wkc2 > EC_NOFRAME)
|
2015-11-04 20:02:33 +08:00
|
|
|
|
{
|
2015-03-12 04:45:16 +08:00
|
|
|
|
/* copy secondary result to primary rx buffer */
|
|
|
|
|
memcpy(&(port->rxbuf[idx]), &(port->redport->rxbuf[idx]), port->txbuflength[idx] - ETH_HEADERSIZE);
|
|
|
|
|
wkc = wkc2;
|
2015-11-04 20:02:33 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
2015-03-12 04:45:16 +08:00
|
|
|
|
}
|
2015-11-04 20:02:33 +08:00
|
|
|
|
|
2015-03-12 04:45:16 +08:00
|
|
|
|
/* return WKC or EC_NOFRAME */
|
|
|
|
|
return wkc;
|
2015-11-04 20:02:33 +08:00
|
|
|
|
}
|
2015-03-12 04:45:16 +08:00
|
|
|
|
|
|
|
|
|
/** Blocking receive frame function. Calls ec_waitinframe_red().
|
|
|
|
|
* @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;
|
2015-11-04 20:02:33 +08:00
|
|
|
|
|
2015-03-12 04:45:16 +08:00
|
|
|
|
if (timeout == 0 && (port->redstate == ECT_RED_NONE))
|
|
|
|
|
{
|
|
|
|
|
int loop_cnt = 0;
|
|
|
|
|
/* Allow us to consume MAX buffer number of frames */
|
|
|
|
|
do
|
|
|
|
|
{
|
|
|
|
|
wkc = ecx_inframe(port, idx, 0);
|
|
|
|
|
loop_cnt++;
|
|
|
|
|
} while (wkc <= EC_NOFRAME && (loop_cnt <= EC_MAXBUF));
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2015-11-04 20:02:33 +08:00
|
|
|
|
osal_timer_start (&timer, timeout);
|
2015-03-12 04:45:16 +08:00
|
|
|
|
wkc = ecx_waitinframe_red(port, idx, &timer);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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] 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;
|
|
|
|
|
|
|
|
|
|
osal_timer_start (&timer1, timeout);
|
|
|
|
|
/* tx frame on primary and if in redundant mode a dummy on secondary */
|
|
|
|
|
ecx_outframe_red(port, idx);
|
|
|
|
|
wkc = ecx_waitinframe_red(port, idx, &timer1);
|
2015-11-04 20:02:33 +08:00
|
|
|
|
|
2015-03-12 04:45:16 +08:00
|
|
|
|
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
|