feat(motor): 自适应 PDO 窗口与通道活动指示,新增原生 SOEM 时序诊断

后端 PDO 循环改为按在线槽收缩状态窗口,稀疏总线下单槽每周期都能刷新,
并补上尾部槽的发现扫描游标;新增自旋节拍源(阈值/显式覆盖)、P 核识别
与线程级钉核,自旋期间释放 GIL 以免 HTTP 线程被饿死;启动时先关停旧实例,
避免两个进程争用 Npcap 适配器。

记录每拍完整时间线、GC stop-the-world 事件与网格跳拍数,经 /api/motor/dbg
导出用于归因固定频率的超时;配
This commit is contained in:
cbb
2026-09-05 01:20:14 +08:00
parent 979da72181
commit 79633a2b0f
121 changed files with 33088 additions and 75 deletions
+561
View File
@@ -0,0 +1,561 @@
/*
* Licensed under the GNU General Public License version 2 with exceptions. See
* LICENSE file in the project root for full license information
*/
/** \file
* \brief
* EtherCAT RAW socket driver.
*
* Low level interface functions to send and receive EtherCAT packets.
* EtherCAT has the property that packets are only send by the master,
* and the send packets always return in the receive buffer.
* There can be multiple packets "on the wire" before they return.
* To combine the received packets with the original send packets a buffer
* system is installed. The identifier is put in the index item of the
* EtherCAT header. The index is stored and compared when a frame is received.
* If there is a match the packet can be combined with the transmit packet
* and returned to the higher level function.
*
* The socket layer can exhibit a reversal in the packet order (rare).
* If the Tx order is A-B-C the return order could be A-C-B. The indexed buffer
* will reorder the packets automatically.
*
* The "redundant" option will configure two sockets and two NIC interfaces.
* Slaves are connected to both interfaces, one on the IN port and one on the
* OUT port. Packets are send via both interfaces. Any one of the connections
* (also an interconnect) can be removed and the slaves are still serviced with
* packets. The software layer will detect the possible failure modes and
* compensate. If needed the packets from interface A are resent through interface B.
* This layer if fully transparent for the higher layers.
*/
#include <sys/time.h>
#include <time.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <string.h>
#include <assert.h>
#include "oshw.h"
#include "osal.h"
#include "nicdrv.h"
#include "ee.h"
#include "intel_i210.h"
#include "ee_x86_64_tsc.h"
/** Redundancy modes */
enum
{
/** No redundancy, single NIC mode */
ECT_RED_NONE,
/** Double redundant NIC connection */
ECT_RED_DOUBLE
};
/** Primary source MAC address used for EtherCAT.
* This address is not the MAC address used from the NIC.
* EtherCAT does not care about MAC addressing, but it is used here to
* differentiate the route the packet traverses through the EtherCAT
* segment. This is needed to find out the packet flow in redundant
* configurations. */
const uint16 priMAC[3] = { 0x0201, 0x0101, 0x0101 };
/** Secondary source MAC address used for EtherCAT. */
const uint16 secMAC[3] = { 0x0604, 0x0404, 0x0404 };
/** second MAC word is used for identification */
#define RX_PRIM priMAC[1]
/** second MAC word is used for identification */
#define RX_SEC secMAC[1]
void ee_port_lock(void);
void ee_port_unlock(void);
static inline void ecx_clear_rxbufstat(int *rxbufstat)
{
int i;
for(i = 0; i < EC_MAXBUF; i++)
rxbufstat[i] = EC_BUF_EMPTY;
}
/** Basic setup to connect NIC to socket.
* @param[in] port = port context struct
* @param[in] ifname = Name of NIC device, f.e. "eth0"
* @param[in] secondary = if >0 then use secondary stack instead of primary
* @return >0 if succeeded
*/
int ecx_setupnic(ecx_portt *port, const char *ifname, int secondary)
{
int d;
struct eth_device *dev;
OSEE_PRINT("ecx_setupnic() searching %s...\n", ifname);
for (d = 0;; ++d) {
dev = eth_get_device(d);
if (dev == NULL)
break; // ERROR: device not found
if (!strncmp(dev->name, ifname, MAX_DEVICE_NAME)){
// Device found
int i;
eth_setup_device(d, 1, 100);
port->dev_id = d;
port->sockhandle = -1;
port->lastidx = 0;
port->redstate = ECT_RED_NONE;
port->stack.sock = &(port->sockhandle);
port->stack.txbuf = &(port->txbuf);
port->stack.txbuflength = &(port->txbuflength);
port->stack.tempbuf = &(port->tempinbuf);
port->stack.rxbuf = &(port->rxbuf);
port->stack.rxbufstat = &(port->rxbufstat);
port->stack.rxsa = &(port->rxsa);
ecx_clear_rxbufstat(&(port->rxbufstat[0]));
/* setup ethernet headers in tx buffers so we don't have to repeat it */
for (i = 0; i < EC_MAXBUF; i++)
{
ec_setupheader(&(port->txbuf[i]));
port->rxbufstat[i] = EC_BUF_EMPTY;
}
ec_setupheader(&(port->txbuf2));
break; // device found
}
}
return (dev != NULL);
}
/** Close sockets used
* @param[in] port = port context struct
* @return 0
*/
inline int ecx_closenic(ecx_portt *port)
{
// Nothing to do
return 0;
}
/** Fill buffer with ethernet header structure.
* Destination MAC is always broadcast.
* Ethertype is always ETH_P_ECAT.
* @param[out] p = buffer
*/
void ec_setupheader(void *p)
{
ec_etherheadert *bp;
bp = p;
bp->da0 = oshw_htons(0xffff);
bp->da1 = oshw_htons(0xffff);
bp->da2 = oshw_htons(0xffff);
bp->sa0 = oshw_htons(priMAC[0]);
bp->sa1 = oshw_htons(priMAC[1]);
bp->sa2 = oshw_htons(priMAC[2]);
bp->etype = oshw_htons(ETH_P_ECAT);
}
/** Get new frame identifier index and allocate corresponding rx buffer.
* @param[in] port = port context struct
* @return new index.
*/
uint8 ecx_getindex(ecx_portt *port)
{
uint8 idx;
uint8 cnt = 0;
ee_port_lock();
idx = port->lastidx + 1;
/* index can't be larger than buffer array */
if (idx >= EC_MAXBUF)
idx = 0;
/* try to find unused index */
while ((port->rxbufstat[idx] != EC_BUF_EMPTY) && (cnt < EC_MAXBUF)) {
idx++;
cnt++;
if (idx >= EC_MAXBUF)
idx = 0;
}
port->rxbufstat[idx] = EC_BUF_ALLOC;
if (port->redstate != ECT_RED_NONE)
port->redport->rxbufstat[idx] = EC_BUF_ALLOC;
port->lastidx = idx;
ee_port_unlock();
return idx;
}
/** Set rx buffer status.
* @param[in] port = port context struct
* @param[in] idx = index in buffer array
* @param[in] bufstat = status to set
*/
void ecx_setbufstat(ecx_portt *port, uint8 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, uint8 idx, int stacknumber)
{
int lp;
ec_stackT *stack;
if (!stacknumber)
stack = &(port->stack);
else
stack = &(port->redport->stack);
lp = (*stack->txbuflength)[idx];
(*stack->rxbufstat)[idx] = EC_BUF_TX;
eth_send_packet(port->dev_id, (*stack->txbuf)[idx], lp, 1);
return 1;
}
/** Transmit buffer over socket (non blocking).
* @param[in] port = port context struct
* @param[in] idx = index in tx buffer array
* @return socket send result
*/
int ecx_outframe_red(ecx_portt *port, uint8 idx)
{
ec_comt *datagramP;
ec_etherheadert *ehp;
int rval;
ehp = (ec_etherheadert *)&(port->txbuf[idx]);
/* rewrite MAC source address 1 to primary */
ehp->sa1 = oshw_htons(priMAC[1]);
/* transmit over primary socket*/
rval = ecx_outframe(port, idx, 0);
if (port->redstate != ECT_RED_NONE) {
ee_port_lock();
ehp = (ec_etherheadert *)&(port->txbuf2);
/* use dummy frame for secondary socket transmit (BRD) */
datagramP = (ec_comt*)&(port->txbuf2[ETH_HEADERSIZE]);
/* write index to frame */
datagramP->index = idx;
/* rewrite MAC source address 1 to secondary */
ehp->sa1 = oshw_htons(secMAC[1]);
/* transmit over secondary socket */
port->redport->rxbufstat[idx] = EC_BUF_TX;
eth_send_packet(port->dev_id, &(port->txbuf2), port->txbuflength2, 1);
ee_port_unlock();
}
return rval;
}
/** Non blocking read of socket. Put frame in temporary buffer.
* @param[in] port = port context struct
* @param[in] stacknumber = 0=primary 1=secondary stack
* @return >0 if frame is available and read
*/
static int ecx_recvpkt(ecx_portt *port, int stacknumber)
{
int lp, bytesrx;
ec_stackT *stack;
if (!stacknumber)
stack = &(port->stack);
else
stack = &(port->redport->stack);
lp = sizeof(port->tempinbuf);
bytesrx = eth_receive_packet(port->dev_id, (*stack->tempbuf), lp, 1, 0);
port->tempinbufs = bytesrx;
return (bytesrx > 0);
}
/** Non blocking receive frame function. Uses RX buffer and index to combine
* read frame with transmitted frame. To compensate for received frames that
* are out-of-order all frames are stored in their respective indexed buffer.
* If a frame was placed in the buffer previously, the function retrieves it
* from that buffer index without calling ec_recvpkt. If the requested index
* is not already in the buffer it calls ec_recvpkt to fetch it. There are
* three options now, 1 no frame read, so exit. 2 frame read but other
* than requested index, store in buffer and exit. 3 frame read with matching
* index, store in buffer, set completed flag in buffer status and exit.
*
* @param[in] port = port context struct
* @param[in] idx = requested index of frame
* @param[in] stacknumber = 0=primary 1=secondary stack
* @return Workcounter if a frame is found with corresponding index, otherwise
* EC_NOFRAME or EC_OTHERFRAME.
*/
int ecx_inframe(ecx_portt *port, uint8 idx, int stacknumber)
{
uint16 l;
int rval;
uint8 idxf;
ec_etherheadert *ehp;
ec_comt *ecp;
ec_stackT *stack;
ec_bufT *rxbuf;
if (!stacknumber)
stack = &(port->stack);
else
stack = &(port->redport->stack);
rval = EC_NOFRAME;
rxbuf = &(*stack->rxbuf)[idx];
/* check if requested index is already in buffer ? */
if ((idx < EC_MAXBUF) && ((*stack->rxbufstat)[idx] == EC_BUF_RCVD)) {
l = (*rxbuf)[0] + ((uint16)((*rxbuf)[1] & 0x0f) << 8);
/* return WKC */
rval = ((*rxbuf)[l] + ((uint16)(*rxbuf)[l + 1] << 8));
/* mark as completed */
(*stack->rxbufstat)[idx] = EC_BUF_COMPLETE;
} else {
ee_port_lock();
/* non blocking call to retrieve frame from socket */
while (1) {
if (ecx_recvpkt(port, stacknumber)) {
rval = EC_OTHERFRAME;
ehp =(ec_etherheadert*)(stack->tempbuf);
/* check if it is an EtherCAT frame */
if (ehp->etype == oshw_htons(ETH_P_ECAT)) {
ecp =(ec_comt*)(&(*stack->tempbuf)[ETH_HEADERSIZE]);
l = etohs(ecp->elength) & 0x0fff;
idxf = ecp->index;
/* found index equals requested index ? */
if (idxf == idx) {
/* yes, put it in the buffer array (strip ethernet header) */
memcpy(rxbuf, &(*stack->tempbuf)[ETH_HEADERSIZE], (*stack->txbuflength)[idx] - ETH_HEADERSIZE);
/* return WKC */
rval = ((*rxbuf)[l] + ((uint16)((*rxbuf)[l + 1]) << 8));
/* mark as completed */
(*stack->rxbufstat)[idx] = EC_BUF_COMPLETE;
/* store MAC source word 1 for redundant routing info */
(*stack->rxsa)[idx] = oshw_ntohs(ehp->sa1);
break;
} else if (idxf < EC_MAXBUF && (*stack->rxbufstat)[idxf] == EC_BUF_TX) {
rxbuf = &(*stack->rxbuf)[idxf];
/* put it in the buffer array (strip ethernet header) */
memcpy(rxbuf, &(*stack->tempbuf)[ETH_HEADERSIZE], (*stack->txbuflength)[idxf] - ETH_HEADERSIZE);
/* mark as received */
(*stack->rxbufstat)[idxf] = EC_BUF_RCVD;
(*stack->rxsa)[idxf] = oshw_ntohs(ehp->sa1);
break;
} else {
OSEE_PRINT("ecx_inframe(): WARNING: strange things happened\n");
/* strange things happened */
}
} else {
OSEE_PRINT("ecx_inframe(): WARNING it is NOT an EtherCAT frame!\n");
}
} else {
// WARNING: no messages received.
break;
}
}
ee_port_unlock();
}
/* WKC if matching frame found */
return rval;
}
/** Blocking redundant receive frame function. If redundant mode is not active then
* it skips the secondary stack and redundancy functions. In redundant mode it waits
* for both (primary and secondary) frames to come in. The result goes in an decision
* tree that decides, depending on the route of the packet and its possible missing arrival,
* how to reroute the original packet to get the data in an other try.
*
* @param[in] port = port context struct
* @param[in] idx = requested index of frame
* @param[in] timer = absolute timeout time
* @return Workcounter if a frame is found with corresponding index, otherwise
* EC_NOFRAME.
*/
static int ecx_waitinframe_red(ecx_portt *port, uint8 idx, osal_timert *timer)
{
osal_timert timer2;
int wkc = EC_NOFRAME;
int wkc2 = EC_NOFRAME;
int primrx, secrx;
/* if not in redundant mode then always assume secondary is OK */
if (port->redstate == ECT_RED_NONE)
wkc2 = 0;
do {
/* only read frame if not already in */
if (wkc <= EC_NOFRAME)
wkc = ecx_inframe(port, idx, 0);
/* only try secondary if in redundant mode */
if (port->redstate != ECT_RED_NONE) {
/* only read frame if not already in */
if (wkc2 <= EC_NOFRAME)
wkc2 = ecx_inframe(port, idx, 1);
}
/* wait for both frames to arrive or timeout */
} while (((wkc <= EC_NOFRAME) || (wkc2 <= EC_NOFRAME)) && !osal_timer_is_expired(timer));
/* only do redundant functions when in redundant mode */
if (port->redstate != ECT_RED_NONE) {
/* primrx if the received MAC source on primary socket */
primrx = 0;
if (wkc > EC_NOFRAME) primrx = port->rxsa[idx];
/* secrx if the received MAC source on psecondary socket */
secrx = 0;
if (wkc2 > EC_NOFRAME) secrx = port->redport->rxsa[idx];
/* primary socket got secondary frame and secondary socket got primary frame */
/* normal situation in redundant mode */
if ( ((primrx == RX_SEC) && (secrx == RX_PRIM)) ) {
/* copy secondary buffer to primary */
memcpy(&(port->rxbuf[idx]), &(port->redport->rxbuf[idx]), port->txbuflength[idx] - ETH_HEADERSIZE);
wkc = wkc2;
}
/* primary socket got nothing or primary frame, and secondary socket got secondary frame */
/* we need to resend TX packet */
if ( ((primrx == 0) && (secrx == RX_SEC)) ||
((primrx == RX_PRIM) && (secrx == RX_SEC)) ) {
/* If both primary and secondary have partial connection retransmit the primary received
* frame over the secondary socket. The result from the secondary received frame is a combined
* frame that traversed all slaves in standard order. */
if ( (primrx == RX_PRIM) && (secrx == RX_SEC) ) {
/* copy primary rx to tx buffer */
memcpy(&(port->txbuf[idx][ETH_HEADERSIZE]), &(port->rxbuf[idx]), port->txbuflength[idx] - ETH_HEADERSIZE);
}
osal_timer_start (&timer2, EC_TIMEOUTRET);
/* resend secondary tx */
ecx_outframe(port, idx, 1);
do {
/* retrieve frame */
wkc2 = ecx_inframe(port, idx, 1);
} while ((wkc2 <= EC_NOFRAME) && !osal_timer_is_expired(&timer2));
if (wkc2 > EC_NOFRAME) {
/* copy secondary result to primary rx buffer */
memcpy(&(port->rxbuf[idx]), &(port->redport->rxbuf[idx]), port->txbuflength[idx] - ETH_HEADERSIZE);
wkc = wkc2;
}
}
}
/* return WKC or EC_NOFRAME */
return wkc;
}
/** Blocking receive frame function. Calls ec_waitinframe_red().
* @param[in] port = port context struct
* @param[in] idx = requested index of frame
* @param[in] timeout = timeout in us
* @return Workcounter if a frame is found with corresponding index, otherwise
* EC_NOFRAME.
*/
int ecx_waitinframe(ecx_portt *port, uint8 idx, int timeout)
{
int wkc;
osal_timert timer;
osal_timer_start (&timer, timeout);
wkc = ecx_waitinframe_red(port, idx, &timer);
return wkc;
}
/** Blocking send and receive frame function. Used for non processdata frames.
* A datagram is build into a frame and transmitted via this function. It waits
* for an answer and returns the workcounter. The function retries if time is
* left and the result is WKC=0 or no frame received.
*
* The function calls ec_outframe_red() and ec_waitinframe_red().
*
* @param[in] port = port context struct
* @param[in] idx = index of frame
* @param[in] timeout = timeout in us
* @return Workcounter or EC_NOFRAME
*/
int ecx_srconfirm(ecx_portt *port, uint8 idx, int timeout)
{
int wkc = EC_NOFRAME;
osal_timert timer1, timer2;
osal_timer_start (&timer1, timeout);
do {
/* tx frame on primary and if in redundant mode a dummy on secondary */
ecx_outframe_red(port, idx);
if (timeout < EC_TIMEOUTRET) {
osal_timer_start (&timer2, timeout);
} else {
/* normally use partial timeout for rx */
osal_timer_start (&timer2, EC_TIMEOUTRET);
}
/* get frame from primary or if in redundant mode possibly
from secondary */
wkc = ecx_waitinframe_red(port, idx, &timer2);
/* wait for answer with WKC>=0 or otherwise retry until timeout */
} while ((wkc <= EC_NOFRAME) && !osal_timer_is_expired (&timer1));
return wkc;
}
#ifdef EC_VER1
int ec_setupnic(const char *ifname, int secondary)
{
return ecx_setupnic(&ecx_port, ifname, secondary);
}
int ec_closenic(void)
{
return ecx_closenic(&ecx_port);
}
int ec_getindex(void)
{
return ecx_getindex(&ecx_port);
}
void ec_setbufstat(uint8 idx, int bufstat)
{
ecx_setbufstat(&ecx_port, idx, bufstat);
}
int ec_outframe(uint8 idx, int stacknumber)
{
return ecx_outframe(&ecx_port, idx, stacknumber);
}
int ec_outframe_red(uint8 idx)
{
return ecx_outframe_red(&ecx_port, idx);
}
int ec_inframe(uint8 idx, int stacknumber)
{
return ecx_inframe(&ecx_port, idx, stacknumber);
}
int ec_waitinframe(uint8 idx, int timeout)
{
return ecx_waitinframe(&ecx_port, idx, timeout);
}
int ec_srconfirm(uint8 idx, int timeout)
{
return ecx_srconfirm(&ecx_port, idx, timeout);
}
#endif
+122
View File
@@ -0,0 +1,122 @@
/*
* Licensed under the GNU General Public License version 2 with exceptions. See
* LICENSE file in the project root for full license information
*/
/** \file
* \brief
* Headerfile for nicdrv.c
*/
#ifndef _nicdrvh_
#define _nicdrvh_
#ifdef __cplusplus
extern "C"
{
#endif
typedef struct
{
/** socket connection used */
int *sock;
/** tx buffer */
ec_bufT (*txbuf)[EC_MAXBUF];
/** tx buffer lengths */
int (*txbuflength)[EC_MAXBUF];
/** temporary receive buffer */
ec_bufT *tempbuf;
/** rx buffers */
ec_bufT (*rxbuf)[EC_MAXBUF];
/** rx buffer status fields */
int (*rxbufstat)[EC_MAXBUF];
/** received MAC source address (middle word) */
int (*rxsa)[EC_MAXBUF];
} ec_stackT;
/** pointer structure to buffers for redundant port */
typedef struct
{
ec_stackT stack;
int sockhandle;
/** rx buffers */
ec_bufT rxbuf[EC_MAXBUF];
/** rx buffer status */
int rxbufstat[EC_MAXBUF];
/** rx MAC source address */
int rxsa[EC_MAXBUF];
/** temporary rx buffer */
ec_bufT tempinbuf;
} ecx_redportt;
/** pointer structure to buffers, vars and mutexes for port instantiation */
typedef struct
{
ec_stackT stack;
int sockhandle;
/** rx buffers */
ec_bufT rxbuf[EC_MAXBUF];
/** rx buffer status */
int rxbufstat[EC_MAXBUF];
/** rx MAC source address */
int rxsa[EC_MAXBUF];
/** temporary rx buffer */
ec_bufT tempinbuf;
/** temporary rx buffer status */
int tempinbufs;
/** transmit buffers */
ec_bufT txbuf[EC_MAXBUF];
/** transmit buffer lengths */
int txbuflength[EC_MAXBUF];
/** temporary tx buffer */
ec_bufT txbuf2;
/** temporary tx buffer length */
int txbuflength2;
/** last used frame index */
uint8 lastidx;
/** current redundancy state */
int redstate;
/** pointer to redundancy port and buffers */
ecx_redportt *redport;
/** Device id in the device pool */
int dev_id;
// TODO: add mutex support
} ecx_portt;
extern const uint16 priMAC[3];
extern const uint16 secMAC[3];
#ifdef EC_VER1
extern ecx_portt ecx_port;
extern ecx_redportt ecx_redport;
int ec_setupnic(const char * ifname, int secondary);
int ec_closenic(void);
void ec_setbufstat(uint8 idx, int bufstat);
uint8 ec_getindex(void);
int ec_outframe(uint8 idx, int sock);
int ec_outframe_red(uint8 idx);
int ec_waitinframe(uint8 idx, int timeout);
int ec_srconfirm(uint8 idx,int timeout);
int ec_inframe(uint8 idx, int stacknumber);
#endif
void ec_setupheader(void *p);
int ecx_setupnic(ecx_portt *port, const char *ifname, int secondary);
int ecx_closenic(ecx_portt *port);
void ecx_setbufstat(ecx_portt *port, uint8 idx, int bufstat);
uint8 ecx_getindex(ecx_portt *port);
int ecx_outframe(ecx_portt *port, uint8 idx, int sock);
int ecx_outframe_red(ecx_portt *port, uint8 idx);
int ecx_waitinframe(ecx_portt *port, uint8 idx, int timeout);
int ecx_srconfirm(ecx_portt *port, uint8 idx,int timeout);
int ecx_inframe(ecx_portt *port, uint8 idx, int stacknumber);
#ifdef __cplusplus
}
#endif
#endif
+79
View File
@@ -0,0 +1,79 @@
/*
* Licensed under the GNU General Public License version 2 with exceptions. See
* LICENSE file in the project root for full license information
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
#include "oshw.h"
#include "intel_i210.h"
#include "ethercat.h"
#if !defined(__gnu_linux__)
#include <machine/endian.h>
#else
#include <endian.h>
#define __htons(x) htobe16(x)
#define __ntohs(x) be16toh(x)
#endif
ec_adaptert adapters [DEVS_MAX_NB];
/**
* Host to Network byte order (i.e. to big endian).
*
* Note that Ethercat uses little endian byte order, except for the Ethernet
* header which is big endian as usual.
*/
inline uint16 oshw_htons(uint16 host)
{
// __htons() is provided by the bare-metal x86 compiler
return __htons(host);
}
/**
* Network (i.e. big endian) to Host byte order.
*
* Note that Ethercat uses little endian byte order, except for the Ethernet
* header which is big endian as usual.
*/
inline uint16 oshw_ntohs(uint16 network)
{
// __ntohs() is provided by the bare-metal x86 compiler
return __ntohs(network);
}
/** Create list over available network adapters.
* @return First element in linked list of adapters
*/
ec_adaptert* oshw_find_adapters(void)
{
ec_adaptert *ret = NULL;
if (eth_discover_devices() >= 0) {
for (int i = 0;; ++i) {
struct eth_device *dev = eth_get_device(i);
if (dev == NULL) {
adapters[i-1].next = NULL;
break;
}
strncpy(adapters[i].name, dev->name, MAX_DEVICE_NAME);
adapters[i].next = &adapters[i+1];
}
ret = &(adapters[0]);
}
return ret;
}
/** Free memory allocated memory used by adapter collection.
* @param[in] adapter = First element in linked list of adapters
* EC_NOFRAME.
*/
void oshw_free_adapters(ec_adaptert *adapter)
{
}
extern int ec_slavecount;
+31
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
+744
View File
@@ -0,0 +1,744 @@
/*
* 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 sent by the master,
* and the sent 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 sent 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 <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 connection */
ECT_RED_DOUBLE
};
/** Primary source MAC address used for EtherCAT.
* This address is not the MAC address used from the NIC.
* EtherCAT does not care about MAC addressing, but it is used here to
* differentiate the route the packet traverses through the EtherCAT
* segment. This is needed to find out the packet flow in redundant
* configurations. */
const uint16 priMAC[3] = { 0x0101, 0x0101, 0x0101 };
/** Secondary source MAC address used for EtherCAT. */
const uint16 secMAC[3] = { 0x0404, 0x0404, 0x0404 };
/** second MAC word is used for identification */
#define RX_PRIM priMAC[1]
/** second MAC word is used for identification */
#define RX_SEC secMAC[1]
#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
*/
int ecx_setupnic(ecx_portt *port, const char *ifname, int secondary)
{
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));
if (status != E_OK)
{
ECAT_PRINT_ERROR("hpeOpen failed with status %04x ", status);
if(status == E_EXIST) ECAT_PRINT_ERROR("E_EXIST\n");
else if(status == E_STATE) ECAT_PRINT_ERROR("E_STATE\n");
else if(status == E_PARAM) ECAT_PRINT_ERROR("E_PARAM\n");
else if(status == E_INVALID_ADDR) ECAT_PRINT_ERROR("E_INVALID_ADDR\n");
else if(status == E_IO) ECAT_PRINT_ERROR("E_IO\n");
else if(status == E_TIME) ECAT_PRINT_ERROR("E_TIME\n");
else ECAT_PRINT_ERROR("UNKNOWN\n");
result = 0;
goto end;
}
conf.option_flags |= OPT_PROMISC;
status = hpeConfigOptions(port->handle, &conf, sizeof(conf));
if (status != E_OK)
{
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;
}
time(&now);
do
{
status = hpeGetMediaStatus(port->handle, &mstat);
if (status != E_OK)
{
ECAT_PRINT_ERROR("hpeGetMediaStatus failed with status %04x\n", status);
result = 0;
goto end;
}
if (mstat.media_speed == SPEED_NONE)
{
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)) {
ECAT_PRINT_ERROR("Media not connected as requested: speed=%u, duplex=%u\n",
mstat.media_speed, mstat.media_duplex);
result = 0;
goto end;
}
status= hpeGetMacAddress(port->handle, mac);
if (status != E_OK)
{
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);
if (status != E_OK)
{
ECAT_PRINT_ERROR("hpeAllocateReceiveBufferSet failed with status %04x\n", status);
result = 0;
goto end;
}
status = hpeAttachReceiveBufferSet(port->handle, port->rx_buffers);
if (status != E_OK)
{
ECAT_PRINT_ERROR("hpeAttachReceiveBufferSet failed with status %04x\n", status);
result = 0;
goto end;
}
if (secondary)
{
/* secondary port struct available? */
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);
}
/* 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
*/
int ecx_closenic(ecx_portt *port)
{
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.
* Destination MAC is always broadcast.
* Ethertype is always ETH_P_ECAT.
* @param[out] p = buffer
*/
void ec_setupheader(void *p)
{
ec_etherheadert *bp;
bp = (ec_etherheadert *)p;
bp->da0 = oshw_htons(0xffff);
bp->da1 = oshw_htons(0xffff);
bp->da2 = oshw_htons(0xffff);
bp->sa0 = oshw_htons(priMAC[0]);
bp->sa1 = oshw_htons(priMAC[1]);
bp->sa2 = oshw_htons(priMAC[2]);
bp->etype = oshw_htons(ETH_P_ECAT);
}
/** Get new frame identifier index and allocate corresponding rx buffer.
* @return new index.
*/
uint8 ecx_getindex(ecx_portt *port)
{
uint8 idx;
uint8 cnt;
WaitForRtControl(port->getindex_region);
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;
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, uint8 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, uint8 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]);
if (status != E_OK)
{
result = -2;
goto end;
}
log_RT_event('S',(WORD)3);
(*stack->rxbufstat)[idx] = EC_BUF_TX;
status = hpeStartTransmitter(port->handle);
if (status != E_OK)
{
(*stack->rxbufstat)[idx] = EC_BUF_EMPTY;
result = -3;
goto end;
}
log_RT_event('S',(WORD)4);
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, uint8 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)
{
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]);
port->redport->rxbufstat[idx] = EC_BUF_TX;
status = hpeStartTransmitter(port->redport->handle);
if (status != E_OK)
{
(*stack->rxbufstat)[idx] = EC_BUF_EMPTY;
}
}
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);
}
log_RT_event('R',(WORD)2);
status = hpeGetReceiveBuffer(port->handle, &rxbuffer);
if (status == E_OK)
{
memcpy(stack->tempbuf,rxbuffer->ptr, rxbuffer->used);
bytesrx = rxbuffer->used;
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 retrieves it
* from that buffer index without calling ec_recvpkt. If the requested index
* is not already in the buffer it calls ec_recvpkt to fetch it. There are
* three options now, 1 no frame read, so exit. 2 frame read but other
* than requested index, store in buffer and exit. 3 frame read with matching
* index, store in buffer, set completed flag in buffer status and exit.
*
* @param[in] 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, uint8 idx, int stacknumber)
{
uint16 l;
int rval;
uint8 idxf;
ec_etherheadert *ehp;
ec_comt *ecp;
ec_stackT *stack;
ec_bufT *rxbuf;
if (!stacknumber)
{
stack = &(port->stack);
}
else
{
stack = &(port->redport->stack);
}
rval = EC_NOFRAME;
rxbuf = &(*stack->rxbuf)[idx];
/* check if requested index is already in buffer ? */
if ((idx < EC_MAXBUF) && ( (*stack->rxbufstat)[idx] == EC_BUF_RCVD))
{
l = (*rxbuf)[0] + ((uint16)((*rxbuf)[1] & 0x0f) << 8);
/* return WKC */
rval = ((*rxbuf)[l] + ((uint16)(*rxbuf)[l + 1] << 8));
/* mark as completed */
(*stack->rxbufstat)[idx] = EC_BUF_COMPLETE;
}
else
{
WaitForRtControl(port->rx_region);
/* non blocking call to retrieve frame from socket */
if (ecx_recvpkt(port, stacknumber))
{
rval = EC_OTHERFRAME;
ehp = (ec_etherheadert*)(stack->tempbuf);
/* check if it is an EtherCAT frame */
if (ehp->etype == oshw_htons(ETH_P_ECAT))
{
ecp =(ec_comt*)(&(*stack->tempbuf)[ETH_HEADERSIZE]);
l = etohs(ecp->elength) & 0x0fff;
idxf = ecp->index;
/* found index equals requested index ? */
if (idxf == idx)
{
/* yes, put it in the buffer array (strip ethernet header) */
memcpy(rxbuf, &(*stack->tempbuf)[ETH_HEADERSIZE], (*stack->txbuflength)[idx] - ETH_HEADERSIZE);
/* return WKC */
rval = ((*rxbuf)[l] + ((uint16)((*rxbuf)[l + 1]) << 8));
/* mark as completed */
(*stack->rxbufstat)[idx] = EC_BUF_COMPLETE;
/* store MAC source word 1 for redundant routing info */
(*stack->rxsa)[idx] = oshw_ntohs(ehp->sa1);
}
else
{
/* check if index exist and someone is waiting for it */
if (idxf < EC_MAXBUF && (*stack->rxbufstat)[idxf] == EC_BUF_TX)
{
rxbuf = &(*stack->rxbuf)[idxf];
/* put it in the buffer array (strip ethernet header) */
memcpy(rxbuf, &(*stack->tempbuf)[ETH_HEADERSIZE], (*stack->txbuflength)[idxf] - ETH_HEADERSIZE);
/* mark as received */
(*stack->rxbufstat)[idxf] = EC_BUF_RCVD;
(*stack->rxsa)[idxf] = oshw_ntohs(ehp->sa1);
}
}
}
}
ReleaseRtControl();
}
/* 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] 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, uint8 idx, osal_timert *timer)
{
osal_timert timer2;
int wkc = EC_NOFRAME;
int wkc2 = EC_NOFRAME;
int primrx, secrx;
/* if not in redundant mode then always assume secondary is OK */
if (port->redstate == ECT_RED_NONE)
{
wkc2 = 0;
}
do
{
/* only read frame if not already in */
if (wkc <= EC_NOFRAME)
wkc = ecx_inframe(port, idx, 0);
/* only try secondary if in redundant mode */
if (port->redstate != ECT_RED_NONE)
{
/* only read frame if not already in */
if (wkc2 <= EC_NOFRAME)
{
wkc2 = ecx_inframe(port, idx, 1);
}
}
/* wait for both frames to arrive or timeout */
} while (((wkc <= EC_NOFRAME) || (wkc2 <= EC_NOFRAME)) && !osal_timer_is_expired(timer));
/* only do redundant functions when in redundant mode */
if (port->redstate != ECT_RED_NONE)
{
/* primrx if the received MAC source on primary socket */
primrx = 0;
if (wkc > EC_NOFRAME) primrx = port->rxsa[idx];
/* secrx if the received MAC source on psecondary socket */
secrx = 0;
if (wkc2 > EC_NOFRAME) secrx = port->redport->rxsa[idx];
/* primary socket got secondary frame and secondary socket got primary frame */
/* normal situation in redundant mode */
if ( ((primrx == RX_SEC) && (secrx == RX_PRIM)) )
{
/* copy secondary buffer to primary */
memcpy(&(port->rxbuf[idx]), &(port->redport->rxbuf[idx]), port->txbuflength[idx] - ETH_HEADERSIZE);
wkc = wkc2;
}
/* primary socket got nothing or primary frame, and secondary socket got secondary frame */
/* we need to resend TX packet */
if ( ((primrx == 0) && (secrx == RX_SEC)) ||
((primrx == RX_PRIM) && (secrx == RX_SEC)) )
{
/* If both primary and secondary have partial connection retransmit the primary received
* frame over the secondary socket. The result from the secondary received frame is a combined
* frame that traversed all slaves in standard order. */
if ( (primrx == RX_PRIM) && (secrx == RX_SEC) )
{
/* copy primary rx to tx buffer */
memcpy(&(port->txbuf[idx][ETH_HEADERSIZE]), &(port->rxbuf[idx]), port->txbuflength[idx] - ETH_HEADERSIZE);
}
osal_timer_start (&timer2, EC_TIMEOUTRET);
/* resend secondary tx */
ecx_outframe(port,idx,1);
do
{
/* retrieve frame */
wkc2 = ecx_inframe(port, idx, 1);
} while ((wkc2 <= EC_NOFRAME) && !osal_timer_is_expired(&timer2));
if (wkc2 > EC_NOFRAME)
{
/* copy secondary result to primary rx buffer */
memcpy(&(port->rxbuf[idx]), &(port->redport->rxbuf[idx]), port->txbuflength[idx] - ETH_HEADERSIZE);
wkc = wkc2;
}
}
}
/* return WKC or EC_NOFRAME */
return wkc;
}
/** Blocking receive frame function. Calls ec_waitinframe_red().
* @param[in] 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, uint8 idx, int timeout)
{
int wkc;
osal_timert timer;
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
{
osal_timer_start (&timer, timeout);
wkc = ecx_waitinframe_red(port, idx, &timer);
}
return wkc;
}
/** Blocking send and receive frame function. Used for non processdata frames.
* A datagram is build into a frame and transmitted via this function. It waits
* for an answer and returns the workcounter. The function retries if time is
* left and the result is WKC=0 or no frame received.
*
* The function calls ec_outframe_red() and ec_waitinframe_red().
*
* @param[in] idx = index of frame
* @param[in] timeout = timeout in us
* @return Workcounter or EC_NOFRAME
*/
int ecx_srconfirm(ecx_portt *port, uint8 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);
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);
}
uint8 ec_getindex(void)
{
return ecx_getindex(&ecx_port);
}
void ec_setbufstat(uint8 idx, int bufstat)
{
ecx_setbufstat(&ecx_port, idx, bufstat);
}
int ec_outframe(uint8 idx, int stacknumber)
{
return ecx_outframe(&ecx_port, idx, stacknumber);
}
int ec_outframe_red(uint8 idx)
{
return ecx_outframe_red(&ecx_port, idx);
}
int ec_inframe(uint8 idx, int stacknumber)
{
return ecx_inframe(&ecx_port, idx, stacknumber);
}
int ec_waitinframe(uint8 idx, int timeout)
{
return ecx_waitinframe(&ecx_port, idx, timeout);
}
int ec_srconfirm(uint8 idx, int timeout)
{
return ecx_srconfirm(&ecx_port, idx, timeout);
}
#endif
+116
View File
@@ -0,0 +1,116 @@
/*
* Licensed under the GNU General Public License version 2 with exceptions. See
* LICENSE file in the project root for full license information
*/
/** \file
* \brief
* Headerfile for nicdrv.c
*/
#ifndef _nicdrvh_
#define _nicdrvh_
#define HAVE_REMOTE
#include <rt.h>
#include "hpeif2.h"
/** pointer structure to Tx and Rx stacks */
typedef struct
{
/** socket connection used */
int *sock;
/** tx buffer */
ec_bufT (*txbuf)[EC_MAXBUF];
/** tx buffer lengths */
int (*txbuflength)[EC_MAXBUF];
/** temporary receive buffer */
ec_bufT *tempbuf;
/** rx buffers */
ec_bufT (*rxbuf)[EC_MAXBUF];
/** rx buffer status fields */
int (*rxbufstat)[EC_MAXBUF];
/** received MAC source address (middle word) */
int (*rxsa)[EC_MAXBUF];
} ec_stackT;
typedef struct
{
ec_stackT stack;
/** rx buffers */
ec_bufT rxbuf[EC_MAXBUF];
/** rx buffer status */
int rxbufstat[EC_MAXBUF];
/** rx MAC source address */
int rxsa[EC_MAXBUF];
/** temporary rx buffer */
ec_bufT tempinbuf;
/* Intime */
HPEHANDLE handle;
HPERXBUFFERSET *rx_buffers;
HPETXBUFFERSET *tx_buffers[EC_MAXBUF];
} ecx_redportt;
typedef struct
{
ec_stackT stack;
/** rx buffers */
ec_bufT rxbuf[EC_MAXBUF];
/** rx buffer status */
int rxbufstat[EC_MAXBUF];
/** rx MAC source address */
int rxsa[EC_MAXBUF];
/** temporary rx buffer */
ec_bufT tempinbuf;
/** temporary rx buffer status */
int tempinbufs;
/** transmit buffers */
ec_bufT txbuf[EC_MAXBUF];
/** transmit buffer lengths */
int txbuflength[EC_MAXBUF];
/** temporary tx buffer */
ec_bufT txbuf2;
/** temporary tx buffer length */
int txbuflength2;
/** last used frame index */
uint8 lastidx;
/** current redundancy state */
int redstate;
/** pointer to redundancy port and buffers */
ecx_redportt *redport;
RTHANDLE getindex_region;
RTHANDLE tx_region;
RTHANDLE rx_region;
/* Intime */
HPEHANDLE handle;
HPERXBUFFERSET *rx_buffers;
HPETXBUFFERSET *tx_buffers[EC_MAXBUF];
} ecx_portt;
extern const uint16 priMAC[3];
extern const uint16 secMAC[3];
//extern ecx_portt ecx_port;
//extern ecx_redportt ecx_redport;
int ec_setupnic(const char * ifname, int secondary);
int ec_closenic(void);
void ec_setupheader(void *p);
void ec_setbufstat(uint8 idx, int bufstat);
uint8 ec_getindex(void);
int ec_outframe(uint8 idx, int sock);
int ec_outframe_red(uint8 idx);
int ec_waitinframe(uint8 idx, int timeout);
int ec_srconfirm(uint8 idx,int timeout);
int ecx_setupnic(ecx_portt *port, const char * ifname, int secondary);
int ecx_closenic(ecx_portt *port);
void ecx_setbufstat(ecx_portt *port, uint8 idx, int bufstat);
uint8 ecx_getindex(ecx_portt *port);
int ecx_outframe(ecx_portt *port, uint8 idx, int sock);
int ecx_outframe_red(ecx_portt *port, uint8 idx);
int ecx_waitinframe(ecx_portt *port, uint8 idx, int timeout);
int ecx_srconfirm(ecx_portt *port, uint8 idx,int timeout);
#endif
+48
View File
@@ -0,0 +1,48 @@
/*
* Licensed under the GNU General Public License version 2 with exceptions. See
* LICENSE file in the project root for full license information
*/
#include <sys/endian.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)
{
return NULL;
}
/** 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
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 oshw.c
*/
#ifndef _oshw_
#define _oshw_
#include "ethercattype.h"
#include "ethercatmain.h"
#include "nicdrv.h"
#ifdef __cplusplus
extern "C" {
#endif
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
+648
View File
@@ -0,0 +1,648 @@
/*
* Licensed under the GNU General Public License version 2 with exceptions. See
* LICENSE file in the project root for full license information
*/
/** \file
* \brief
* EtherCAT RAW socket driver.
*
* Low level interface functions to send and receive EtherCAT packets.
* EtherCAT has the property that packets are only send by the master,
* and the send packets always return in the receive buffer.
* There can be multiple packets "on the wire" before they return.
* To combine the received packets with the original send packets a buffer
* system is installed. The identifier is put in the index item of the
* EtherCAT header. The index is stored and compared when a frame is received.
* If there is a match the packet can be combined with the transmit packet
* and returned to the higher level function.
*
* The socket layer can exhibit a reversal in the packet order (rare).
* If the Tx order is A-B-C the return order could be A-C-B. The indexed buffer
* will reorder the packets automatically.
*
* The "redundant" option will configure two sockets and two NIC interfaces.
* Slaves are connected to both interfaces, one on the IN port and one on the
* OUT port. Packets are send via both interfaces. Any one of the connections
* (also an interconnect) can be removed and the slaves are still serviced with
* packets. The software layer will detect the possible failure modes and
* compensate. If needed the packets from interface A are resent through interface B.
* This layer if fully transparent for the higher layers.
*/
#include <sys/types.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <sys/socket.h>
#include <unistd.h>
#include <sys/time.h>
#include <time.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <fcntl.h>
#include <string.h>
#include <netpacket/packet.h>
#include <pthread.h>
#include "oshw.h"
#include "osal.h"
/** Redundancy modes */
enum
{
/** No redundancy, single NIC mode */
ECT_RED_NONE,
/** Double redundant NIC connection */
ECT_RED_DOUBLE
};
/** Primary source MAC address used for EtherCAT.
* This address is not the MAC address used from the NIC.
* EtherCAT does not care about MAC addressing, but it is used here to
* differentiate the route the packet traverses through the EtherCAT
* segment. This is needed to find out the packet flow in redundant
* configurations. */
const uint16 priMAC[3] = { 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]
static void ecx_clear_rxbufstat(int *rxbufstat)
{
int i;
for(i = 0; i < EC_MAXBUF; i++)
{
rxbufstat[i] = EC_BUF_EMPTY;
}
}
/** Basic setup to connect NIC to socket.
* @param[in] port = port context struct
* @param[in] ifname = Name of NIC device, f.e. "eth0"
* @param[in] secondary = if >0 then use secondary stack instead of primary
* @return >0 if succeeded
*/
int ecx_setupnic(ecx_portt *port, const char *ifname, int secondary)
{
int i;
int r, rval, ifindex;
struct timeval timeout;
struct ifreq ifr;
struct sockaddr_ll sll;
int *psock;
pthread_mutexattr_t mutexattr;
rval = 0;
if (secondary)
{
/* secondary port struct available? */
if (port->redport)
{
/* when using secondary socket it is automatically a redundant setup */
psock = &(port->redport->sockhandle);
*psock = -1;
port->redstate = ECT_RED_DOUBLE;
port->redport->stack.sock = &(port->redport->sockhandle);
port->redport->stack.txbuf = &(port->txbuf);
port->redport->stack.txbuflength = &(port->txbuflength);
port->redport->stack.tempbuf = &(port->redport->tempinbuf);
port->redport->stack.rxbuf = &(port->redport->rxbuf);
port->redport->stack.rxbufstat = &(port->redport->rxbufstat);
port->redport->stack.rxsa = &(port->redport->rxsa);
ecx_clear_rxbufstat(&(port->redport->rxbufstat[0]));
}
else
{
/* fail */
return 0;
}
}
else
{
pthread_mutexattr_init(&mutexattr);
pthread_mutexattr_setprotocol(&mutexattr , PTHREAD_PRIO_INHERIT);
pthread_mutex_init(&(port->getindex_mutex), &mutexattr);
pthread_mutex_init(&(port->tx_mutex) , &mutexattr);
pthread_mutex_init(&(port->rx_mutex) , &mutexattr);
port->sockhandle = -1;
port->lastidx = 0;
port->redstate = ECT_RED_NONE;
port->stack.sock = &(port->sockhandle);
port->stack.txbuf = &(port->txbuf);
port->stack.txbuflength = &(port->txbuflength);
port->stack.tempbuf = &(port->tempinbuf);
port->stack.rxbuf = &(port->rxbuf);
port->stack.rxbufstat = &(port->rxbufstat);
port->stack.rxsa = &(port->rxsa);
ecx_clear_rxbufstat(&(port->rxbufstat[0]));
psock = &(port->sockhandle);
}
/* we use RAW packet socket, with packet type ETH_P_ECAT */
*psock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ECAT));
if(*psock < 0)
return 0;
timeout.tv_sec = 0;
timeout.tv_usec = 1;
r = 0;
r |= setsockopt(*psock, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
r |= setsockopt(*psock, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout));
i = 1;
r |= setsockopt(*psock, SOL_SOCKET, SO_DONTROUTE, &i, sizeof(i));
/* connect socket to NIC by name */
strcpy(ifr.ifr_name, ifname);
r |= ioctl(*psock, SIOCGIFINDEX, &ifr);
ifindex = ifr.ifr_ifindex;
strcpy(ifr.ifr_name, ifname);
ifr.ifr_flags = 0;
/* reset flags of NIC interface */
r |= ioctl(*psock, SIOCGIFFLAGS, &ifr);
/* set flags of NIC interface, here promiscuous and broadcast */
ifr.ifr_flags = ifr.ifr_flags | IFF_PROMISC | IFF_BROADCAST;
r |= ioctl(*psock, SIOCSIFFLAGS, &ifr);
/* bind socket to protocol, in this case RAW EtherCAT */
sll.sll_family = AF_PACKET;
sll.sll_ifindex = ifindex;
sll.sll_protocol = htons(ETH_P_ECAT);
r |= bind(*psock, (struct sockaddr *)&sll, sizeof(sll));
/* 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));
if (r == 0) rval = 1;
return rval;
}
/** Close sockets used
* @param[in] port = port context struct
* @return 0
*/
int ecx_closenic(ecx_portt *port)
{
if (port->sockhandle >= 0)
close(port->sockhandle);
if ((port->redport) && (port->redport->sockhandle >= 0))
close(port->redport->sockhandle);
return 0;
}
/** Fill buffer with ethernet header structure.
* Destination MAC is always broadcast.
* Ethertype is always ETH_P_ECAT.
* @param[out] p = buffer
*/
void ec_setupheader(void *p)
{
ec_etherheadert *bp;
bp = 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.
*/
uint8 ecx_getindex(ecx_portt *port)
{
uint8 idx;
uint8 cnt;
pthread_mutex_lock( &(port->getindex_mutex) );
idx = port->lastidx + 1;
/* index can't be larger than buffer array */
if (idx >= EC_MAXBUF)
{
idx = 0;
}
cnt = 0;
/* try to find unused index */
while ((port->rxbufstat[idx] != EC_BUF_EMPTY) && (cnt < EC_MAXBUF))
{
idx++;
cnt++;
if (idx >= EC_MAXBUF)
{
idx = 0;
}
}
port->rxbufstat[idx] = EC_BUF_ALLOC;
if (port->redstate != ECT_RED_NONE)
port->redport->rxbufstat[idx] = EC_BUF_ALLOC;
port->lastidx = idx;
pthread_mutex_unlock( &(port->getindex_mutex) );
return idx;
}
/** Set rx buffer status.
* @param[in] port = port context struct
* @param[in] idx = index in buffer array
* @param[in] bufstat = status to set
*/
void ecx_setbufstat(ecx_portt *port, uint8 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, uint8 idx, int stacknumber)
{
int lp, rval;
ec_stackT *stack;
if (!stacknumber)
{
stack = &(port->stack);
}
else
{
stack = &(port->redport->stack);
}
lp = (*stack->txbuflength)[idx];
(*stack->rxbufstat)[idx] = EC_BUF_TX;
rval = send(*stack->sock, (*stack->txbuf)[idx], lp, 0);
if (rval == -1)
{
(*stack->rxbufstat)[idx] = EC_BUF_EMPTY;
}
return rval;
}
/** Transmit buffer over socket (non blocking).
* @param[in] port = port context struct
* @param[in] idx = index in tx buffer array
* @return socket send result
*/
int ecx_outframe_red(ecx_portt *port, uint8 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)
{
pthread_mutex_lock( &(port->tx_mutex) );
ehp = (ec_etherheadert *)&(port->txbuf2);
/* use dummy frame for secondary socket transmit (BRD) */
datagramP = (ec_comt*)&(port->txbuf2[ETH_HEADERSIZE]);
/* write index to frame */
datagramP->index = idx;
/* rewrite MAC source address 1 to secondary */
ehp->sa1 = htons(secMAC[1]);
/* transmit over secondary socket */
port->redport->rxbufstat[idx] = EC_BUF_TX;
if (send(port->redport->sockhandle, &(port->txbuf2), port->txbuflength2 , 0) == -1)
{
port->redport->rxbufstat[idx] = EC_BUF_EMPTY;
}
pthread_mutex_unlock( &(port->tx_mutex) );
}
return rval;
}
/** Non blocking read of socket. Put frame in temporary buffer.
* @param[in] port = port context struct
* @param[in] stacknumber = 0=primary 1=secondary stack
* @return >0 if frame is available and read
*/
static int ecx_recvpkt(ecx_portt *port, int stacknumber)
{
int lp, bytesrx;
ec_stackT *stack;
if (!stacknumber)
{
stack = &(port->stack);
}
else
{
stack = &(port->redport->stack);
}
lp = sizeof(port->tempinbuf);
bytesrx = recv(*stack->sock, (*stack->tempbuf), lp, 0);
port->tempinbufs = bytesrx;
return (bytesrx > 0);
}
/** Non blocking receive frame function. Uses RX buffer and index to combine
* read frame with transmitted frame. To compensate for received frames that
* are out-of-order all frames are stored in their respective indexed buffer.
* If a frame was placed in the buffer previously, the function retrieves it
* from that buffer index without calling ec_recvpkt. If the requested index
* is not already in the buffer it calls ec_recvpkt to fetch it. There are
* three options now, 1 no frame read, so exit. 2 frame read but other
* than requested index, store in buffer and exit. 3 frame read with matching
* index, store in buffer, set completed flag in buffer status and exit.
*
* @param[in] port = port context struct
* @param[in] idx = requested index of frame
* @param[in] stacknumber = 0=primary 1=secondary stack
* @return Workcounter if a frame is found with corresponding index, otherwise
* EC_NOFRAME or EC_OTHERFRAME.
*/
int ecx_inframe(ecx_portt *port, uint8 idx, int stacknumber)
{
uint16 l;
int rval;
uint8 idxf;
ec_etherheadert *ehp;
ec_comt *ecp;
ec_stackT *stack;
ec_bufT *rxbuf;
if (!stacknumber)
{
stack = &(port->stack);
}
else
{
stack = &(port->redport->stack);
}
rval = EC_NOFRAME;
rxbuf = &(*stack->rxbuf)[idx];
/* check if requested index is already in buffer ? */
if ((idx < EC_MAXBUF) && ((*stack->rxbufstat)[idx] == EC_BUF_RCVD))
{
l = (*rxbuf)[0] + ((uint16)((*rxbuf)[1] & 0x0f) << 8);
/* return WKC */
rval = ((*rxbuf)[l] + ((uint16)(*rxbuf)[l + 1] << 8));
/* mark as completed */
(*stack->rxbufstat)[idx] = EC_BUF_COMPLETE;
}
else
{
pthread_mutex_lock(&(port->rx_mutex));
/* non blocking call to retrieve frame from socket */
if (ecx_recvpkt(port, stacknumber))
{
rval = EC_OTHERFRAME;
ehp =(ec_etherheadert*)(stack->tempbuf);
/* check if it is an EtherCAT frame */
if (ehp->etype == htons(ETH_P_ECAT))
{
ecp =(ec_comt*)(&(*stack->tempbuf)[ETH_HEADERSIZE]);
l = etohs(ecp->elength) & 0x0fff;
idxf = ecp->index;
/* found index equals requested index ? */
if (idxf == idx)
{
/* yes, put it in the buffer array (strip ethernet header) */
memcpy(rxbuf, &(*stack->tempbuf)[ETH_HEADERSIZE], (*stack->txbuflength)[idx] - ETH_HEADERSIZE);
/* return WKC */
rval = ((*rxbuf)[l] + ((uint16)((*rxbuf)[l + 1]) << 8));
/* mark as completed */
(*stack->rxbufstat)[idx] = EC_BUF_COMPLETE;
/* store MAC source word 1 for redundant routing info */
(*stack->rxsa)[idx] = 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] = ntohs(ehp->sa1);
}
else
{
/* strange things happened */
}
}
}
}
pthread_mutex_unlock( &(port->rx_mutex) );
}
/* WKC if matching frame found */
return rval;
}
/** Blocking redundant receive frame function. If redundant mode is not active then
* it skips the secondary stack and redundancy functions. In redundant mode it waits
* for both (primary and secondary) frames to come in. The result goes in an decision
* tree that decides, depending on the route of the packet and its possible missing arrival,
* how to reroute the original packet to get the data in an other try.
*
* @param[in] port = port context struct
* @param[in] idx = requested index of frame
* @param[in] timer = absolute timeout time
* @return Workcounter if a frame is found with corresponding index, otherwise
* EC_NOFRAME.
*/
static int ecx_waitinframe_red(ecx_portt *port, uint8 idx, osal_timert *timer)
{
osal_timert timer2;
int wkc = EC_NOFRAME;
int wkc2 = EC_NOFRAME;
int primrx, secrx;
/* if not in redundant mode then always assume secondary is OK */
if (port->redstate == ECT_RED_NONE)
wkc2 = 0;
do
{
/* only read frame if not already in */
if (wkc <= EC_NOFRAME)
wkc = ecx_inframe(port, idx, 0);
/* only try secondary if in redundant mode */
if (port->redstate != ECT_RED_NONE)
{
/* only read frame if not already in */
if (wkc2 <= EC_NOFRAME)
wkc2 = ecx_inframe(port, idx, 1);
}
/* wait for both frames to arrive or timeout */
} while (((wkc <= EC_NOFRAME) || (wkc2 <= EC_NOFRAME)) && !osal_timer_is_expired(timer));
/* only do redundant functions when in redundant mode */
if (port->redstate != ECT_RED_NONE)
{
/* primrx if the received MAC source on primary socket */
primrx = 0;
if (wkc > EC_NOFRAME) primrx = port->rxsa[idx];
/* secrx if the received MAC source on psecondary socket */
secrx = 0;
if (wkc2 > EC_NOFRAME) secrx = port->redport->rxsa[idx];
/* primary socket got secondary frame and secondary socket got primary frame */
/* normal situation in redundant mode */
if ( ((primrx == RX_SEC) && (secrx == RX_PRIM)) )
{
/* copy secondary buffer to primary */
memcpy(&(port->rxbuf[idx]), &(port->redport->rxbuf[idx]), port->txbuflength[idx] - ETH_HEADERSIZE);
wkc = wkc2;
}
/* primary socket got nothing or primary frame, and secondary socket got secondary frame */
/* we need to resend TX packet */
if ( ((primrx == 0) && (secrx == RX_SEC)) ||
((primrx == RX_PRIM) && (secrx == RX_SEC)) )
{
/* If both primary and secondary have partial connection retransmit the primary received
* frame over the secondary socket. The result from the secondary received frame is a combined
* frame that traversed all slaves in standard order. */
if ( (primrx == RX_PRIM) && (secrx == RX_SEC) )
{
/* copy primary rx to tx buffer */
memcpy(&(port->txbuf[idx][ETH_HEADERSIZE]), &(port->rxbuf[idx]), port->txbuflength[idx] - ETH_HEADERSIZE);
}
osal_timer_start (&timer2, EC_TIMEOUTRET);
/* resend secondary tx */
ecx_outframe(port, idx, 1);
do
{
/* retrieve frame */
wkc2 = ecx_inframe(port, idx, 1);
} while ((wkc2 <= EC_NOFRAME) && !osal_timer_is_expired(&timer2));
if (wkc2 > EC_NOFRAME)
{
/* copy secondary result to primary rx buffer */
memcpy(&(port->rxbuf[idx]), &(port->redport->rxbuf[idx]), port->txbuflength[idx] - ETH_HEADERSIZE);
wkc = wkc2;
}
}
}
/* return WKC or EC_NOFRAME */
return wkc;
}
/** Blocking receive frame function. Calls ec_waitinframe_red().
* @param[in] port = port context struct
* @param[in] idx = requested index of frame
* @param[in] timeout = timeout in us
* @return Workcounter if a frame is found with corresponding index, otherwise
* EC_NOFRAME.
*/
int ecx_waitinframe(ecx_portt *port, uint8 idx, int timeout)
{
int wkc;
osal_timert timer;
osal_timer_start (&timer, timeout);
wkc = ecx_waitinframe_red(port, idx, &timer);
return wkc;
}
/** Blocking send and receive frame function. Used for non processdata frames.
* A datagram is build into a frame and transmitted via this function. It waits
* for an answer and returns the workcounter. The function retries if time is
* left and the result is WKC=0 or no frame received.
*
* The function calls ec_outframe_red() and ec_waitinframe_red().
*
* @param[in] port = port context struct
* @param[in] idx = index of frame
* @param[in] timeout = timeout in us
* @return Workcounter or EC_NOFRAME
*/
int ecx_srconfirm(ecx_portt *port, uint8 idx, int timeout)
{
int wkc = EC_NOFRAME;
osal_timert timer1, timer2;
osal_timer_start (&timer1, timeout);
do
{
/* tx frame on primary and if in redundant mode a dummy on secondary */
ecx_outframe_red(port, idx);
if (timeout < EC_TIMEOUTRET)
{
osal_timer_start (&timer2, timeout);
}
else
{
/* normally use partial timeout for rx */
osal_timer_start (&timer2, EC_TIMEOUTRET);
}
/* get frame from primary or if in redundant mode possibly from secondary */
wkc = ecx_waitinframe_red(port, idx, &timer2);
/* wait for answer with WKC>=0 or otherwise retry until timeout */
} while ((wkc <= EC_NOFRAME) && !osal_timer_is_expired (&timer1));
return wkc;
}
#ifdef EC_VER1
int ec_setupnic(const char *ifname, int secondary)
{
return ecx_setupnic(&ecx_port, ifname, secondary);
}
int ec_closenic(void)
{
return ecx_closenic(&ecx_port);
}
uint8 ec_getindex(void)
{
return ecx_getindex(&ecx_port);
}
void ec_setbufstat(uint8 idx, int bufstat)
{
ecx_setbufstat(&ecx_port, idx, bufstat);
}
int ec_outframe(uint8 idx, int stacknumber)
{
return ecx_outframe(&ecx_port, idx, stacknumber);
}
int ec_outframe_red(uint8 idx)
{
return ecx_outframe_red(&ecx_port, idx);
}
int ec_inframe(uint8 idx, int stacknumber)
{
return ecx_inframe(&ecx_port, idx, stacknumber);
}
int ec_waitinframe(uint8 idx, int timeout)
{
return ecx_waitinframe(&ecx_port, idx, timeout);
}
int ec_srconfirm(uint8 idx, int timeout)
{
return ecx_srconfirm(&ecx_port, idx, timeout);
}
#endif
+120
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.c
*/
#ifndef _nicdrvh_
#define _nicdrvh_
#ifdef __cplusplus
extern "C"
{
#endif
#include <pthread.h>
/** pointer structure to Tx and Rx stacks */
typedef struct
{
/** socket connection used */
int *sock;
/** tx buffer */
ec_bufT (*txbuf)[EC_MAXBUF];
/** tx buffer lengths */
int (*txbuflength)[EC_MAXBUF];
/** temporary receive buffer */
ec_bufT *tempbuf;
/** rx buffers */
ec_bufT (*rxbuf)[EC_MAXBUF];
/** rx buffer status fields */
int (*rxbufstat)[EC_MAXBUF];
/** received MAC source address (middle word) */
int (*rxsa)[EC_MAXBUF];
} ec_stackT;
/** pointer structure to buffers for redundant port */
typedef struct
{
ec_stackT stack;
int sockhandle;
/** rx buffers */
ec_bufT rxbuf[EC_MAXBUF];
/** rx buffer status */
int rxbufstat[EC_MAXBUF];
/** rx MAC source address */
int rxsa[EC_MAXBUF];
/** temporary rx buffer */
ec_bufT tempinbuf;
} ecx_redportt;
/** pointer structure to buffers, vars and mutexes for port instantiation */
typedef struct
{
ec_stackT stack;
int sockhandle;
/** rx buffers */
ec_bufT rxbuf[EC_MAXBUF];
/** rx buffer status */
int rxbufstat[EC_MAXBUF];
/** rx MAC source address */
int rxsa[EC_MAXBUF];
/** temporary rx buffer */
ec_bufT tempinbuf;
/** temporary rx buffer status */
int tempinbufs;
/** transmit buffers */
ec_bufT txbuf[EC_MAXBUF];
/** transmit buffer lengths */
int txbuflength[EC_MAXBUF];
/** temporary tx buffer */
ec_bufT txbuf2;
/** temporary tx buffer length */
int txbuflength2;
/** last used frame index */
uint8 lastidx;
/** current redundancy state */
int redstate;
/** pointer to redundancy port and buffers */
ecx_redportt *redport;
pthread_mutex_t getindex_mutex;
pthread_mutex_t tx_mutex;
pthread_mutex_t rx_mutex;
} ecx_portt;
extern const uint16 priMAC[3];
extern const uint16 secMAC[3];
#ifdef EC_VER1
extern ecx_portt ecx_port;
extern ecx_redportt ecx_redport;
int ec_setupnic(const char * ifname, int secondary);
int ec_closenic(void);
void ec_setbufstat(uint8 idx, int bufstat);
uint8 ec_getindex(void);
int ec_outframe(uint8 idx, int sock);
int ec_outframe_red(uint8 idx);
int ec_waitinframe(uint8 idx, int timeout);
int ec_srconfirm(uint8 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, uint8 idx, int bufstat);
uint8 ecx_getindex(ecx_portt *port);
int ecx_outframe(ecx_portt *port, uint8 idx, int sock);
int ecx_outframe_red(ecx_portt *port, uint8 idx);
int ecx_waitinframe(ecx_portt *port, uint8 idx, int timeout);
int ecx_srconfirm(ecx_portt *port, uint8 idx,int timeout);
#ifdef __cplusplus
}
#endif
#endif
+117
View File
@@ -0,0 +1,117 @@
/*
* Licensed under the GNU General Public License version 2 with exceptions. See
* LICENSE file in the project root for full license information
*/
#include <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 "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)
{
int i;
struct if_nameindex *ids;
ec_adaptert * adapter;
ec_adaptert * prev_adapter = NULL;
ec_adaptert * ret_adapter = NULL;
/* Iterate all devices and create a local copy holding the name and
* description.
*/
ids = if_nameindex ();
for(i = 0; ids[i].if_index != 0; i++)
{
adapter = (ec_adaptert *)malloc(sizeof(ec_adaptert));
/* If we got more than one adapter save link list pointer to previous
* adapter.
* Else save as pointer to return.
*/
if (prev_adapter)
{
prev_adapter->next = adapter;
}
else
{
ret_adapter = adapter;
}
/* fetch description and name, in Linux we use the same on both */
adapter->next = NULL;
if (ids[i].if_name)
{
strncpy(adapter->name, ids[i].if_name, EC_MAXLEN_ADAPTERNAME);
adapter->name[EC_MAXLEN_ADAPTERNAME-1] = '\0';
strncpy(adapter->desc, ids[i].if_name, EC_MAXLEN_ADAPTERNAME);
adapter->desc[EC_MAXLEN_ADAPTERNAME-1] = '\0';
}
else
{
adapter->name[0] = '\0';
adapter->desc[0] = '\0';
}
prev_adapter = adapter;
}
if_freenameindex (ids);
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)
{
ec_adaptert * next_adapter;
/* Iterate the linked list and free all elements holding
* adapter information
*/
if(adapter)
{
next_adapter = adapter->next;
free (adapter);
while (next_adapter)
{
adapter = next_adapter;
next_adapter = adapter->next;
free (adapter);
}
}
}
+31
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
+649
View File
@@ -0,0 +1,649 @@
/*
* Licensed under the GNU General Public License version 2 with exceptions. See
* LICENSE file in the project root for full license information
*/
/** \file
* \brief
* EtherCAT RAW socket driver.
*
* Low level interface functions to send and receive EtherCAT packets.
* EtherCAT has the property that packets are only send by the master,
* and the send packets always return in the receive buffer.
* There can be multiple packets "on the wire" before they return.
* To combine the received packets with the original send packets a buffer
* system is installed. The identifier is put in the index item of the
* EtherCAT header. The index is stored and compared when a frame is received.
* If there is a match the packet can be combined with the transmit packet
* and returned to the higher level function.
*
* The socket layer can exhibit a reversal in the packet order (rare).
* If the Tx order is A-B-C the return order could be A-C-B. The indexed buffer
* will reorder the packets automatically.
*
* The "redundant" option will configure two sockets and two NIC interfaces.
* Slaves are connected to both interfaces, one on the IN port and one on the
* OUT port. Packets are send via both interfaces. Any one of the connections
* (also an interconnect) can be removed and the slaves are still serviced with
* packets. The software layer will detect the possible failure modes and
* compensate. If needed the packets from interface A are resent through interface B.
* This layer is fully transparent for the higher layers.
*/
#include <assert.h>
#include <sys/types.h>
#include <stdio.h>
#include <fcntl.h>
#include <string.h>
#include "ethercattype.h"
#include "nicdrv.h"
#include "osal.h"
/** Redundancy modes */
enum
{
/** No redundancy, single NIC mode */
ECT_RED_NONE,
/** Double redundant NIC connection */
ECT_RED_DOUBLE
};
/** Primary source MAC address used for EtherCAT.
* This address is not the MAC address used from the NIC.
* EtherCAT does not care about MAC addressing, but it is used here to
* differentiate the route the packet traverses through the EtherCAT
* segment. This is needed to fund 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]
static char errbuf[PCAP_ERRBUF_SIZE];
static void ecx_clear_rxbufstat(int *rxbufstat)
{
int i;
for(i = 0; i < EC_MAXBUF; i++)
{
rxbufstat[i] = EC_BUF_EMPTY;
}
}
/** Basic setup to connect NIC to socket.
* @param[in] port = port context struct
* @param[in] ifname = Name of NIC device, f.e. "eth0"
* @param[in] secondary = if >0 then use secondary stack instead of primary
* @return >0 if succeeded
*/
int ecx_setupnic(ecx_portt *port, const char *ifname, int secondary)
{
int i, rval;
pcap_t **psock;
rval = 0;
if (secondary)
{
/* secondary port struct available? */
if (port->redport)
{
/* when using secondary socket it is automatically a redundant setup */
psock = &(port->redport->sockhandle);
*psock = NULL;
port->redstate = ECT_RED_DOUBLE;
port->redport->stack.sock = &(port->redport->sockhandle);
port->redport->stack.txbuf = &(port->txbuf);
port->redport->stack.txbuflength = &(port->txbuflength);
port->redport->stack.tempbuf = &(port->redport->tempinbuf);
port->redport->stack.rxbuf = &(port->redport->rxbuf);
port->redport->stack.rxbufstat = &(port->redport->rxbufstat);
port->redport->stack.rxsa = &(port->redport->rxsa);
ecx_clear_rxbufstat(&(port->redport->rxbufstat[0]));
}
else
{
/* fail */
return 0;
}
}
else
{
pthread_mutex_init(&(port->getindex_mutex), NULL);
pthread_mutex_init(&(port->tx_mutex), NULL);
pthread_mutex_init(&(port->rx_mutex), NULL);
port->sockhandle = NULL;
port->lastidx = 0;
port->redstate = ECT_RED_NONE;
port->stack.sock = &(port->sockhandle);
port->stack.txbuf = &(port->txbuf);
port->stack.txbuflength = &(port->txbuflength);
port->stack.tempbuf = &(port->tempinbuf);
port->stack.rxbuf = &(port->rxbuf);
port->stack.rxbufstat = &(port->rxbufstat);
port->stack.rxsa = &(port->rxsa);
ecx_clear_rxbufstat(&(port->rxbufstat[0]));
psock = &(port->sockhandle);
}
*psock = pcap_open_live(ifname, 65536, 1, 0, errbuf);
if (NULL == *psock)
{
return 0;
}
rval = pcap_setdirection(*psock, PCAP_D_IN);
if (rval != 0)
{
return 0;
}
rval = pcap_setnonblock(*psock, 1, errbuf);
if (rval != 0)
{
return 0;
}
for (i = 0; i < EC_MAXBUF; i++)
{
ec_setupheader(&(port->txbuf[i]));
port->rxbufstat[i] = EC_BUF_EMPTY;
}
ec_setupheader(&(port->txbuf2));
return 1;
}
/** Close sockets used
* @param[in] port = port context struct
* @return 0
*/
int ecx_closenic(ecx_portt *port)
{
if (port->sockhandle != NULL)
{
pthread_mutex_destroy(&(port->getindex_mutex));
pthread_mutex_destroy(&(port->tx_mutex));
pthread_mutex_destroy(&(port->rx_mutex));
pcap_close(port->sockhandle);
port->sockhandle = NULL;
}
if ((port->redport) && (port->redport->sockhandle != NULL))
{
pcap_close(port->redport->sockhandle);
port->redport->sockhandle = NULL;
}
return 0;
}
/** Fill buffer with ethernet header structure.
* Destination MAC is always broadcast.
* Ethertype is always ETH_P_ECAT.
* @param[out] p = buffer
*/
void ec_setupheader(void *p)
{
ec_etherheadert *bp;
bp = p;
bp->da0 = 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.
*/
uint8 ecx_getindex(ecx_portt *port)
{
uint8 idx;
uint8 cnt;
pthread_mutex_lock(&(port->getindex_mutex));
idx = port->lastidx + 1;
/* index can't be larger than buffer array */
if (idx >= EC_MAXBUF)
{
idx = 0;
}
cnt = 0;
/* try to find unused index */
while ((port->rxbufstat[idx] != EC_BUF_EMPTY) && (cnt < EC_MAXBUF))
{
idx++;
cnt++;
if (idx >= EC_MAXBUF)
{
idx = 0;
}
}
port->rxbufstat[idx] = EC_BUF_ALLOC;
if (port->redstate != ECT_RED_NONE)
port->redport->rxbufstat[idx] = EC_BUF_ALLOC;
port->lastidx = idx;
pthread_mutex_unlock(&(port->getindex_mutex));
return idx;
}
/** Set rx buffer status.
* @param[in] port = port context struct
* @param[in] idx = index in buffer array
* @param[in] bufstat = status to set
*/
void ecx_setbufstat(ecx_portt *port, uint8 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, uint8 idx, int stacknumber)
{
int lp, rval;
ec_stackT *stack;
if (!stacknumber)
{
stack = &(port->stack);
}
else
{
stack = &(port->redport->stack);
}
lp = (*stack->txbuflength)[idx];
(*stack->rxbufstat)[idx] = EC_BUF_TX;
rval = pcap_sendpacket(*stack->sock, (*stack->txbuf)[idx], lp);
if (rval == PCAP_ERROR)
{
(*stack->rxbufstat)[idx] = EC_BUF_EMPTY;
}
return rval;
}
/** Transmit buffer over socket (non blocking).
* @param[in] port = port context struct
* @param[in] idx = index in tx buffer array
* @return socket send result
*/
int ecx_outframe_red(ecx_portt *port, uint8 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)
{
pthread_mutex_lock( &(port->tx_mutex) );
ehp = (ec_etherheadert *)&(port->txbuf2);
/* use dummy frame for secondary socket transmit (BRD) */
datagramP = (ec_comt*)&(port->txbuf2[ETH_HEADERSIZE]);
/* write index to frame */
datagramP->index = idx;
/* rewrite MAC source address 1 to secondary */
ehp->sa1 = htons(secMAC[1]);
/* transmit over secondary socket */
port->redport->rxbufstat[idx] = EC_BUF_TX;
if (pcap_sendpacket(port->redport->sockhandle, (u_char const *)&(port->txbuf2), port->txbuflength2) == PCAP_ERROR)
{
port->redport->rxbufstat[idx] = EC_BUF_EMPTY;
}
pthread_mutex_unlock( &(port->tx_mutex) );
}
return rval;
}
/** Non blocking read of socket. Put frame in temporary buffer.
* @param[in] port = port context struct
* @param[in] stacknumber = 0=primary 1=secondary stack
* @return >0 if frame is available and read
*/
static int ecx_recvpkt(ecx_portt *port, int stacknumber)
{
int lp, bytesrx;
ec_stackT *stack;
struct pcap_pkthdr * header;
unsigned char const * pkt_data;
int res;
if (!stacknumber)
{
stack = &(port->stack);
}
else
{
stack = &(port->redport->stack);
}
lp = sizeof(port->tempinbuf);
res = pcap_next_ex(*stack->sock, &header, &pkt_data);
if (res <=0 )
{
port->tempinbufs = 0;
return 0;
}
bytesrx = header->len;
if (bytesrx > lp)
{
bytesrx = lp;
}
memcpy(*stack->tempbuf, pkt_data, bytesrx);
port->tempinbufs = bytesrx;
return (bytesrx > 0);
}
/** Non blocking receive frame function. Uses RX buffer and index to combine
* read frame with transmitted frame. To compensate for received frames that
* are out-of-order all frames are stored in their respective indexed buffer.
* If a frame was placed in the buffer previously, the function retrieves it
* from that buffer index without calling ec_recvpkt. If the requested index
* is not already in the buffer it calls ec_recvpkt to fetch it. There are
* three options now, 1 no frame read, so exit. 2 frame read but other
* than requested index, store in buffer and exit. 3 frame read with matching
* index, store in buffer, set completed flag in buffer status and exit.
*
* @param[in] port = port context struct
* @param[in] idx = requested index of frame
* @param[in] stacknumber = 0=primary 1=secondary stack
* @return Workcounter if a frame is found with corresponding index, otherwise
* EC_NOFRAME or EC_OTHERFRAME.
*/
int ecx_inframe(ecx_portt *port, uint8 idx, int stacknumber)
{
uint16 l;
int rval;
uint8 idxf;
ec_etherheadert *ehp;
ec_comt *ecp;
ec_stackT *stack;
ec_bufT *rxbuf;
if (!stacknumber)
{
stack = &(port->stack);
}
else
{
stack = &(port->redport->stack);
}
rval = EC_NOFRAME;
rxbuf = &(*stack->rxbuf)[idx];
/* check if requested index is already in buffer ? */
if ((idx < EC_MAXBUF) && ((*stack->rxbufstat)[idx] == EC_BUF_RCVD))
{
l = (*rxbuf)[0] + ((uint16)((*rxbuf)[1] & 0x0f) << 8);
/* return WKC */
rval = ((*rxbuf)[l] + ((uint16)(*rxbuf)[l + 1] << 8));
/* mark as completed */
(*stack->rxbufstat)[idx] = EC_BUF_COMPLETE;
}
else
{
pthread_mutex_lock(&(port->rx_mutex));
/* non blocking call to retrieve frame from socket */
if (ecx_recvpkt(port, stacknumber))
{
rval = EC_OTHERFRAME;
ehp =(ec_etherheadert*)(stack->tempbuf);
/* check if it is an EtherCAT frame */
if (ehp->etype == htons(ETH_P_ECAT))
{
ecp =(ec_comt*)(&(*stack->tempbuf)[ETH_HEADERSIZE]);
l = etohs(ecp->elength) & 0x0fff;
idxf = ecp->index;
/* found index equals requested index ? */
if (idxf == idx)
{
/* yes, put it in the buffer array (strip ethernet header) */
memcpy(rxbuf, &(*stack->tempbuf)[ETH_HEADERSIZE], (*stack->txbuflength)[idx] - ETH_HEADERSIZE);
/* return WKC */
rval = ((*rxbuf)[l] + ((uint16)((*rxbuf)[l + 1]) << 8));
/* mark as completed */
(*stack->rxbufstat)[idx] = EC_BUF_COMPLETE;
/* store MAC source word 1 for redundant routing info */
(*stack->rxsa)[idx] = 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] = ntohs(ehp->sa1);
}
else
{
/* strange things happened */
}
}
}
}
pthread_mutex_unlock( &(port->rx_mutex) );
}
/* WKC if matching frame found */
return rval;
}
/** Blocking redundant receive frame function. If redundant mode is not active then
* it skips the secondary stack and redundancy functions. In redundant mode it waits
* for both (primary and secondary) frames to come in. The result goes in an decision
* tree that decides, depending on the route of the packet and its possible missing arrival,
* how to reroute the original packet to get the data in an other try.
*
* @param[in] port = port context struct
* @param[in] idx = requested index of frame
* @param[in] tvs = timeout
* @return Workcounter if a frame is found with corresponding index, otherwise
* EC_NOFRAME.
*/
static int ecx_waitinframe_red(ecx_portt *port, uint8 idx, osal_timert *timer)
{
osal_timert timer2;
int wkc = EC_NOFRAME;
int wkc2 = EC_NOFRAME;
int primrx, secrx;
/* if not in redundant mode then always assume secondary is OK */
if (port->redstate == ECT_RED_NONE)
wkc2 = 0;
do
{
/* only read frame if not already in */
if (wkc <= EC_NOFRAME)
wkc = ecx_inframe(port, idx, 0);
/* only try secondary if in redundant mode */
if (port->redstate != ECT_RED_NONE)
{
/* only read frame if not already in */
if (wkc2 <= EC_NOFRAME)
wkc2 = ecx_inframe(port, idx, 1);
}
/* wait for both frames to arrive or timeout */
} while (((wkc <= EC_NOFRAME) || (wkc2 <= EC_NOFRAME)) && !osal_timer_is_expired(timer));
/* only do redundant functions when in redundant mode */
if (port->redstate != ECT_RED_NONE)
{
/* primrx if the received MAC source on primary socket */
primrx = 0;
if (wkc > EC_NOFRAME) primrx = port->rxsa[idx];
/* secrx if the received MAC source on psecondary socket */
secrx = 0;
if (wkc2 > EC_NOFRAME) secrx = port->redport->rxsa[idx];
/* primary socket got secondary frame and secondary socket got primary frame */
/* normal situation in redundant mode */
if ( ((primrx == RX_SEC) && (secrx == RX_PRIM)) )
{
/* copy secondary buffer to primary */
memcpy(&(port->rxbuf[idx]), &(port->redport->rxbuf[idx]), port->txbuflength[idx] - ETH_HEADERSIZE);
wkc = wkc2;
}
/* primary socket got nothing or primary frame, and secondary socket got secondary frame */
/* we need to resend TX packet */
if ( ((primrx == 0) && (secrx == RX_SEC)) ||
((primrx == RX_PRIM) && (secrx == RX_SEC)) )
{
/* If both primary and secondary have partial connection retransmit the primary received
* frame over the secondary socket. The result from the secondary received frame is a combined
* frame that traversed all slaves in standard order. */
if ( (primrx == RX_PRIM) && (secrx == RX_SEC) )
{
/* copy primary rx to tx buffer */
memcpy(&(port->txbuf[idx][ETH_HEADERSIZE]), &(port->rxbuf[idx]), port->txbuflength[idx] - ETH_HEADERSIZE);
}
osal_timer_start (&timer2, EC_TIMEOUTRET);
/* resend secondary tx */
ecx_outframe(port, idx, 1);
do
{
/* retrieve frame */
wkc2 = ecx_inframe(port, idx, 1);
} while ((wkc2 <= EC_NOFRAME) && !osal_timer_is_expired(&timer2));
if (wkc2 > EC_NOFRAME)
{
/* copy secondary result to primary rx buffer */
memcpy(&(port->rxbuf[idx]), &(port->redport->rxbuf[idx]), port->txbuflength[idx] - ETH_HEADERSIZE);
wkc = wkc2;
}
}
}
/* return WKC or EC_NOFRAME */
return wkc;
}
/** Blocking receive frame function. Calls ec_waitinframe_red().
* @param[in] port = port context struct
* @param[in] idx = requested index of frame
* @param[in] timeout = timeout in us
* @return Workcounter if a frame is found with corresponding index, otherwise
* EC_NOFRAME.
*/
int ecx_waitinframe(ecx_portt *port, uint8 idx, int timeout)
{
int wkc;
osal_timert timer;
osal_timer_start (&timer, timeout);
wkc = ecx_waitinframe_red(port, idx, &timer);
return wkc;
}
/** Blocking send and receive frame function. Used for non processdata frames.
* A datagram is build into a frame and transmitted via this function. It waits
* for an answer and returns the workcounter. The function retries if time is
* left and the result is WKC=0 or no frame received.
*
* The function calls ec_outframe_red() and ec_waitinframe_red().
*
* @param[in] port = port context struct
* @param[in] idx = index of frame
* @param[in] timeout = timeout in us
* @return Workcounter or EC_NOFRAME
*/
int ecx_srconfirm(ecx_portt *port, uint8 idx, int timeout)
{
int wkc = EC_NOFRAME;
osal_timert timer1, timer2;
osal_timer_start (&timer1, timeout);
do
{
/* tx frame on primary and if in redundant mode a dummy on secondary */
ecx_outframe_red(port, idx);
if (timeout < EC_TIMEOUTRET)
{
osal_timer_start (&timer2, timeout);
}
else
{
/* normally use partial timeout for rx */
osal_timer_start (&timer2, EC_TIMEOUTRET);
}
/* get frame from primary or if in redundant mode possibly from secondary */
wkc = ecx_waitinframe_red(port, idx, &timer2);
/* wait for answer with WKC>=0 or otherwise retry until timeout */
} while ((wkc <= EC_NOFRAME) && !osal_timer_is_expired (&timer1));
return wkc;
}
#ifdef EC_VER1
int ec_setupnic(const char *ifname, int secondary)
{
return ecx_setupnic(&ecx_port, ifname, secondary);
}
int ec_closenic(void)
{
return ecx_closenic(&ecx_port);
}
uint8 ec_getindex(void)
{
return ecx_getindex(&ecx_port);
}
void ec_setbufstat(uint8 idx, int bufstat)
{
ecx_setbufstat(&ecx_port, idx, bufstat);
}
int ec_outframe(uint8 idx, int stacknumber)
{
return ecx_outframe(&ecx_port, idx, stacknumber);
}
int ec_outframe_red(uint8 idx)
{
return ecx_outframe_red(&ecx_port, idx);
}
int ec_inframe(uint8 idx, int stacknumber)
{
return ecx_inframe(&ecx_port, idx, stacknumber);
}
int ec_waitinframe(uint8 idx, int timeout)
{
return ecx_waitinframe(&ecx_port, idx, timeout);
}
int ec_srconfirm(uint8 idx, int timeout)
{
return ecx_srconfirm(&ecx_port, idx, timeout);
}
#endif
+120
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.c
*/
#ifndef _nicdrvh_
#define _nicdrvh_
#ifdef __cplusplus
extern "C"
{
#endif
#include <pcap/pcap.h>
/** pointer structure to Tx and Rx stacks */
typedef struct
{
/** socket connection used */
pcap_t **sock;
/** tx buffer */
ec_bufT (*txbuf)[EC_MAXBUF];
/** tx buffer lengths */
int (*txbuflength)[EC_MAXBUF];
/** temporary receive buffer */
ec_bufT *tempbuf;
/** rx buffers */
ec_bufT (*rxbuf)[EC_MAXBUF];
/** rx buffer status fields */
int (*rxbufstat)[EC_MAXBUF];
/** received MAC source address (middle word) */
int (*rxsa)[EC_MAXBUF];
} ec_stackT;
/** pointer structure to buffers for redundant port */
typedef struct
{
ec_stackT stack;
pcap_t *sockhandle;
/** rx buffers */
ec_bufT rxbuf[EC_MAXBUF];
/** rx buffer status */
int rxbufstat[EC_MAXBUF];
/** rx MAC source address */
int rxsa[EC_MAXBUF];
/** temporary rx buffer */
ec_bufT tempinbuf;
} ecx_redportt;
/** pointer structure to buffers, vars and mutexes for port instantiation */
typedef struct
{
ec_stackT stack;
pcap_t *sockhandle;
/** rx buffers */
ec_bufT rxbuf[EC_MAXBUF];
/** rx buffer status */
int rxbufstat[EC_MAXBUF];
/** rx MAC source address */
int rxsa[EC_MAXBUF];
/** temporary rx buffer */
ec_bufT tempinbuf;
/** temporary rx buffer status */
int tempinbufs;
/** transmit buffers */
ec_bufT txbuf[EC_MAXBUF];
/** transmit buffer lenghts */
int txbuflength[EC_MAXBUF];
/** temporary tx buffer */
ec_bufT txbuf2;
/** temporary tx buffer length */
int txbuflength2;
/** last used frame index */
uint8 lastidx;
/** current redundancy state */
int redstate;
/** pointer to redundancy port and buffers */
ecx_redportt *redport;
pthread_mutex_t getindex_mutex;
pthread_mutex_t tx_mutex;
pthread_mutex_t rx_mutex;
} ecx_portt;
extern const uint16 priMAC[3];
extern const uint16 secMAC[3];
#ifdef EC_VER1
extern ecx_portt ecx_port;
extern ecx_redportt ecx_redport;
int ec_setupnic(const char * ifname, int secondary);
int ec_closenic(void);
void ec_setbufstat(uint8 idx, int bufstat);
uint8 ec_getindex(void);
int ec_outframe(uint8 idx, int sock);
int ec_outframe_red(uint8 idx);
int ec_waitinframe(uint8 idx, int timeout);
int ec_srconfirm(uint8 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, uint8 idx, int bufstat);
uint8 ecx_getindex(ecx_portt *port);
int ecx_outframe(ecx_portt *port, uint8 idx, int sock);
int ecx_outframe_red(ecx_portt *port, uint8 idx);
int ecx_waitinframe(ecx_portt *port, uint8 idx, int timeout);
int ecx_srconfirm(ecx_portt *port, uint8 idx,int timeout);
#ifdef __cplusplus
}
#endif
#endif
+117
View File
@@ -0,0 +1,117 @@
/*
* Licensed under the GNU General Public License version 2 with exceptions. See
* LICENSE file in the project root for full license information
*/
#include <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 "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)
{
int i;
struct if_nameindex *ids;
ec_adaptert * adapter;
ec_adaptert * prev_adapter;
ec_adaptert * ret_adapter = NULL;
/* Iterate all devices and create a local copy holding the name and
* description.
*/
ids = if_nameindex ();
for(i = 0; ids[i].if_index != 0; i++)
{
adapter = (ec_adaptert *)malloc(sizeof(ec_adaptert));
/* If we got more than one adapter save link list pointer to previous
* adapter.
* Else save as pointer to return.
*/
if (i)
{
prev_adapter->next = adapter;
}
else
{
ret_adapter = adapter;
}
/* fetch description and name, in macosx we use the same on both */
adapter->next = NULL;
if (ids[i].if_name)
{
strncpy(adapter->name, ids[i].if_name, EC_MAXLEN_ADAPTERNAME);
adapter->name[EC_MAXLEN_ADAPTERNAME-1] = '\0';
strncpy(adapter->desc, ids[i].if_name, EC_MAXLEN_ADAPTERNAME);
adapter->desc[EC_MAXLEN_ADAPTERNAME-1] = '\0';
}
else
{
adapter->name[0] = '\0';
adapter->desc[0] = '\0';
}
prev_adapter = adapter;
}
if_freenameindex (ids);
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)
{
ec_adaptert * next_adapter;
/* Iterate the linked list and free all elements holding
* adapter information
*/
if(adapter)
{
next_adapter = adapter->next;
free (adapter);
while (next_adapter)
{
adapter = next_adapter;
next_adapter = adapter->next;
free (adapter);
}
}
}
+31
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
+696
View File
@@ -0,0 +1,696 @@
/*
* Licensed under the GNU General Public License version 2 with exceptions. See
* LICENSE file in the project root for full license information
*/
/** \file
* \brief
* EtherCAT RAW socket driver.
*
* Low level interface functions to send and receive EtherCAT packets.
* EtherCAT has the property that packets are only send by the master,
* and the send packets always return in the receive buffer.
* There can be multiple packets "on the wire" before they return.
* To combine the received packets with the original send packets a buffer
* system is installed. The identifier is put in the index item of the
* EtherCAT header. The index is stored and compared when a frame is received.
* If there is a match the packet can be combined with the transmit packet
* and returned to the higher level function.
*
* The socket layer can exhibit a reversal in the packet order (rare).
* If the Tx order is A-B-C the return order could be A-C-B. The indexed buffer
* will reorder the packets automatically.
*
* The "redundant" option will configure two sockets and two NIC interfaces.
* Slaves are connected to both interfaces, one on the IN port and one on the
* OUT port. Packets are send via both interfaces. Any one of the connections
* (also an interconnect) can be removed and the slaves are still serviced with
* packets. The software layer will detect the possible failure modes and
* compensate. If needed the packets from interface A are resent through interface B.
* This layer if fully transparent for the higher layers.
*/
#include <sys/time.h>
#include <time.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <net/bpf.h>
#include <sys/socket.h>
#include <net/if.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <fcntl.h>
#include <string.h>
#include <pthread.h>
#include <assert.h>
#include "oshw.h"
#include "osal.h"
/** Redundancy modes */
enum
{
/** No redundancy, single NIC mode */
ECT_RED_NONE,
/** Double redundant NIC connection */
ECT_RED_DOUBLE
};
/** Primary source MAC address used for EtherCAT.
* This address is not the MAC address used from the NIC.
* EtherCAT does not care about MAC addressing, but it is used here to
* differentiate the route the packet traverses through the EtherCAT
* segment. This is needed to find out the packet flow in redundant
* configurations. */
const uint16 priMAC[3] = { 0x0201, 0x0101, 0x0101 };
/** Secondary source MAC address used for EtherCAT. */
const uint16 secMAC[3] = { 0x0604, 0x0404, 0x0404 };
/** second MAC word is used for identification */
#define RX_PRIM priMAC[1]
/** second MAC word is used for identification */
#define RX_SEC secMAC[1]
static void ecx_clear_rxbufstat(int *rxbufstat)
{
int i;
for(i = 0; i < EC_MAXBUF; i++)
{
rxbufstat[i] = EC_BUF_EMPTY;
}
}
/** Basic setup to connect NIC to socket.
* @param[in] port = port context struct
* @param[in] ifname = Name of NIC device, f.e. "eth0"
* @param[in] secondary = if >0 then use secondary stack instead of primary
* @return >0 if succeeded
*/
int ecx_setupnic(ecx_portt *port, const char *ifname, int secondary)
{
int i;
int r, rval;
struct timeval timeout;
struct ifreq ifr;
int *bpf;
// const uint8_t bpffnamelen = 12;
char fname[13] = {0};
const int maxbpffile = 1000;
uint true_val = 1;
rval = 0;
if (secondary)
{
/* secondary port struct available? */
if (port->redport)
{
/* when using secondary socket it is automatically a redundant setup */
bpf = &(port->redport->sockhandle);
*bpf = -1;
port->redstate = ECT_RED_DOUBLE;
port->redport->stack.sock = &(port->redport->sockhandle);
port->redport->stack.txbuf = &(port->txbuf);
port->redport->stack.txbuflength = &(port->txbuflength);
port->redport->stack.tempbuf = &(port->redport->tempinbuf);
port->redport->stack.rxbuf = &(port->redport->rxbuf);
port->redport->stack.rxbufstat = &(port->redport->rxbufstat);
port->redport->stack.rxsa = &(port->redport->rxsa);
ecx_clear_rxbufstat(&(port->redport->rxbufstat[0]));
}
else
{
/* fail */
return 0;
}
}
else
{
pthread_mutex_init(&(port->getindex_mutex), NULL);
pthread_mutex_init(&(port->tx_mutex) , NULL);
pthread_mutex_init(&(port->rx_mutex) , NULL);
port->sockhandle = -1;
port->lastidx = 0;
port->redstate = ECT_RED_NONE;
port->stack.sock = &(port->sockhandle);
port->stack.txbuf = &(port->txbuf);
port->stack.txbuflength = &(port->txbuflength);
port->stack.tempbuf = &(port->tempinbuf);
port->stack.rxbuf = &(port->rxbuf);
port->stack.rxbufstat = &(port->rxbufstat);
port->stack.rxsa = &(port->rxsa);
ecx_clear_rxbufstat(&(port->rxbufstat[0]));
bpf = &(port->sockhandle);
}
/* Open a bpf file descriptor */
*bpf = -1;
for(i = 0; *bpf == -1 && i < maxbpffile; ++i)
{
sprintf(fname, "/dev/bpf%i", i);
*bpf = open(fname, O_RDWR);
}
if(*bpf == -1)
{
/* Failed to open bpf */
perror("Could not open bpf\n");
return 0;
}
/* Need to hand the same buffer size as bpf expects,
force bpf to use our buffer size! */
uint buffer_len = sizeof(ec_bufT);
if (ioctl(*bpf, BIOCSBLEN, &buffer_len) == -1) {
perror("BIOCGBLEN");
}
if (buffer_len < 1518) {
printf("buffer_len %d < 1518\n", buffer_len);
return 0;
}
/* connect bpf to NIC by name */
strcpy(ifr.ifr_name, ifname);
if(ioctl(*bpf, BIOCSETIF, &ifr) == -1) {
perror("BIOCSETIF");
return 0;
}
/* Set required bpf options */
/* Activate immediate mode */
if (ioctl(*bpf, BIOCIMMEDIATE, &true_val) == -1) {
perror("BIOCIMMEDIATE");
return 0;
}
/* Set interface in promiscuous mode */
if (ioctl(*bpf, BIOCPROMISC, &true_val) == -1) {
perror("BIOCPROMISC");
return 0;
}
/* Allow to have custom source address */
if (ioctl(*bpf, BIOCSHDRCMPLT, &true_val) == -1) {
perror("BIOCSHDRCMPLT");
return 0;
}
/* Listen only to incomming messages */
uint direction = BPF_D_IN;
if (ioctl(*bpf, BIOCSDIRECTION, &direction) == -1) {
perror("BIOCSDIRECTION");
return 0;
}
/* Set read timeout */
timeout.tv_sec = 0;
timeout.tv_usec = 11000;
if (ioctl(*bpf, BIOCSRTIMEOUT, &timeout) == -1) {
perror("BIOCSRTIMEOUT");
return 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;
}
/** Close sockets used
* @param[in] port = port context struct
* @return 0
*/
int ecx_closenic(ecx_portt *port)
{
if (port->sockhandle >= 0)
close(port->sockhandle);
if ((port->redport) && (port->redport->sockhandle >= 0))
close(port->redport->sockhandle);
return 0;
}
/** Fill buffer with ethernet header structure.
* Destination MAC is always broadcast.
* Ethertype is always ETH_P_ECAT.
* @param[out] p = buffer
*/
void ec_setupheader(void *p)
{
ec_etherheadert *bp;
bp = 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.
*/
uint8 ecx_getindex(ecx_portt *port)
{
uint8 idx;
uint8 cnt;
pthread_mutex_lock( &(port->getindex_mutex) );
idx = port->lastidx + 1;
/* index can't be larger than buffer array */
if (idx >= EC_MAXBUF)
{
idx = 0;
}
cnt = 0;
/* try to find unused index */
while ((port->rxbufstat[idx] != EC_BUF_EMPTY) && (cnt < EC_MAXBUF))
{
idx++;
cnt++;
if (idx >= EC_MAXBUF)
{
idx = 0;
}
}
port->rxbufstat[idx] = EC_BUF_ALLOC;
if (port->redstate != ECT_RED_NONE)
port->redport->rxbufstat[idx] = EC_BUF_ALLOC;
port->lastidx = idx;
pthread_mutex_unlock( &(port->getindex_mutex) );
return idx;
}
/** Set rx buffer status.
* @param[in] port = port context struct
* @param[in] idx = index in buffer array
* @param[in] bufstat = status to set
*/
void ecx_setbufstat(ecx_portt *port, uint8 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, uint8 idx, int stacknumber)
{
int lp, rval;
ec_stackT *stack;
if (!stacknumber)
{
stack = &(port->stack);
}
else
{
stack = &(port->redport->stack);
}
lp = (*stack->txbuflength)[idx];
//rval = send(*stack->sock, (*stack->txbuf)[idx], lp, 0);
(*stack->rxbufstat)[idx] = EC_BUF_TX;
rval = write (*stack->sock,(*stack->txbuf)[idx], lp);
if (rval == -1)
{
(*stack->rxbufstat)[idx] = EC_BUF_EMPTY;
}
return rval;
}
/** Transmit buffer over socket (non blocking).
* @param[in] port = port context struct
* @param[in] idx = index in tx buffer array
* @return socket send result
*/
int ecx_outframe_red(ecx_portt *port, uint8 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)
{
pthread_mutex_lock( &(port->tx_mutex) );
ehp = (ec_etherheadert *)&(port->txbuf2);
/* use dummy frame for secondary socket transmit (BRD) */
datagramP = (ec_comt*)&(port->txbuf2[ETH_HEADERSIZE]);
/* write index to frame */
datagramP->index = idx;
/* rewrite MAC source address 1 to secondary */
ehp->sa1 = htons(secMAC[1]);
/* transmit over secondary socket */
//send(port->redport->sockhandle, &(port->txbuf2), port->txbuflength2 , 0);
port->redport->rxbufstat[idx] = EC_BUF_TX;
if (write(port->redport->sockhandle, &(port->txbuf2), port->txbuflength2) == -1)
{
(*stack->rxbufstat)[idx] = EC_BUF_EMPTY;
}
pthread_mutex_unlock( &(port->tx_mutex) );
}
return rval;
}
/** Non blocking read of socket. Put frame in temporary buffer.
* @param[in] port = port context struct
* @param[in] stacknumber = 0=primary 1=secondary stack
* @return >0 if frame is available and read
*/
static int ecx_recvpkt(ecx_portt *port, int stacknumber)
{
int lp, bytesrx;
ec_stackT *stack;
if (!stacknumber)
{
stack = &(port->stack);
}
else
{
stack = &(port->redport->stack);
}
lp = sizeof(port->tempinbuf);
//bytesrx = recv(*stack->sock, (*stack->tempbuf), lp, 0);
bytesrx = read(*stack->sock, (*stack->tempbuf), lp);
port->tempinbufs = bytesrx;
return (bytesrx > 0);
}
/** Non blocking receive frame function. Uses RX buffer and index to combine
* read frame with transmitted frame. To compensate for received frames that
* are out-of-order all frames are stored in their respective indexed buffer.
* If a frame was placed in the buffer previously, the function retrieves it
* from that buffer index without calling ec_recvpkt. If the requested index
* is not already in the buffer it calls ec_recvpkt to fetch it. There are
* three options now, 1 no frame read, so exit. 2 frame read but other
* than requested index, store in buffer and exit. 3 frame read with matching
* index, store in buffer, set completed flag in buffer status and exit.
*
* @param[in] port = port context struct
* @param[in] idx = requested index of frame
* @param[in] stacknumber = 0=primary 1=secondary stack
* @return Workcounter if a frame is found with corresponding index, otherwise
* EC_NOFRAME or EC_OTHERFRAME.
*/
int ecx_inframe(ecx_portt *port, uint8 idx, int stacknumber)
{
uint16 l;
int rval;
uint8 idxf;
ec_etherheadert *ehp;
ec_comt *ecp;
ec_stackT *stack;
ec_bufT *rxbuf;
if (!stacknumber)
{
stack = &(port->stack);
}
else
{
stack = &(port->redport->stack);
}
rval = EC_NOFRAME;
rxbuf = &(*stack->rxbuf)[idx];
/* check if requested index is already in buffer ? */
if ((idx < EC_MAXBUF) && ((*stack->rxbufstat)[idx] == EC_BUF_RCVD))
{
l = (*rxbuf)[0] + ((uint16)((*rxbuf)[1] & 0x0f) << 8);
/* return WKC */
rval = ((*rxbuf)[l] + ((uint16)(*rxbuf)[l + 1] << 8));
/* mark as completed */
(*stack->rxbufstat)[idx] = EC_BUF_COMPLETE;
}
else
{
pthread_mutex_lock(&(port->rx_mutex));
/* non blocking call to retrieve frame from socket */
if (ecx_recvpkt(port, stacknumber))
{
/* The data read from /dev/bpf includes an extra header, skip it. */
struct bpf_hdr *bpfh = (struct bpf_hdr *)(stack->tempbuf);
rval = EC_OTHERFRAME;
ehp =(ec_etherheadert*)(*stack->tempbuf + bpfh->bh_hdrlen);
/* check if it is an EtherCAT frame */
if (ehp->etype == htons(ETH_P_ECAT))
{
/* The EtherCAT header follows the ethernet frame header. */
ecp =(ec_comt*)(&ehp[1]);
l = etohs(ecp->elength) & 0x0fff;
idxf = ecp->index;
/* found index equals requested index ? */
if (idxf == idx)
{
/* yes, put it in the buffer array (strip headers) */
memcpy(rxbuf, &(ehp[1]), port->tempinbufs - ((uint32_t)ecp - (uint32_t)*stack->tempbuf));
/* 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);
}
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] = ntohs(ehp->sa1);
}
else
{
/* strange things happened */
}
}
}
}
pthread_mutex_unlock( &(port->rx_mutex) );
}
/* WKC if matching frame found */
return rval;
}
/** Blocking redundant receive frame function. If redundant mode is not active then
* it skips the secondary stack and redundancy functions. In redundant mode it waits
* for both (primary and secondary) frames to come in. The result goes in an decision
* tree that decides, depending on the route of the packet and its possible missing arrival,
* how to reroute the original packet to get the data in an other try.
*
* @param[in] port = port context struct
* @param[in] idx = requested index of frame
* @param[in] timer = absolute timeout time
* @return Workcounter if a frame is found with corresponding index, otherwise
* EC_NOFRAME.
*/
static int ecx_waitinframe_red(ecx_portt *port, uint8 idx, osal_timert *timer)
{
osal_timert timer2;
int wkc = EC_NOFRAME;
int wkc2 = EC_NOFRAME;
int primrx, secrx;
/* if not in redundant mode then always assume secondary is OK */
if (port->redstate == ECT_RED_NONE)
wkc2 = 0;
do
{
/* only read frame if not already in */
if (wkc <= EC_NOFRAME)
wkc = ecx_inframe(port, idx, 0);
/* only try secondary if in redundant mode */
if (port->redstate != ECT_RED_NONE)
{
/* only read frame if not already in */
if (wkc2 <= EC_NOFRAME)
wkc2 = ecx_inframe(port, idx, 1);
}
/* wait for both frames to arrive or timeout */
} while (((wkc <= EC_NOFRAME) || (wkc2 <= EC_NOFRAME)) && !osal_timer_is_expired(timer));
/* only do redundant functions when in redundant mode */
if (port->redstate != ECT_RED_NONE)
{
/* primrx if the received MAC source on primary socket */
primrx = 0;
if (wkc > EC_NOFRAME) primrx = port->rxsa[idx];
/* secrx if the received MAC source on psecondary socket */
secrx = 0;
if (wkc2 > EC_NOFRAME) secrx = port->redport->rxsa[idx];
/* primary socket got secondary frame and secondary socket got primary frame */
/* normal situation in redundant mode */
if ( ((primrx == RX_SEC) && (secrx == RX_PRIM)) )
{
/* copy secondary buffer to primary */
memcpy(&(port->rxbuf[idx]), &(port->redport->rxbuf[idx]), port->txbuflength[idx] - ETH_HEADERSIZE);
wkc = wkc2;
}
/* primary socket got nothing or primary frame, and secondary socket got secondary frame */
/* we need to resend TX packet */
if ( ((primrx == 0) && (secrx == RX_SEC)) ||
((primrx == RX_PRIM) && (secrx == RX_SEC)) )
{
/* If both primary and secondary have partial connection retransmit the primary received
* frame over the secondary socket. The result from the secondary received frame is a combined
* frame that traversed all slaves in standard order. */
if ( (primrx == RX_PRIM) && (secrx == RX_SEC) )
{
/* copy primary rx to tx buffer */
memcpy(&(port->txbuf[idx][ETH_HEADERSIZE]), &(port->rxbuf[idx]), port->txbuflength[idx] - ETH_HEADERSIZE);
}
osal_timer_start (&timer2, EC_TIMEOUTRET);
/* resend secondary tx */
ecx_outframe(port, idx, 1);
do
{
/* retrieve frame */
wkc2 = ecx_inframe(port, idx, 1);
} while ((wkc2 <= EC_NOFRAME) && !osal_timer_is_expired(&timer2));
if (wkc2 > EC_NOFRAME)
{
/* copy secondary result to primary rx buffer */
memcpy(&(port->rxbuf[idx]), &(port->redport->rxbuf[idx]), port->txbuflength[idx] - ETH_HEADERSIZE);
wkc = wkc2;
}
}
}
/* return WKC or EC_NOFRAME */
return wkc;
}
/** Blocking receive frame function. Calls ec_waitinframe_red().
* @param[in] port = port context struct
* @param[in] idx = requested index of frame
* @param[in] timeout = timeout in us
* @return Workcounter if a frame is found with corresponding index, otherwise
* EC_NOFRAME.
*/
int ecx_waitinframe(ecx_portt *port, uint8 idx, int timeout)
{
int wkc;
osal_timert timer;
osal_timer_start (&timer, timeout);
wkc = ecx_waitinframe_red(port, idx, &timer);
return wkc;
}
/** Blocking send and receive frame function. Used for non processdata frames.
* A datagram is build into a frame and transmitted via this function. It waits
* for an answer and returns the workcounter. The function retries if time is
* left and the result is WKC=0 or no frame received.
*
* The function calls ec_outframe_red() and ec_waitinframe_red().
*
* @param[in] port = port context struct
* @param[in] idx = index of frame
* @param[in] timeout = timeout in us
* @return Workcounter or EC_NOFRAME
*/
int ecx_srconfirm(ecx_portt *port, uint8 idx, int timeout)
{
int wkc = EC_NOFRAME;
osal_timert timer1, timer2;
osal_timer_start (&timer1, timeout);
do
{
/* tx frame on primary and if in redundant mode a dummy on secondary */
ecx_outframe_red(port, idx);
if (timeout < EC_TIMEOUTRET)
{
osal_timer_start (&timer2, timeout);
}
else
{
/* normally use partial timeout for rx */
osal_timer_start (&timer2, EC_TIMEOUTRET);
}
/* get frame from primary or if in redundant mode possibly from secondary */
wkc = ecx_waitinframe_red(port, idx, &timer2);
/* wait for answer with WKC>=0 or otherwise retry until timeout */
} while ((wkc <= EC_NOFRAME) && !osal_timer_is_expired (&timer1));
return wkc;
}
#ifdef EC_VER1
int ec_setupnic(const char *ifname, int secondary)
{
return ecx_setupnic(&ecx_port, ifname, secondary);
}
int ec_closenic(void)
{
return ecx_closenic(&ecx_port);
}
uint8 ec_getindex(void)
{
return ecx_getindex(&ecx_port);
}
void ec_setbufstat(uint8 idx, int bufstat)
{
ecx_setbufstat(&ecx_port, idx, bufstat);
}
int ec_outframe(uint8 idx, int stacknumber)
{
return ecx_outframe(&ecx_port, idx, stacknumber);
}
int ec_outframe_red(uint8 idx)
{
return ecx_outframe_red(&ecx_port, idx);
}
int ec_inframe(uint8 idx, int stacknumber)
{
return ecx_inframe(&ecx_port, idx, stacknumber);
}
int ec_waitinframe(uint8 idx, int timeout)
{
return ecx_waitinframe(&ecx_port, idx, timeout);
}
int ec_srconfirm(uint8 idx, int timeout)
{
return ecx_srconfirm(&ecx_port, idx, timeout);
}
#endif
+120
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.c
*/
#ifndef _nicdrvh_
#define _nicdrvh_
#ifdef __cplusplus
extern "C"
{
#endif
#include <pthread.h>
/** pointer structure to Tx and Rx stacks */
typedef struct
{
/** socket connection used */
int *sock;
/** tx buffer */
ec_bufT (*txbuf)[EC_MAXBUF];
/** tx buffer lengths */
int (*txbuflength)[EC_MAXBUF];
/** temporary receive buffer */
ec_bufT *tempbuf;
/** rx buffers */
ec_bufT (*rxbuf)[EC_MAXBUF];
/** rx buffer status fields */
int (*rxbufstat)[EC_MAXBUF];
/** received MAC source address (middle word) */
int (*rxsa)[EC_MAXBUF];
} ec_stackT;
/** pointer structure to buffers for redundant port */
typedef struct
{
ec_stackT stack;
int sockhandle;
/** rx buffers */
ec_bufT rxbuf[EC_MAXBUF];
/** rx buffer status */
int rxbufstat[EC_MAXBUF];
/** rx MAC source address */
int rxsa[EC_MAXBUF];
/** temporary rx buffer */
ec_bufT tempinbuf;
} ecx_redportt;
/** pointer structure to buffers, vars and mutexes for port instantiation */
typedef struct
{
ec_stackT stack;
int sockhandle;
/** rx buffers */
ec_bufT rxbuf[EC_MAXBUF];
/** rx buffer status */
int rxbufstat[EC_MAXBUF];
/** rx MAC source address */
int rxsa[EC_MAXBUF];
/** temporary rx buffer */
ec_bufT tempinbuf;
/** temporary rx buffer status */
int tempinbufs;
/** transmit buffers */
ec_bufT txbuf[EC_MAXBUF];
/** transmit buffer lengths */
int txbuflength[EC_MAXBUF];
/** temporary tx buffer */
ec_bufT txbuf2;
/** temporary tx buffer length */
int txbuflength2;
/** last used frame index */
uint8 lastidx;
/** current redundancy state */
int redstate;
/** pointer to redundancy port and buffers */
ecx_redportt *redport;
pthread_mutex_t getindex_mutex;
pthread_mutex_t tx_mutex;
pthread_mutex_t rx_mutex;
} ecx_portt;
extern const uint16 priMAC[3];
extern const uint16 secMAC[3];
#ifdef EC_VER1
extern ecx_portt ecx_port;
extern ecx_redportt ecx_redport;
int ec_setupnic(const char * ifname, int secondary);
int ec_closenic(void);
void ec_setbufstat(uint8 idx, int bufstat);
uint8 ec_getindex(void);
int ec_outframe(uint8 idx, int sock);
int ec_outframe_red(uint8 idx);
int ec_waitinframe(uint8 idx, int timeout);
int ec_srconfirm(uint8 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, uint8 idx, int bufstat);
uint8 ecx_getindex(ecx_portt *port);
int ecx_outframe(ecx_portt *port, uint8 idx, int sock);
int ecx_outframe_red(ecx_portt *port, uint8 idx);
int ecx_waitinframe(ecx_portt *port, uint8 idx, int timeout);
int ecx_srconfirm(ecx_portt *port, uint8 idx,int timeout);
#ifdef __cplusplus
}
#endif
#endif
+117
View File
@@ -0,0 +1,117 @@
/*
* Licensed under the GNU General Public License version 2 with exceptions. See
* LICENSE file in the project root for full license information
*/
#include <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 "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)
{
int i;
struct if_nameindex *ids;
ec_adaptert * adapter;
ec_adaptert * prev_adapter;
ec_adaptert * ret_adapter = NULL;
/* Iterate all devices and create a local copy holding the name and
* description.
*/
ids = if_nameindex ();
for(i = 0; ids[i].if_index != 0; i++)
{
adapter = (ec_adaptert *)malloc(sizeof(ec_adaptert));
/* If we got more than one adapter save link list pointer to previous
* adapter.
* Else save as pointer to return.
*/
if (i)
{
prev_adapter->next = adapter;
}
else
{
ret_adapter = adapter;
}
/* fetch description and name, in rtems we use the same on both */
adapter->next = NULL;
if (ids[i].if_name)
{
strncpy(adapter->name, ids[i].if_name, EC_MAXLEN_ADAPTERNAME);
adapter->name[EC_MAXLEN_ADAPTERNAME-1] = '\0';
strncpy(adapter->desc, ids[i].if_name, EC_MAXLEN_ADAPTERNAME);
adapter->desc[EC_MAXLEN_ADAPTERNAME-1] = '\0';
}
else
{
adapter->name[0] = '\0';
adapter->desc[0] = '\0';
}
prev_adapter = adapter;
}
if_freenameindex (ids);
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)
{
ec_adaptert * next_adapter;
/* Iterate the linked list and free all elements holding
* adapter information
*/
if(adapter)
{
next_adapter = adapter->next;
free (adapter);
while (next_adapter)
{
adapter = next_adapter;
next_adapter = adapter->next;
free (adapter);
}
}
}
+31
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
+802
View File
@@ -0,0 +1,802 @@
/******************************************************************************
* * *** ***
* *** *** ***
* *** **** ********** *** ***** *** **** *****
* ********* ********** *** ********* ************ *********
* **** *** *** *** *** **** ***
* *** *** ****** *** *********** *** **** *****
* *** *** ****** *** ************* *** **** *****
* *** **** **** *** *** *** **** ***
* *** ******* ***** ************** ************* *********
* *** ***** *** ******* ** ** ****** *****
* t h e r e a l t i m e t a r g e t e x p e r t s
*
* http://www.rt-labs.com
* Copyright (C) 2007. rt-labs AB, Sweden. All rights reserved.
*------------------------------------------------------------------------------
* $Id: fec_ecat.c 138 2014-04-11 09:11:48Z rtlfrm $
*------------------------------------------------------------------------------
*/
#include "fec_ecat.h"
#include <dev.h>
#include <kern.h>
#include <string.h>
#include <uassert.h>
#include <eth/fec_buffer.h>
#include <eth/phy/mii.h>
#include <bsp.h>
#include <config.h>
#undef RTK_DEBUG /* Print debugging info? */
#undef RTK_DEBUG_DATA /* Print packets? */
#ifdef RTK_DEBUG
#define DPRINT(...) rprintp ("FEC: "__VA_ARGS__)
#else
#define DPRINT(...)
#endif /* DEBUG */
#ifdef RTK_DEBUG_DATA
#define DUMP_PACKET(p, l) fec_ecat_dump_packet (p, l)
#else
#define DUMP_PACKET(p, l)
#endif /* RTK_DEBUG_DATA */
#define ETHERNET_FCS_SIZE_IN_BYTES 4 /* Frame Check Sequence (32-bit CRC) */
#define DELAY(ms) task_delay (tick_from_ms (ms) + 1)
#define ETH_PHY_ADDRESS 0x1 /* Default jumper setting on TWR-K60F */
#define NUM_BUFFERS 2
#define FEC_ALLIGNED __attribute__((section(".dma"),aligned(16)))
/*
* "The device's system clock is connected to the module clock,
* as named in the Ethernet chapter. The minimum system clock frequency
* for 100 Mbps operation is 25 MHz."
*
* - Kinetis K60 manual ch. 3.9.1.1 "Ethernet Clocking Options"
*/
#define FEC_MIN_MODULE_CLOCK_Hz (25 * 1000 * 1000)
/* Note that the core clock and the system clock has the same clock frequency */
#define FEC_MODULE_CLOCK_Hz CFG_IPG_CLOCK
//----------------------------------------------------------------------------//
typedef struct fec_mac_t
{
uint32_t lower; /* octets 0, 1, 2, 3 */
uint32_t upper; /* octets 4, 5 */
} fec_mac_t;
/* Message Information Block (MIB) Counters */
typedef struct fec_mib
{
uint32_t rmon_t_drop; /* MBAR_ETH + 0x200 */
uint32_t rmon_t_packets; /* MBAR_ETH + 0x204 */
uint32_t rmon_t_bc_pkt; /* MBAR_ETH + 0x208 */
uint32_t rmon_t_mc_pkt; /* MBAR_ETH + 0x20C */
uint32_t rmon_t_crc_align; /* MBAR_ETH + 0x210 */
uint32_t rmon_t_undersize; /* MBAR_ETH + 0x214 */
uint32_t rmon_t_oversize; /* MBAR_ETH + 0x218 */
uint32_t rmon_t_frag; /* MBAR_ETH + 0x21C */
uint32_t rmon_t_jab; /* MBAR_ETH + 0x220 */
uint32_t rmon_t_col; /* MBAR_ETH + 0x224 */
uint32_t rmon_t_p64; /* MBAR_ETH + 0x228 */
uint32_t rmon_t_p65to127; /* MBAR_ETH + 0x22C */
uint32_t rmon_t_p128to255; /* MBAR_ETH + 0x230 */
uint32_t rmon_t_p256to511; /* MBAR_ETH + 0x234 */
uint32_t rmon_t_p512to1023; /* MBAR_ETH + 0x238 */
uint32_t rmon_t_p1024to2047; /* MBAR_ETH + 0x23C */
uint32_t rmon_t_p_gte2048; /* MBAR_ETH + 0x240 */
uint32_t rmon_t_octets; /* MBAR_ETH + 0x244 */
uint32_t ieee_t_drop; /* MBAR_ETH + 0x248 */
uint32_t ieee_t_frame_ok; /* MBAR_ETH + 0x24C */
uint32_t ieee_t_1col; /* MBAR_ETH + 0x250 */
uint32_t ieee_t_mcol; /* MBAR_ETH + 0x254 */
uint32_t ieee_t_def; /* MBAR_ETH + 0x258 */
uint32_t ieee_t_lcol; /* MBAR_ETH + 0x25C */
uint32_t ieee_t_excol; /* MBAR_ETH + 0x260 */
uint32_t ieee_t_macerr; /* MBAR_ETH + 0x264 */
uint32_t ieee_t_cserr; /* MBAR_ETH + 0x268 */
uint32_t ieee_t_sqe; /* MBAR_ETH + 0x26C */
uint32_t t_fdxfc; /* MBAR_ETH + 0x270 */
uint32_t ieee_t_octets_ok; /* MBAR_ETH + 0x274 */
uint32_t res13[2]; /* MBAR_ETH + 0x278-27C */
uint32_t rmon_r_drop; /* MBAR_ETH + 0x280 */
uint32_t rmon_r_packets; /* MBAR_ETH + 0x284 */
uint32_t rmon_r_bc_pkt; /* MBAR_ETH + 0x288 */
uint32_t rmon_r_mc_pkt; /* MBAR_ETH + 0x28C */
uint32_t rmon_r_crc_align; /* MBAR_ETH + 0x290 */
uint32_t rmon_r_undersize; /* MBAR_ETH + 0x294 */
uint32_t rmon_r_oversize; /* MBAR_ETH + 0x298 */
uint32_t rmon_r_frag; /* MBAR_ETH + 0x29C */
uint32_t rmon_r_jab; /* MBAR_ETH + 0x2A0 */
uint32_t rmon_r_resvd_0; /* MBAR_ETH + 0x2A4 */
uint32_t rmon_r_p64; /* MBAR_ETH + 0x2A8 */
uint32_t rmon_r_p65to127; /* MBAR_ETH + 0x2AC */
uint32_t rmon_r_p128to255; /* MBAR_ETH + 0x2B0 */
uint32_t rmon_r_p256to511; /* MBAR_ETH + 0x2B4 */
uint32_t rmon_r_p512to1023; /* MBAR_ETH + 0x2B8 */
uint32_t rmon_r_p1024to2047; /* MBAR_ETH + 0x2BC */
uint32_t rmon_r_p_gte2048; /* MBAR_ETH + 0x2C0 */
uint32_t rmon_r_octets; /* MBAR_ETH + 0x2C4 */
uint32_t ieee_r_drop; /* MBAR_ETH + 0x2C8 */
uint32_t ieee_r_frame_ok; /* MBAR_ETH + 0x2CC */
uint32_t ieee_r_crc; /* MBAR_ETH + 0x2D0 */
uint32_t ieee_r_align; /* MBAR_ETH + 0x2D4 */
uint32_t r_macerr; /* MBAR_ETH + 0x2D8 */
uint32_t r_fdxfc; /* MBAR_ETH + 0x2DC */
uint32_t ieee_r_octets_ok; /* MBAR_ETH + 0x2E0 */
uint32_t res14[7]; /* MBAR_ETH + 0x2E4-2FC */
} fec_mib_t;
COMPILETIME_ASSERT (sizeof (fec_mib_t) == 0x100);
/* Register read/write struct */
typedef struct reg_fec
{
uint32_t resv0;
uint32_t eir;
uint32_t eimr;
uint32_t resv1;
uint32_t rdar;
uint32_t tdar;
uint32_t resv2[3];
uint32_t ecr;
uint32_t resv3[6];
uint32_t mmfr;
uint32_t mscr;
uint32_t resv4[7];
uint32_t mibc;
uint32_t resv5[7];
uint32_t rcr;
uint32_t resv6[15];
uint32_t tcr;
uint32_t resv7[7];
fec_mac_t pa; /* Physical address. PALR/PAUR */
uint32_t opd;
uint32_t resv8[10];
uint32_t iaur;
uint32_t ialr;
uint32_t gaur;
uint32_t galr;
uint32_t resv9[7];
uint32_t tfwr;
uint32_t resv10;
uint32_t frbr;
uint32_t frsr;
uint32_t resv11[11];
uint32_t erdsr;
uint32_t etdsr;
uint32_t emrbr;
uint32_t resv14;
uint32_t rx_section_full; /* Not present on all controllers */
uint32_t rx_section_empty; /* Not present on all controllers */
uint32_t rx_almost_empty; /* Not present on all controllers */
uint32_t rx_almost_full; /* Not present on all controllers */
uint32_t tx_section_empty; /* Not present on all controllers */
uint32_t tx_almost_empty; /* Not present on all controllers */
uint32_t tx_almost_full; /* Not present on all controllers */
uint32_t resv12[21];
const fec_mib_t mib;
uint32_t fec_miigsk_cfgr; /* Not present on all controllers */
uint32_t resv13;
uint32_t fec_miigsk_enr; /* Not present on all controllers */
uint32_t resv15[0x7d];
fec_mac_t smac[4]; /* Not present on all controllers */
} reg_fec_t;
COMPILETIME_ASSERT (offsetof (reg_fec_t, emrbr) == 0x188);
COMPILETIME_ASSERT (offsetof (reg_fec_t, tx_almost_full) == 0x1A8);
COMPILETIME_ASSERT (offsetof (reg_fec_t, fec_miigsk_cfgr) == 0x300);
COMPILETIME_ASSERT (offsetof (reg_fec_t, smac) == 0x500);
COMPILETIME_ASSERT (offsetof (reg_fec_t, smac[3]) == 0x518);
//----------------------------------------------------------------------------//
/* Bit definitions and macros for FEC_EIR */
#define FEC_EIR_CLEAR_ALL (0xFFF80000)
#define FEC_EIR_HBERR (0x80000000)
#define FEC_EIR_BABR (0x40000000)
#define FEC_EIR_BABT (0x20000000)
#define FEC_EIR_GRA (0x10000000)
#define FEC_EIR_TXF (0x08000000)
#define FEC_EIR_TXB (0x04000000)
#define FEC_EIR_RXF (0x02000000)
#define FEC_EIR_RXB (0x01000000)
#define FEC_EIR_MII (0x00800000)
#define FEC_EIR_EBERR (0x00400000)
#define FEC_EIR_LC (0x00200000)
#define FEC_EIR_RL (0x00100000)
#define FEC_EIR_UN (0x00080000)
/* Bit definitions and macros for FEC_RDAR */
#define FEC_RDAR_R_DES_ACTIVE (0x01000000)
/* Bit definitions and macros for FEC_TDAR */
#define FEC_TDAR_X_DES_ACTIVE (0x01000000)
/* Bit definitions and macros for FEC_ECR */
#define FEC_ECR_DBSWP (0x00000100)
#define FEC_ECR_ETHER_EN (0x00000002)
#define FEC_ECR_RESET (0x00000001)
/* Bit definitions and macros for FEC_MMFR */
#define FEC_MMFR_DATA(x) (((x)&0xFFFF))
#define FEC_MMFR_ST(x) (((x)&0x03)<<30)
#define FEC_MMFR_ST_01 (0x40000000)
#define FEC_MMFR_OP_RD (0x20000000)
#define FEC_MMFR_OP_WR (0x10000000)
#define FEC_MMFR_PA(x) (((x)&0x1F)<<23)
#define FEC_MMFR_RA(x) (((x)&0x1F)<<18)
#define FEC_MMFR_TA(x) (((x)&0x03)<<16)
#define FEC_MMFR_TA_10 (0x00020000)
/* Bit definitions and macros for FEC_MSCR */
#define FEC_MSCR_DIS_PREAMBLE (0x00000080)
#define FEC_MSCR_MII_SPEED(x) (((x)&0x3F)<<1)
/* Bit definitions and macros for FEC_MIBC */
#define FEC_MIBC_MIB_DISABLE (0x80000000)
#define FEC_MIBC_MIB_IDLE (0x40000000)
/* Bit definitions and macros for FEC_RCR */
#define FEC_RCR_GRS (0x80000000)
#define FEC_RCR_NO_LGTH_CHECK (0x40000000)
#define FEC_RCR_MAX_FL(x) (((x)&0x7FF)<<16)
#define FEC_RCR_CNTL_FRM_ENA (0x00008000)
#define FEC_RCR_CRC_FWD (0x00004000)
#define FEC_RCR_PAUSE_FWD (0x00002000)
#define FEC_RCR_PAD_EN (0x00001000)
#define FEC_RCR_RMII_ECHO (0x00000800)
#define FEC_RCR_RMII_LOOP (0x00000400)
#define FEC_RCR_RMII_10T (0x00000200)
#define FEC_RCR_RMII_MODE (0x00000100)
#define FEC_RCR_SGMII_ENA (0x00000080)
#define FEC_RCR_RGMII_ENA (0x00000040)
#define FEC_RCR_FCE (0x00000020)
#define FEC_RCR_BC_REJ (0x00000010)
#define FEC_RCR_PROM (0x00000008)
#define FEC_RCR_MII_MODE (0x00000004)
#define FEC_RCR_DRT (0x00000002)
#define FEC_RCR_LOOP (0x00000001)
/* Bit definitions and macros for FEC_TCR */
#define FEC_TCR_RFC_PAUSE (0x00000010)
#define FEC_TCR_TFC_PAUSE (0x00000008)
#define FEC_TCR_FDEN (0x00000004)
#define FEC_TCR_HBC (0x00000002)
#define FEC_TCR_GTS (0x00000001)
/* Bit definitions and macros for FEC_PAUR */
#define FEC_PAUR_PADDR2(x) (((x)&0xFFFF)<<16)
#define FEC_PAUR_TYPE(x) ((x)&0xFFFF)
/* Bit definitions and macros for FEC_OPD */
#define FEC_OPD_PAUSE_DUR(x) (((x)&0x0000FFFF)<<0)
#define FEC_OPD_OPCODE(x) (((x)&0x0000FFFF)<<16)
/* Bit definitions and macros for FEC_TFWR */
#define FEC_TFWR_STR_FWD BIT (8) /* Present on ENET but not on FEC */
#define FEC_TFWR_X_WMRK(x) ((x)&0x03)
#define FEC_TFWR_X_WMRK_64 (0x01)
#define FEC_TFWR_X_WMRK_128 (0x02)
#define FEC_TFWR_X_WMRK_192 (0x03)
/* Bit definitions and macros for FEC_FRBR */
#define FEC_FRBR_R_BOUND(x) (((x)&0xFF)<<2)
/* Bit definitions and macros for FEC_FRSR */
#define FEC_FRSR_R_FSTART(x) (((x)&0xFF)<<2)
/* Bit definitions and macros for FEC_ERDSR */
#define FEC_ERDSR_R_DES_START(x) (((x)&0x3FFFFFFF)<<2)
/* Bit definitions and macros for FEC_ETDSR */
#define FEC_ETDSR_X_DES_START(x) (((x)&0x3FFFFFFF)<<2)
/* Bit definitions and macros for FEC_EMRBR */
#define FEC_EMRBR_R_BUF_SIZE(x) (((x)&0x7F)<<4)
//----------------------------------------------------------------------------//
/* the defines of MII operation */
#define FEC_MII_ST 0x40000000
#define FEC_MII_OP_OFF 28
#define FEC_MII_OP_MASK 0x03
#define FEC_MII_OP_RD 0x02
#define FEC_MII_OP_WR 0x01
#define FEC_MII_PA_OFF 23
#define FEC_MII_PA_MASK 0xFF
#define FEC_MII_RA_OFF 18
#define FEC_MII_RA_MASK 0xFF
#define FEC_MII_TA 0x00020000
#define FEC_MII_DATA_OFF 0
#define FEC_MII_DATA_MASK 0x0000FFFF
#define FEC_MII_FRAME (FEC_MII_ST | FEC_MII_TA)
#define FEC_MII_OP(x) (((x) & FEC_MII_OP_MASK) << FEC_MII_OP_OFF)
#define FEC_MII_PA(pa) (((pa) & FEC_MII_PA_MASK) << FEC_MII_PA_OFF)
#define FEC_MII_RA(ra) (((ra) & FEC_MII_RA_MASK) << FEC_MII_RA_OFF)
#define FEC_MII_SET_DATA(v) (((v) & FEC_MII_DATA_MASK) << FEC_MII_DATA_OFF)
#define FEC_MII_GET_DATA(v) (((v) >> FEC_MII_DATA_OFF) & FEC_MII_DATA_MASK)
#define FEC_MII_READ(pa, ra) ((FEC_MII_FRAME | FEC_MII_OP(FEC_MII_OP_RD)) |\
FEC_MII_PA(pa) | FEC_MII_RA(ra))
#define FEC_MII_WRITE(pa, ra, v) (FEC_MII_FRAME | FEC_MII_OP(FEC_MII_OP_WR)|\
FEC_MII_PA(pa) | FEC_MII_RA(ra) | FEC_MII_SET_DATA(v))
#define FEC_MII_TIMEOUT 10 // 1 * 10 ms
#define FEC_MII_TICK 1 // 1 ms
//----------------------------------------------------------------------------//
/* Ethernet Transmit and Receive Buffers */
#define PKT_MAXBUF_SIZE 1518
//----------------------------------------------------------------------------//
typedef enum fec_phy_inteface
{
/**
* MII, Media Independent Interface
* 4-bit operating at 25 Mhz
*/
FEC_PHY_MII,
/**
* RMII, Reduced Media Independent Interface
* 2-bit operating at 50 Mhz
*/
FEC_PHY_RMII,
FEC_PHY_RGMII
} fec_phy_inteface_t;
typedef struct fec_cfg
{
uint32_t base;
uint32_t clock;
fec_buffer_bd_t * tx_bd_base;
fec_buffer_bd_t * rx_bd_base;
fec_phy_inteface_t phy_interface;
} fec_cfg_t;
typedef struct fec
{
uint32_t clock;
fec_buffer_bd_t * tx_bd_base;
fec_buffer_bd_t * rx_bd_base;
volatile reg_fec_t *base;
phy_t * phy;
fec_phy_inteface_t phy_interface;
} fec_t;
/* Buffer descriptors. */
static fec_buffer_bd_t fec_tx_bd[NUM_BUFFERS] FEC_ALLIGNED;
static fec_buffer_bd_t fec_rx_bd[NUM_BUFFERS] FEC_ALLIGNED;
/* Data buffers. */
static uint8_t fec_tx_data[NUM_BUFFERS * TX_BUFFER_SIZE] FEC_ALLIGNED;
static uint8_t fec_rx_data[NUM_BUFFERS * RX_BUFFER_SIZE] FEC_ALLIGNED;
static fec_t *fec;
//----------------------------------------------------------------------------//
#ifdef RTK_DEBUG
const char * fec_ecat_link_duplex_name (uint8_t link_state)
{
if (link_state & PHY_LINK_OK)
{
if (link_state & PHY_LINK_FULL_DUPLEX)
{
return "full duplex";
}
else
{
return "half duplex";
}
}
else
{
return "no duplex (link down)";
}
}
const char * fec_ecat_link_speed_name (uint8_t link_state)
{
if (link_state & PHY_LINK_OK)
{
if (link_state & PHY_LINK_10MBIT)
{
return "10 Mbps";
}
else if (link_state & PHY_LINK_100MBIT)
{
return "100 Mbps";
}
else if (link_state & PHY_LINK_1000MBIT)
{
return "1 Gbps";
}
else
{
return "unknown speed";
}
}
else
{
return "0 Mbps (link down)";
}
}
#endif /* RTK_DEBUG */
#ifdef RTK_DEBUG_DATA
#include <ctype.h>
static void fec_ecat_dump_packet (const uint8_t * payload, size_t len)
{
size_t i, j, n;
char s[80];
ASSERT (payload != NULL);
for (i = 0; i < len; i += 16)
{
n = 0;
for (j = 0; j < 16 && (i + j) < len; j++)
{
ASSERT (n <= sizeof(s));
n += rsnprintf (s + n, sizeof(s) - n, "%02x ", payload[i + j]);
}
for (; j < 16; j++)
{
ASSERT (n <= sizeof(s));
n += rsnprintf (s + n, sizeof(s) - n, " ");
}
ASSERT (n <= sizeof(s));
n += rsnprintf (s + n, sizeof(s) - n, "|");
for (j = 0; j < 16 && (i + j) < len; j++)
{
uint8_t c = payload[i + j];
c = (isprint (c)) ? c : '.';
ASSERT (n <= sizeof(s));
n += rsnprintf (s + n, sizeof(s) - n, "%c", c);
}
ASSERT (n <= sizeof(s));
n += rsnprintf (s + n, sizeof(s) - n, "|\n");
ASSERT (n <= sizeof(s));
DPRINT ("%s", s);
}
}
#endif /* DEBUG_DATA */
static uint16_t fec_ecat_read_phy (void * arg, uint8_t address, uint8_t reg)
{
fec->base->eir = FEC_EIR_MII; // Clear interrupt.
fec->base->mmfr = FEC_MII_READ(address, reg); // Write read command.
while (!(fec->base->eir & FEC_EIR_MII))
{
; // Wait for interrupt.
}
fec->base->eir = FEC_EIR_MII; // Clear interrupt.
uint16_t data = FEC_MII_GET_DATA(fec->base->mmfr);
return data; // Return read data.
}
static void fec_ecat_write_phy (void * arg, uint8_t address, uint8_t reg,
uint16_t value)
{
fec->base->eir = FEC_EIR_MII; // Clear interrupt.
fec->base->mmfr = FEC_MII_WRITE(address, reg, value); // Write data.
while (!(fec->base->eir & FEC_EIR_MII))
{
; // Wait for interrupt.
}
fec->base->eir = FEC_EIR_MII; // Clear interrupt.
}
static void fec_ecat_init_hw (const fec_mac_address_t * mac_address)
{
uint32_t mii_speed;
/* Hard reset */
fec->base->ecr = FEC_ECR_RESET;
while (fec->base->ecr & FEC_ECR_RESET)
{
;
}
// Configure MDC clock.
mii_speed = (fec->clock + 499999) / 5000000;
fec->base->mscr = (fec->base->mscr & (~0x7E)) | (mii_speed << 1);
// Receive control register
fec->base->rcr = FEC_RCR_MAX_FL(PKT_MAXBUF_SIZE) | FEC_RCR_MII_MODE;
// set RMII mode in RCR register.
if (fec->phy_interface == FEC_PHY_RMII)
{
fec->base->rcr |= FEC_RCR_RMII_MODE;
}
else if(fec->phy_interface == FEC_PHY_RGMII)
{
fec->base->rcr &= ~(FEC_RCR_RMII_MODE | FEC_RCR_MII_MODE );
fec->base->rcr |= (FEC_RCR_RGMII_ENA | FEC_RCR_MII_MODE);
}
// Reset phy
if (fec->phy->ops->reset)
{
fec->phy->ops->reset (fec->phy);
}
/* Don't receive any unicast frames except those whose destination address
* equals our physical address.
*/
fec->base->iaur = 0;
fec->base->ialr = 0;
/* Receive all multicast frames. */
fec->base->gaur = UINT32_MAX;
fec->base->galr = UINT32_MAX;
/* Set our physical address. */
fec->base->pa.lower = (mac_address->octet[0] << 24) +
(mac_address->octet[1] << 16) +
(mac_address->octet[2] << 8) +
(mac_address->octet[3] << 0);
fec->base->pa.upper = (mac_address->octet[4] << 24) +
(mac_address->octet[5] << 16) +
0x8808;
/* Start link autonegotiation */
fec->phy->ops->start (fec->phy);
}
int fec_ecat_send (const void *payload, size_t tot_len)
{
fec_buffer_bd_t * bd;
/* Frames larger than the maximum Ethernet frame size are not allowed. */
ASSERT (tot_len <= PKT_MAXBUF_SIZE);
ASSERT (tot_len <= TX_BUFFER_SIZE);
/* Bus errors should never occur, unless the MPU is enabled and forbids
* the Ethernet MAC DMA from accessing the descriptors or buffers.
*/
ASSERT ((fec->base->eir & FEC_EIR_EBERR) == false);
/* Allocate a transmit buffer. We wait here until the MAC has released at
* least one buffer. This could take a couple of microseconds.
*/
bd = NULL;
while (bd == NULL)
{
bd = fec_buffer_get_tx ();
}
DPRINT ("out (%u):\n", tot_len);
DUMP_PACKET (payload, tot_len);
/* Copy frame to allocated buffer */
memcpy (bd->data, payload, tot_len);
bd->length = tot_len;
fec_buffer_produce_tx (bd);
/* Wait for previous transmissions to complete.
*
* This is a workaround for Freescale Kinetis errata e6358.
* See "Mask Set Errata for Mask 3N96B".
*/
while (fec->base->tdar)
{
;
}
/* Transmit frame */
fec->base->tdar = 1;
return tot_len;
}
int fec_ecat_recv (void * buffer, size_t buffer_length)
{
fec_buffer_bd_t * bd;
int return_value;
size_t frame_length_without_fcs;
/* Bus errors should never occur, unless the MPU is enabled and forbids
* the Ethernet MAC DMA from accessing the descriptors or buffers.
*/
ASSERT ((fec->base->eir & FEC_EIR_EBERR) == false);
bd = fec_buffer_get_rx ();
if (bd == NULL)
{
/* No frame received. Not an error. */
return_value = 0;
return return_value;
}
/* The FCS CRC should not be returned to user, so subtract that. */
frame_length_without_fcs = bd->length - ETHERNET_FCS_SIZE_IN_BYTES;
/* A frame was received. Handle it and then return its buffer to hardware */
if ((bd->status & BD_RX_L)== 0){
/* Buffer is not last buffer in frame. This really should never
* happen as our buffers are large enough to contain any
* Ethernet frame. Nevertheless, is has been observed to happen.
* Drop the packet and hope for the best.
*/
DPRINT ("recv(): End of frame not found. Status: 0x%x\n", bd->status);
return_value = -1;
}
else if (bd->status & (BD_RX_LG | BD_RX_NO | BD_RX_CR | BD_RX_OV))
{
DPRINT ("recv(): Frame is damaged. Status: 0x%x\n", bd->status);
return_value = -1;
}
else if (buffer_length >= frame_length_without_fcs)
{
/* No errors detected, so frame should be valid. */
ASSERT (frame_length_without_fcs > 0);
memcpy (buffer, bd->data, frame_length_without_fcs);
DPRINT ("in (%u):\n", frame_length_without_fcs);
DUMP_PACKET (buffer, frame_length_without_fcs);
return_value = frame_length_without_fcs;
}
else
{
DPRINT ("received_frame: User buffer is too small.\n");
return_value = -1;
}
/* Tell the HW that there are new free RX buffers. */
fec_buffer_produce_rx (bd);
fec->base->rdar = 1;
return return_value;
}
static void fec_ecat_hotplug (void)
{
uint8_t link_state = 0;
/* Disable frame reception/transmission */
fec->base->ecr &= ~FEC_ECR_ETHER_EN;
/* Set duplex mode according to link state */
link_state = fec->phy->ops->get_link_state (fec->phy);
/* Set duplex */
if (link_state & PHY_LINK_FULL_DUPLEX)
{
fec->base->tcr |= FEC_TCR_FDEN;
fec->base->rcr &= ~FEC_RCR_DRT;
}
else
{
fec->base->tcr &= ~FEC_TCR_FDEN;
fec->base->rcr |= FEC_RCR_DRT;
}
/* Set RMII 10-Mbps mode. */
if (link_state & PHY_LINK_10MBIT)
{
fec->base->rcr |= FEC_RCR_RMII_10T;
}
else
{
fec->base->rcr &= ~FEC_RCR_RMII_10T;
}
/* Clear any pending interrupt */
fec->base->eir = 0xffffffff;
// Disable all interrupts.
fec->base->eimr = 0x0;
/* Configure the amount of data required in the
* transmit FIFO before transmission of a frame can begin.
*/
/* All data in frame (i.e. store-and-forward mode). */
fec->base->tfwr = FEC_TFWR_STR_FWD;
// Reset buffers.
fec_buffer_reset ();
// FEC_ERDSR - Receive buffer descriptor ring start register
fec->base->erdsr = (uint32_t)fec->rx_bd_base;
// FEC_ETDSR - Transmit buffer descriptor ring start register
fec->base->etdsr = (uint32_t)fec->tx_bd_base;
// FEC_EMRBR - Maximum receive buffer size register
fec->base->emrbr = RX_BUFFER_SIZE - 1;
/* Let controller count frames. Useful for debugging */
fec->base->mibc = 0x0;
/* Now enable the transmit and receive processing.
*
* Also let MAC hardware convert DMA descriptor fields between little endian
* (as accessed by ARM core) and big endian (as used internally by the
* MAC hardware. Software would otherwise need to do the conversions.
*/
fec->base->ecr = FEC_ECR_ETHER_EN | FEC_ECR_DBSWP;
/* Indicate that there have been empty receive buffers produced */
// FEC_RDAR - Receive Descriptor ring - Receive descriptor active register
fec->base->rdar = 1;
DPRINT ("Link up. Speed: %s. Mode: %s.\n", fec_ecat_link_speed_name (link_state),
fec_ecat_link_duplex_name (link_state));
}
static dev_state_t fec_ecat_probe (void)
{
uint8_t link_state;
link_state = fec->phy->ops->get_link_state (fec->phy);
return (link_state & PHY_LINK_OK) ? StateAttached : StateDetached;
}
COMPILETIME_ASSERT (FEC_MODULE_CLOCK_Hz >= FEC_MIN_MODULE_CLOCK_Hz);
int fec_ecat_init (const fec_mac_address_t * mac_address,
bool phy_loopback_mode)
{
dev_state_t state;
static const fec_cfg_t eth_cfg =
{
.base = ENET_BASE,
.clock = FEC_MODULE_CLOCK_Hz,
.tx_bd_base = fec_tx_bd,
.rx_bd_base = fec_rx_bd,
.phy_interface = FEC_PHY_RMII,
};
static phy_cfg_t phy_cfg =
{
.address = ETH_PHY_ADDRESS,
.read = NULL, /* Set by MAC driver */
.write = NULL, /* Set by MAC driver */
};
phy_cfg.loopback_mode = phy_loopback_mode;
/* Initialise buffers and buffer descriptors.*/
fec_buffer_init_tx (fec_tx_bd, fec_tx_data, NUM_BUFFERS);
fec_buffer_init_rx (fec_rx_bd, fec_rx_data, NUM_BUFFERS);
fec = malloc (sizeof (fec_t));
UASSERT (fec != NULL, EMEM);
/* Initialise driver state */
fec->rx_bd_base = eth_cfg.rx_bd_base;
fec->tx_bd_base = eth_cfg.tx_bd_base;
fec->clock = eth_cfg.clock;
fec->base = (reg_fec_t *)eth_cfg.base;
fec->phy_interface = eth_cfg.phy_interface;
fec->phy = mii_init (&phy_cfg);
fec->phy->arg = fec;
fec->phy->read = fec_ecat_read_phy;
fec->phy->write = fec_ecat_write_phy;
/* Initialize hardware */
fec_ecat_init_hw (mac_address);
state = StateDetached;
while (state == StateDetached)
{
state = fec_ecat_probe ();
}
fec_ecat_hotplug ();
return 0;
}
+56
View File
@@ -0,0 +1,56 @@
/******************************************************************************
* * *** ***
* *** *** ***
* *** **** ********** *** ***** *** **** *****
* ********* ********** *** ********* ************ *********
* **** *** *** *** *** **** ***
* *** *** ****** *** *********** *** **** *****
* *** *** ****** *** ************* *** **** *****
* *** **** **** *** *** *** **** ***
* *** ******* ***** ************** ************* *********
* *** ***** *** ******* ** ** ****** *****
* t h e r e a l t i m e t a r g e t e x p e r t s
*
* http://www.rt-labs.com
* Copyright (C) 2007. rt-labs AB, Sweden. All rights reserved.
*------------------------------------------------------------------------------
* $Id: fec_ecat.h 91 2014-04-02 13:32:29Z rtlfrm $
*------------------------------------------------------------------------------
*/
/**
* \defgroup fec EtherCat Ethernet MAC driver for Frescale K60 SoCs.
*
* \{
*/
#ifndef FEC_H
#define FEC_H
#include <types.h>
#ifdef __cplusplus
extern "C"
{
#endif
typedef struct fec_mac_address
{
uint8_t octet[6];
} fec_mac_address_t;
int fec_ecat_init (const fec_mac_address_t * mac_address, bool phy_loopback_mode);
int fec_ecat_send (const void *payload, size_t tot_len);
int fec_ecat_recv (void * buffer, size_t buffer_length);
#ifdef __cplusplus
}
#endif
#endif /* FEC_H */
/**
* \}
*/
+423
View File
@@ -0,0 +1,423 @@
/******************************************************************************
* * *** ***
* *** *** ***
* *** **** ********** *** ***** *** **** *****
* ********* ********** *** ********* ************ *********
* **** *** *** *** *** **** ***
* *** *** ****** *** *********** *** **** *****
* *** *** ****** *** ************* *** **** *****
* *** **** **** *** *** *** **** ***
* *** ******* ***** ************** ************* *********
* *** ***** *** ******* ** ** ****** *****
* t h e r e a l t i m e t a r g e t e x p e r t s
*
* http://www.rt-labs.com
* Copyright (C) 2006. rt-labs AB, Sweden. All rights reserved.
*------------------------------------------------------------------------------
* $Id: lw_emac.c 348 2012-10-18 16:41:14Z rtlfrm $
*
*
*------------------------------------------------------------------------------
*/
#include <bsp.h>
#include <kern.h>
#include <config.h>
#include <bfin_dma.h>
#include <string.h>
#include "lw_emac.h"
/* MII standard registers.
See IEEE Std 802.3-2005 clause 22:
"Reconciliation Sublayer (RS) and Media Independent Interface (MII)",
section 2.4.1 to 3.
http://standards.ieee.org/getieee802/download/802.3-2005_section2.pdf */
#define MII_BMCR 0x00 /* Basic Mode Control Register */
#define MII_BMSR 0x01 /* Basic Mode Status Register */
#define MII_PHYIDR1 0x02 /* PHY Identifier Register 1 */
#define MII_PHYIDR2 0x03 /* PHY Identifier Register 2 */
#define MII_LPAR 0x05 /* Auto-Negotiation Link Partner Ability Register */
/* MII basic mode control register */
#define MII_BMCR_RST BIT (15)
#define MII_BMCR_ANEG_EN BIT (12)
#define MII_BMCR_ANEG_RST BIT (9)
/* MII basic mode status register */
#define MII_BMSR_ANEGACK BIT (5)
#define MII_BMSR_LINK BIT (2)
/* MII auto-negotiation advertisement register */
#define MII_ANAR_100_FD BIT (8) /* Can do 100BASE-TX full duplex */
#define MII_ANAR_10_FD BIT (6) /* Can do 10BASE-T full duplex */
/* Management Data Clock (MDC) frequency.
* 2.5Mz corresponds to the minimum period for MDC as defined by the standard.
* This is independent of whether the PHY is in 10Mbit/s or 100Mbit/s mode.
* See Std. IEEE 802.3 (Ethernet) Clause 22.2.2.11 ("management data clock"). */
#define PHY_MDC_Hz 2500000
#define PHY_RETRIES 3000
/* TODO: This might need changing for different boards. Fix? */
#define PHY_ADDR 0
#define ETH_RX_BUF_SIZE 2
#define ETH_TX_BUF_SIZE 1
#define ETH_FRAME_SIZE 0x614
/* Ethernet MAC register structure */
typedef struct bfin_emac_regs
{
/* Control-Status Register Group */
uint32_t opmode; /* operating mode */
uint32_t addrlo; /* address low */
uint32_t addrhi; /* address high */
uint32_t hashlo; /* multicast hash table low */
uint32_t hashhi; /* multicast hash table high */
uint32_t staadd; /* station management address */
uint32_t stadat; /* station management data */
uint32_t flc; /* flow control */
uint32_t vlan1; /* VLAN1 tag */
uint32_t vlan2; /* VLAN2 tag */
uint32_t _reserved1;
uint32_t wkup_ctl; /* wakeup frame control and status */
uint32_t wkup_ffmsk[4]; /* wakeup frame n byte mask (n == 0,..,3) */
uint32_t wkup_ffcmd; /* wakeup frame filter commands */
uint32_t wkup_ffoff; /* wakeup frame filter offsets */
uint32_t wkup_ffcrc0; /* wakeup frame filter CRC0/1 */
uint32_t wkup_ffcrc1; /* wakeup frame filter CRC2/3 */
uint32_t _reserved2[4];
/* System Interface Register Group */
uint32_t sysctl; /* system control */
uint32_t systat; /* system status */
/* Ethernet Frame Status Register Group */
uint32_t rx_stat; /* RX Current Frame Status */
uint32_t rx_stky; /* RX Sticky Frame Status */
uint32_t rx_irqe; /* RX Frame Status Interrupt Enable */
uint32_t tx_stat; /* TX Current Frame Status Register */
uint32_t tx_stky; /* TT Sticky Frame Status */
uint32_t tx_irqe; /* TX Frame Status Interrupt Enable */
/* MAC Management Counter Register Group */
uint32_t mmc_ctl; /* Management Counters Control */
uint32_t mmc_rirqs; /* MMC RX Interrupt Status */
uint32_t mmc_rirqe; /* MMC RX Interrupt Enable */
uint32_t mmc_tirqs; /* MMC TX Interrupt Status */
uint32_t mmc_tirqe; /* MMC TX Interrupt Enable */
uint32_t _reserved3[27];
uint32_t mmc_rxc_ok; /* FramesReceivedOK */
uint32_t mmc_rxc_fcs; /* FrameCheckSequenceErrors */
uint32_t mmc_rxc_align; /* AlignmentErrors */
uint32_t mmc_rxc_octet; /* OctetsReceivedOK */
uint32_t mmc_rxc_dmaovf; /* FramesLostDueToIntMACRcvError */
uint32_t mmc_rxc_unicst; /* UnicastFramesReceivedOK */
uint32_t mmc_rxc_multi; /* MulticastFramesReceivedOK */
uint32_t mmc_rxc_broad; /* BroadcastFramesReceivedOK */
uint32_t mmc_rxc_lnerri; /* InRangeLengthErrors */
uint32_t mmc_rxc_lnerro; /* OutOfRangeLengthField */
uint32_t mmc_rxc_long; /* FrameTooLongErrors */
uint32_t mmc_rxc_macctl; /* MACControlFramesReceived */
uint32_t mmc_rxc_opcode; /* UnsupportedOpcodesReceived */
uint32_t mmc_rxc_pause; /* PAUSEMACCtrlFramesReceived */
uint32_t mmc_rxc_allfrm; /* FramesReceivedAll */
uint32_t mmc_rxc_alloct; /* OctetsReceivedAll */
uint32_t mmc_rxc_typed; /* TypedFramesReceived */
uint32_t mmc_rxc_short; /* FramesLenLt64Received */
uint32_t mmc_rxc_eq64; /* FramesLenEq64Received */
uint32_t mmc_rxc_lt128; /* FramesLen65_127Received */
uint32_t mmc_rxc_lt256; /* FramesLen128_255Received */
uint32_t mmc_rxc_lt512; /* FramesLen256_511Received */
uint32_t mmc_rxc_lt1024; /* FramesLen512_1023Received */
uint32_t mmc_rxc_ge1024; /* FramesLen1024_MaxReceived */
/* TODO: add all registers */
} bfin_emac_regs_t;
COMPILETIME_ASSERT (offsetof (bfin_emac_regs_t, opmode) == 0x0);
COMPILETIME_ASSERT (offsetof (bfin_emac_regs_t, staadd) == 0x14);
COMPILETIME_ASSERT (offsetof (bfin_emac_regs_t, wkup_ctl) == 0x2c);
COMPILETIME_ASSERT (offsetof (bfin_emac_regs_t, wkup_ffcrc1) == 0x4c);
COMPILETIME_ASSERT (offsetof (bfin_emac_regs_t, sysctl) == 0x60);
COMPILETIME_ASSERT (offsetof (bfin_emac_regs_t, rx_stat) == 0x68);
COMPILETIME_ASSERT (offsetof (bfin_emac_regs_t, mmc_ctl) == 0x80);
COMPILETIME_ASSERT (offsetof (bfin_emac_regs_t, mmc_tirqe) == 0x90);
COMPILETIME_ASSERT (offsetof (bfin_emac_regs_t, mmc_rxc_ok) == 0x100);
COMPILETIME_ASSERT (offsetof (bfin_emac_regs_t, mmc_rxc_dmaovf) == 0x110);
COMPILETIME_ASSERT (offsetof (bfin_emac_regs_t, mmc_rxc_ge1024) == 0x15c);
/* 32-bit aligned struct for ethernet data */
typedef struct ethernet_data {
uint16_t length; /* When TX = frame data length in bytes, not including 'length'
* When RX = 0x0000 padding when using RXDWA
*/
uint8_t data[ETH_FRAME_SIZE]; /* Hold the ethernet frame */
uint16_t padding; /* To make the struct 32-bit aligned */
} ethernet_data_t;
COMPILETIME_ASSERT (sizeof (ethernet_data_t) == 0x618) ;
static volatile bfin_emac_regs_t * pEth = (bfin_emac_regs_t *) EMAC_OPMODE;
/* Buffers for rx and tx DMA operations */
static ethernet_data_t rxBuffer[ETH_RX_BUF_SIZE] __attribute__((section(".dma")));
static ethernet_data_t txBuffer[ETH_TX_BUF_SIZE] __attribute__((section(".dma")));
static volatile uint32_t rxStatusWord[ETH_RX_BUF_SIZE] __attribute__((section(".dma")));
static volatile uint32_t txStatusWord[ETH_TX_BUF_SIZE] __attribute__((section(".dma")));
static bfin_dma_descriptor_t rxDMADesc[ETH_RX_BUF_SIZE * 2] __attribute__((section(".dma")));
static bfin_dma_descriptor_t txDMADesc[ETH_TX_BUF_SIZE * 2] __attribute__((section(".dma")));
/* Hold the current index of rx and tx buffers */
static uint8_t rxIdx, txIdx;
/* Internal function that writes to a PHY register */
static void lw_emac_write_phy_reg(uint8_t phy_addr, uint8_t reg_addr, uint32_t data) {
/* Set the flags that should be set reg_addr on PHY */
pEth->stadat = data;
/* Wait for our turn then initiate writing of stadat to PHY register */
pEth->staadd = SET_PHYAD(phy_addr) | SET_REGAD(reg_addr) | STAOP | STABUSY;
while (pEth->staadd & STABUSY) ;
}
/* Internal function that reads from; and returns a PHY register */
static uint32_t lw_emac_read_phy_reg(uint8_t phy_addr, uint8_t reg_addr) {
pEth->staadd = SET_PHYAD(phy_addr) | SET_REGAD(reg_addr) | STABUSY;
while (pEth->staadd & STABUSY) ;
return pEth->stadat;
}
/* Internal function that sets the MAC address */
static void lw_emac_set_mac_addr(uint8_t * ethAddr)
{
pEth->addrlo =
ethAddr[0] |
ethAddr[1] << 8 |
ethAddr[2] << 16 |
ethAddr[3] << 24;
pEth->addrhi =
ethAddr[4] |
ethAddr[5] << 8;
}
static uint8_t lw_emac_init_registers(uint8_t * ethAddr) {
uint32_t clock_divisor, sysctl_mdcdiv, phy_stadat, counter;
/* CONFIGURE MAC MII PINS */
/* Enable PHY Clock Output */
*(volatile uint16_t *) VR_CTL |= PHYCLKOE;
/* Set all bits to 1 to use MII mode */
*(uint16_t *) PORTH_FER = 0xFFFF;
/* CONFIGURE MAC REGISTERS */
/*
* Set the MMC (MAC Management Counter) control register
* RTSC = Clear all counters
*
* Note that counters are not enabled at this time
*/
pEth->mmc_ctl = RSTC;
/* Set MAC address */
lw_emac_set_mac_addr (ethAddr);
clock_divisor = SCLK / PHY_MDC_Hz;
sysctl_mdcdiv = clock_divisor / 2 - 1;
ASSERT (sysctl_mdcdiv <= 0x3f);
rprintp ("PHY ID: %04x %04x\n",
lw_emac_read_phy_reg (PHY_ADDR, MII_PHYIDR1),
lw_emac_read_phy_reg (PHY_ADDR, MII_PHYIDR2)
);
/*
* Set the system control register
* SET_MDCDIV(x) = Set MDC to 2.5 MHz
* RXDWA = Pad incoming frame with 0x0000 as to make the data-part 32-bit aligned
* RXCKS = Enable Receive Frame TCP/UDP Checksum Computation
*/
pEth->sysctl = SET_MDCDIV(sysctl_mdcdiv) | RXDWA ;
/* CONFIGURE PHY */
/*
* Set the PHY basic control register
* MII_BMCR_ANEG_EN = Auto negotiation on
* MII_BMCR_ANEG_RST = Restart the auto-neg process by setting
* Speed handled by auto negotiation
*/
phy_stadat = MII_BMCR_ANEG_EN | MII_BMCR_ANEG_RST;
lw_emac_write_phy_reg(PHY_ADDR, MII_BMCR, phy_stadat);
/* Loop until link is up or time out after PHY_RETRIES */
counter = 0;
do {
if (counter > PHY_RETRIES) {
rprintp("Ethernet link is down\n");
return -1;
}
task_delay (tick_from_ms (10));
phy_stadat = lw_emac_read_phy_reg(PHY_ADDR, MII_BMSR);
++counter;
} while (!(phy_stadat & MII_BMSR_LINK)) ;
/* Check whether link partner can do full duplex or not */
phy_stadat = lw_emac_read_phy_reg(PHY_ADDR, MII_LPAR);
if (phy_stadat & (MII_ANAR_100_FD | MII_ANAR_10_FD) ) {
pEth->opmode = FDMODE;
}
else {
pEth->opmode = 0;
}
/*
* Setup DMA MAC receive/transfer channels with XCOUNT 0 (which we use
* together with description based DMA) and XMODIFY 4 (bytes per transfer)
*/
bfin_dma_channel_init(DMA_CHANNEL_EMAC_RX, 0, 4);
bfin_dma_channel_init(DMA_CHANNEL_EMAC_TX, 0, 4);
return 0;
}
int bfin_EMAC_init (uint8_t *ethAddr)
{
rxIdx = txIdx = 0;
if (lw_emac_init_registers(ethAddr) != 0) {
return -1;
}
// Reset status words
memset ((uint8_t *)rxStatusWord, 0, sizeof(rxStatusWord));
memset ((uint8_t *)txStatusWord, 0, sizeof(txStatusWord));
txDMADesc[0].next = &txDMADesc[1];
txDMADesc[0].start_addr = &txBuffer[0];
txDMADesc[0].config = DMA_CONFIG_DMA_EN |
DMA_CONFIG_WDSIZE(DMA_WDSIZE_32BIT) |
DMA_CONFIG_NDSIZE(5) |
DMA_CONFIG_FLOW(DMA_FLOW_DESCRIPTOR_LIST_LARGE);
txDMADesc[1].next = &txDMADesc[0];
txDMADesc[1].start_addr = &txStatusWord[0];
txDMADesc[1].config = DMA_CONFIG_DMA_EN |
DMA_CONFIG_WNR |
DMA_CONFIG_WDSIZE(DMA_WDSIZE_32BIT) |
DMA_CONFIG_NDSIZE(0) |
DMA_CONFIG_FLOW(DMA_FLOW_STOP);
rxDMADesc[0].next = &rxDMADesc[1];
rxDMADesc[0].start_addr = &rxBuffer[0];
rxDMADesc[0].config = DMA_CONFIG_DMA_EN |
DMA_CONFIG_WNR |
DMA_CONFIG_WDSIZE(DMA_WDSIZE_32BIT) |
DMA_CONFIG_NDSIZE(5) |
DMA_CONFIG_FLOW(DMA_FLOW_DESCRIPTOR_LIST_LARGE);
rxDMADesc[1].next = &rxDMADesc[2];
rxDMADesc[1].start_addr = &rxStatusWord[0];
rxDMADesc[1].config = DMA_CONFIG_DMA_EN |
DMA_CONFIG_WNR |
DMA_CONFIG_WDSIZE(DMA_WDSIZE_32BIT) |
DMA_CONFIG_NDSIZE(5) |
DMA_CONFIG_FLOW(DMA_FLOW_DESCRIPTOR_LIST_LARGE);
rxDMADesc[2].next = &rxDMADesc[3];
rxDMADesc[2].start_addr = &rxBuffer[1];
rxDMADesc[2].config = DMA_CONFIG_DMA_EN |
DMA_CONFIG_WNR |
DMA_CONFIG_WDSIZE(DMA_WDSIZE_32BIT) |
DMA_CONFIG_NDSIZE(5) |
DMA_CONFIG_FLOW(DMA_FLOW_DESCRIPTOR_LIST_LARGE);
rxDMADesc[3].next = &rxDMADesc[0];
rxDMADesc[3].start_addr = &rxStatusWord[1];
rxDMADesc[3].config = DMA_CONFIG_DMA_EN |
DMA_CONFIG_WNR |
DMA_CONFIG_WDSIZE(DMA_WDSIZE_32BIT) |
DMA_CONFIG_NDSIZE(5) |
DMA_CONFIG_FLOW(DMA_FLOW_DESCRIPTOR_LIST_LARGE);
bfin_dma_channel_enable(DMA_CHANNEL_EMAC_RX, &rxDMADesc[0]);
/* Enable Receiving, Automatic Pad Stripping and receiving of frames with length < 64 butes */
pEth->opmode |= ASTP | PSF | RE;
return 0;
}
int bfin_EMAC_send (void *packet, int length)
{
UASSERT(length > 0, EARG);
UASSERT(length < ETH_FRAME_SIZE, EARG);
/* TODO: Check DMA Error in IRQ status */
while (bfin_dma_channel_interrupt_is_active (DMA_CHANNEL_EMAC_TX));
txBuffer[txIdx].length = length;
memcpy(txBuffer[txIdx].data, packet, length);
bfin_dma_channel_enable(DMA_CHANNEL_EMAC_TX, txDMADesc);
pEth->opmode |= TE;
while ((txStatusWord[txIdx] & TX_COMP) == 0);
ASSERT(txStatusWord[txIdx] & TX_OK);
txStatusWord[txIdx] = 0;
++txIdx;
if (txIdx == ETH_TX_BUF_SIZE) {
txIdx = 0;
}
return 0;
}
int bfin_EMAC_recv (uint8_t * packet, size_t size)
{
uint32_t length;
uint32_t status = rxStatusWord[rxIdx];
/* Check if rx frame is completed */
if ((status & RX_COMP) == 0) {
return -1;
}
else if ((status & RX_OK) == 0) {
ASSERT(0);
/* TODO: Handle error */
return -1;
}
else if (status & RX_DMAO) {
ASSERT(0);
/* TODO: Handle overrun */
return -1;
}
length = status & RX_FRLEN;
if (size < length) {
length = size;
}
memcpy(packet, rxBuffer[rxIdx].data, length);
bfin_dma_channel_interrupt_clear (DMA_CHANNEL_EMAC_RX);
rxStatusWord[rxIdx] = 0;
++rxIdx;
if (rxIdx == ETH_RX_BUF_SIZE) {
rxIdx = 0;
}
return length;
}
+12
View File
@@ -0,0 +1,12 @@
/*
* author: Tomas Vestelind
*/
#ifndef LWIP_MAC_H
#define LWIP_MAC_H
int bfin_EMAC_init(uint8_t *enetaddr);
int bfin_EMAC_send(void *packet, int length);
int bfin_EMAC_recv(uint8_t * packet, size_t size);
#endif /* LWIP_MAC_H */
+628
View File
@@ -0,0 +1,628 @@
/*
* Licensed under the GNU General Public License version 2 with exceptions. See
* LICENSE file in the project root for full license information
*/
/** \file
* \brief
* EtherCAT RAW socket driver.
*
* Low level interface functions to send and receive EtherCAT packets.
* EtherCAT has the property that packets are only send by the master,
* and the send packets always return in the receive buffer.
* There can be multiple packets "on the wire" before they return.
* To combine the received packets with the original send packets a buffer
* system is installed. The identifier is put in the index item of the
* EtherCAT header. The index is stored and compared when a frame is received.
* If there is a match the packet can be combined with the transmit packet
* and returned to the higher level function.
*
* The socket layer can exhibit a reversal in the packet order (rare).
* If the Tx order is A-B-C the return order could be A-C-B. The indexed buffer
* will reorder the packets automatically.
*
* The "redundant" option will configure two sockets and two NIC interfaces.
* Slaves are connected to both interfaces, one on the IN port and one on the
* OUT port. Packets are send via both interfaces. Any one of the connections
* (also an interconnect) can be removed and the slaves are still serviced with
* packets. The software layer will detect the possible failure modes and
* compensate. If needed the packets from interface A are resent through interface B.
* This layer is fully transparent for the higher layers.
*/
#include <kern.h>
#include <ioctl.h>
#include <stdio.h>
#include <string.h>
#include "osal.h"
#include "oshw.h"
#include "lw_mac/lw_emac.h"
#ifndef MAX
#define MAX(a,b) (((a) > (b)) ? (a) : (b))
#define MIN(a,b) (((a) < (b)) ? (a) : (b))
#endif
/** Redundancy modes */
enum
{
/** No redundancy, single NIC mode */
ECT_RED_NONE,
/** Double redundant NIC connection */
ECT_RED_DOUBLE
};
/** Primary source MAC address used for EtherCAT.
* This address is not the MAC address used from the NIC.
* EtherCAT does not care about MAC addressing, but it is used here to
* differentiate the route the packet traverses through the EtherCAT
* segment. This is needed to find out the packet flow in redundant
* configurations. */
const uint16 priMAC[3] = { 0x0101, 0x0101, 0x0101 };
/** Secondary source MAC address used for EtherCAT. */
const uint16 secMAC[3] = { 0x0404, 0x0404, 0x0404 };
/** second MAC word is used for identification */
#define RX_PRIM priMAC[1]
/** second MAC word is used for identification */
#define RX_SEC secMAC[1]
static void ecx_clear_rxbufstat(int *rxbufstat)
{
int i;
for(i = 0; i < EC_MAXBUF; i++)
{
rxbufstat[i] = EC_BUF_EMPTY;
}
}
/** Basic setup to connect NIC to socket.
* @param[in] port = port context struct
* @param[in] ifname = Name of NIC device, f.e. "eth0"
* @param[in] secondary = if >0 then use secondary stack instead of primary
* @return >0 if succeeded
*/
int ecx_setupnic(ecx_portt *port, const char *ifname, int secondary)
{
int i;
int rVal;
int *psock;
port->getindex_mutex = mtx_create();
port->tx_mutex = mtx_create();
port->rx_mutex = mtx_create();
rVal = bfin_EMAC_init((uint8_t *)priMAC);
if (rVal != 0)
return 0;
if (secondary)
{
/* secondary port struct available? */
if (port->redport)
{
/* when using secondary socket it is automatically a redundant setup */
psock = &(port->redport->sockhandle);
*psock = -1;
port->redstate = ECT_RED_DOUBLE;
port->redport->stack.sock = &(port->redport->sockhandle);
port->redport->stack.txbuf = &(port->txbuf);
port->redport->stack.txbuflength = &(port->txbuflength);
port->redport->stack.tempbuf = &(port->redport->tempinbuf);
port->redport->stack.rxbuf = &(port->redport->rxbuf);
port->redport->stack.rxbufstat = &(port->redport->rxbufstat);
port->redport->stack.rxsa = &(port->redport->rxsa);
ecx_clear_rxbufstat(&(port->redport->rxbufstat[0]));
}
else
{
/* fail */
return 0;
}
}
else
{
port->getindex_mutex = mtx_create();
port->tx_mutex = mtx_create();
port->rx_mutex = mtx_create();
port->sockhandle = -1;
port->lastidx = 0;
port->redstate = ECT_RED_NONE;
port->stack.sock = &(port->sockhandle);
port->stack.txbuf = &(port->txbuf);
port->stack.txbuflength = &(port->txbuflength);
port->stack.tempbuf = &(port->tempinbuf);
port->stack.rxbuf = &(port->rxbuf);
port->stack.rxbufstat = &(port->rxbufstat);
port->stack.rxsa = &(port->rxsa);
ecx_clear_rxbufstat(&(port->rxbufstat[0]));
psock = &(port->sockhandle);
}
/* setup ethernet headers in tx buffers so we don't have to repeat it */
for (i = 0; i < EC_MAXBUF; i++)
{
ec_setupheader(&(port->txbuf[i]));
port->rxbufstat[i] = EC_BUF_EMPTY;
}
ec_setupheader(&(port->txbuf2));
return 1;
}
/** Close sockets used
* @param[in] port = port context struct
* @return 0
*/
int ecx_closenic(ecx_portt *port)
{
if (port->sockhandle >= 0)
{
close(port->sockhandle);
}
if ((port->redport) && (port->redport->sockhandle >= 0))
{
close(port->redport->sockhandle);
}
return 0;
}
/** Fill buffer with ethernet header structure.
* Destination MAC is always broadcast.
* Ethertype is always ETH_P_ECAT.
* @param[out] p = buffer
*/
void ec_setupheader(void *p)
{
ec_etherheadert *bp;
bp = 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.
*/
uint8 ecx_getindex(ecx_portt *port)
{
uint8 idx;
uint8 cnt;
mtx_lock (port->getindex_mutex);
idx = port->lastidx + 1;
/* index can't be larger than buffer array */
if (idx >= EC_MAXBUF)
{
idx = 0;
}
cnt = 0;
/* try to find unused index */
while ((port->rxbufstat[idx] != EC_BUF_EMPTY) && (cnt < EC_MAXBUF))
{
idx++;
cnt++;
if (idx >= EC_MAXBUF)
{
idx = 0;
}
}
port->rxbufstat[idx] = EC_BUF_ALLOC;
if (port->redstate != ECT_RED_NONE)
{
port->redport->rxbufstat[idx] = EC_BUF_ALLOC;
}
port->lastidx = idx;
mtx_unlock (port->getindex_mutex);
return idx;
}
/** Set rx buffer status.
* @param[in] port = port context struct
* @param[in] idx = index in buffer array
* @param[in] bufstat = status to set
*/
void ecx_setbufstat(ecx_portt *port, uint8 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, uint8 idx, int stacknumber)
{
int lp, rval;
ec_stackT *stack;
if (!stacknumber)
{
stack = &(port->stack);
}
else
{
stack = &(port->redport->stack);
}
lp = (*stack->txbuflength)[idx];
(*stack->rxbufstat)[idx] = EC_BUF_TX;
rval = bfin_EMAC_send((*stack->txbuf)[idx], lp);
return rval;
}
/** Transmit buffer over socket (non blocking).
* @param[in] port = port context struct
* @param[in] idx = index in tx buffer array
* @return socket send result
*/
int ecx_outframe_red(ecx_portt *port, uint8 idx)
{
ec_comt *datagramP;
ec_etherheadert *ehp;
int rval;
ehp = (ec_etherheadert *)&(port->txbuf[idx]);
/* rewrite MAC source address 1 to primary */
ehp->sa1 = oshw_htons(priMAC[1]);
/* transmit over primary socket*/
rval = ecx_outframe(port, idx, 0);
if (port->redstate != ECT_RED_NONE)
{
mtx_lock (port->tx_mutex);
ehp = (ec_etherheadert *)&(port->txbuf2);
/* use dummy frame for secondary socket transmit (BRD) */
datagramP = (ec_comt*)&(port->txbuf2[ETH_HEADERSIZE]);
/* write index to frame */
datagramP->index = idx;
/* rewrite MAC source address 1 to secondary */
ehp->sa1 = oshw_htons(secMAC[1]);
/* transmit over secondary socket */
//send(sockhandle2, &ec_txbuf2, ec_txbuflength2 , 0);
// OBS! redundant not ACTIVE for BFIN, just added to compile
ASSERT (0);
port->redport->rxbufstat[idx] = EC_BUF_TX;
bfin_EMAC_send(&(port->txbuf2), port->txbuflength2);
mtx_unlock (port->tx_mutex);
}
return rval;
}
/** Non blocking read of socket. Put frame in temporary buffer.
* @param[in] port = port context struct
* @param[in] stacknumber = 0=primary 1=secondary stack
* @return >0 if frame is available and read
*/
static int ecx_recvpkt(ecx_portt *port, int stacknumber)
{
int lp, bytesrx;
ec_stackT *stack;
if (!stacknumber)
{
stack = &(port->stack);
}
else
{
stack = &(port->redport->stack);
}
lp = sizeof(port->tempinbuf);
bytesrx = bfin_EMAC_recv((*stack->tempbuf), lp);
port->tempinbufs = bytesrx;
return (bytesrx > 0);
}
/** Non blocking receive frame function. Uses RX buffer and index to combine
* read frame with transmitted frame. To compensate for received frames that
* are out-of-order all frames are stored in their respective indexed buffer.
* If a frame was placed in the buffer previously, the function retrieves it
* from that buffer index without calling ec_recvpkt. If the requested index
* is not already in the buffer it calls ec_recvpkt to fetch it. There are
* three options now, 1 no frame read, so exit. 2 frame read but other
* than requested index, store in buffer and exit. 3 frame read with matching
* index, store in buffer, set completed flag in buffer status and exit.
*
* @param[in] port = port context struct
* @param[in] idx = requested index of frame
* @param[in] stacknumber = 0=primary 1=secondary stack
* @return Workcounter if a frame is found with corresponding index, otherwise
* EC_NOFRAME or EC_OTHERFRAME.
*/
int ecx_inframe(ecx_portt *port, uint8 idx, int stacknumber)
{
uint16 l;
int rval;
uint8 idxf;
ec_etherheadert *ehp;
ec_comt *ecp;
ec_stackT *stack;
ec_bufT *rxbuf;
if (!stacknumber)
{
stack = &(port->stack);
}
else
{
stack = &(port->redport->stack);
}
rval = EC_NOFRAME;
rxbuf = &(*stack->rxbuf)[idx];
/* check if requested index is already in buffer ? */
if ((idx < EC_MAXBUF) && ( (*stack->rxbufstat)[idx] == EC_BUF_RCVD))
{
l = (*rxbuf)[0] + ((uint16)((*rxbuf)[1] & 0x0f) << 8);
/* return WKC */
rval = ((*rxbuf)[l] + ((uint16)(*rxbuf)[l + 1] << 8));
/* mark as completed */
(*stack->rxbufstat)[idx] = EC_BUF_COMPLETE;
}
else
{
mtx_lock (port->rx_mutex);
/* non blocking call to retrieve frame from socket */
if (ecx_recvpkt(port, stacknumber))
{
rval = EC_OTHERFRAME;
ehp =(ec_etherheadert*)(stack->tempbuf);
/* check if it is an EtherCAT frame */
if (ehp->etype == oshw_htons(ETH_P_ECAT))
{
ecp =(ec_comt*)(&(*stack->tempbuf)[ETH_HEADERSIZE]);
l = etohs(ecp->elength) & 0x0fff;
idxf = ecp->index;
/* found index equals requested index ? */
if (idxf == idx)
{
/* yes, put it in the buffer array (strip ethernet header) */
memcpy(rxbuf, &(*stack->tempbuf)[ETH_HEADERSIZE], (*stack->txbuflength)[idx] - ETH_HEADERSIZE);
/* return WKC */
rval = ((*rxbuf)[l] + ((uint16)((*rxbuf)[l + 1]) << 8));
/* mark as completed */
(*stack->rxbufstat)[idx] = EC_BUF_COMPLETE;
/* store MAC source word 1 for redundant routing info */
(*stack->rxsa)[idx] = oshw_ntohs(ehp->sa1);
}
else
{
/* check if index exist and someone is waiting for it */
if (idxf < EC_MAXBUF && (*stack->rxbufstat)[idxf] == EC_BUF_TX)
{
rxbuf = &(*stack->rxbuf)[idxf];
/* put it in the buffer array (strip ethernet header) */
memcpy(rxbuf, &(*stack->tempbuf)[ETH_HEADERSIZE], (*stack->txbuflength)[idxf] - ETH_HEADERSIZE);
/* mark as received */
(*stack->rxbufstat)[idxf] = EC_BUF_RCVD;
(*stack->rxsa)[idxf] = oshw_ntohs(ehp->sa1);
}
else
{
/* strange things happened */
}
}
}
}
mtx_unlock (port->rx_mutex);
}
/* WKC if matching frame found */
return rval;
}
/** Blocking redundant receive frame function. If redundant mode is not active then
* it skips the secondary stack and redundancy functions. In redundant mode it waits
* for both (primary and secondary) frames to come in. The result goes in an decision
* tree that decides, depending on the route of the packet and its possible missing arrival,
* how to reroute the original packet to get the data in an other try.
*
* @param[in] port = port context struct
* @param[in] idx = requested index of frame
* @param[in] timer = absolute timeout time
* @return Workcounter if a frame is found with corresponding index, otherwise
* EC_NOFRAME.
*/
static int ecx_waitinframe_red(ecx_portt *port, uint8 idx, osal_timert timer)
{
int wkc = EC_NOFRAME;
int wkc2 = EC_NOFRAME;
int primrx, secrx;
/* if not in redundant mode then always assume secondary is OK */
if (port->redstate == ECT_RED_NONE)
{
wkc2 = 0;
}
do
{
/* only read frame if not already in */
if (wkc <= EC_NOFRAME)
{
wkc = ecx_inframe(port, idx, 0);
}
/* only try secondary if in redundant mode */
if (port->redstate != ECT_RED_NONE)
{
/* only read frame if not already in */
if (wkc2 <= EC_NOFRAME)
wkc2 = ecx_inframe(port, idx, 1);
}
/* wait for both frames to arrive or timeout */
} while (((wkc <= EC_NOFRAME) || (wkc2 <= EC_NOFRAME)) && (osal_timer_is_expired(&timer) == FALSE));
/* only do redundant functions when in redundant mode */
if (port->redstate != ECT_RED_NONE)
{
/* primrx if the received MAC source on primary socket */
primrx = 0;
if (wkc > EC_NOFRAME)
{
primrx = port->rxsa[idx];
}
/* secrx if the received MAC source on psecondary socket */
secrx = 0;
if (wkc2 > EC_NOFRAME)
{
secrx = port->redport->rxsa[idx];
}
/* primary socket got secondary frame and secondary socket got primary frame */
/* normal situation in redundant mode */
if ( ((primrx == RX_SEC) && (secrx == RX_PRIM)) )
{
/* copy secondary buffer to primary */
memcpy(&(port->rxbuf[idx]), &(port->redport->rxbuf[idx]), port->txbuflength[idx] - ETH_HEADERSIZE);
wkc = wkc2;
}
/* primary socket got nothing or primary frame, and secondary socket got secondary frame */
/* we need to resend TX packet */
if ( ((primrx == 0) && (secrx == RX_SEC)) ||
((primrx == RX_PRIM) && (secrx == RX_SEC)) )
{
osal_timert read_timer;
/* If both primary and secondary have partial connection retransmit the primary received
* frame over the secondary socket. The result from the secondary received frame is a combined
* frame that traversed all slaves in standard order. */
if ( (primrx == RX_PRIM) && (secrx == RX_SEC) )
{
/* copy primary rx to tx buffer */
memcpy(&(port->txbuf[idx][ETH_HEADERSIZE]), &(port->rxbuf[idx]), port->txbuflength[idx] - ETH_HEADERSIZE);
}
osal_timer_start(&read_timer, EC_TIMEOUTRET);
/* resend secondary tx */
ecx_outframe(port, idx, 1);
do
{
/* retrieve frame */
wkc2 = ecx_inframe(port, idx, 1);
} while ((wkc2 <= EC_NOFRAME) && (osal_timer_is_expired(&read_timer) == FALSE));
if (wkc2 > EC_NOFRAME)
{
/* copy secondary result to primary rx buffer */
memcpy(&(port->rxbuf[idx]), &(port->redport->rxbuf[idx]), port->txbuflength[idx] - ETH_HEADERSIZE);
wkc = wkc2;
}
}
}
/* return WKC or EC_NOFRAME */
return wkc;
}
/** Blocking receive frame function. Calls ec_waitinframe_red().
* @param[in] port = port context struct
* @param[in] idx = requested index of frame
* @param[in] timeout = timeout in us
* @return Workcounter if a frame is found with corresponding index, otherwise
* EC_NOFRAME.
*/
int ecx_waitinframe(ecx_portt *port, uint8 idx, int timeout)
{
int wkc;
osal_timert timer;
osal_timer_start (&timer, timeout);
wkc = ecx_waitinframe_red(port, idx, timer);
return wkc;
}
/** Blocking send and receive frame function. Used for non processdata frames.
* A datagram is build into a frame and transmitted via this function. It waits
* for an answer and returns the workcounter. The function retries if time is
* left and the result is WKC=0 or no frame received.
*
* The function calls ec_outframe_red() and ec_waitinframe_red().
*
* @param[in] port = port context struct
* @param[in] idx = index of frame
* @param[in] timeout = timeout in us
* @return Workcounter or EC_NOFRAME
*/
int ecx_srconfirm(ecx_portt *port, uint8 idx, int timeout)
{
int wkc = EC_NOFRAME;
osal_timert timer;
osal_timer_start(&timer, timeout);
do
{
osal_timert read_timer;
/* tx frame on primary and if in redundant mode a dummy on secondary */
ecx_outframe_red(port, idx);
osal_timer_start(&read_timer, MIN(timeout, EC_TIMEOUTRET));
/* get frame from primary or if in redundant mode possibly from secondary */
wkc = ecx_waitinframe_red(port, idx, read_timer);
/* wait for answer with WKC>0 or otherwise retry until timeout */
} while ((wkc <= EC_NOFRAME) && (osal_timer_is_expired(&timer) == FALSE));
return wkc;
}
#ifdef EC_VER1
int ec_setupnic(const char *ifname, int secondary)
{
return ecx_setupnic(&ecx_port, ifname, secondary);
}
int ec_closenic(void)
{
return ecx_closenic(&ecx_port);
}
uint8 ec_getindex(void)
{
return ecx_getindex(&ecx_port);
}
void ec_setbufstat(uint8 idx, int bufstat)
{
ecx_setbufstat(&ecx_port, idx, bufstat);
}
int ec_outframe(uint8 idx, int stacknumber)
{
return ecx_outframe(&ecx_port, idx, stacknumber);
}
int ec_outframe_red(uint8 idx)
{
return ecx_outframe_red(&ecx_port, idx);
}
int ec_inframe(uint8 idx, int stacknumber)
{
return ecx_inframe(&ecx_port, idx, stacknumber);
}
int ec_waitinframe(uint8 idx, int timeout)
{
return ecx_waitinframe(&ecx_port, idx, timeout);
}
int ec_srconfirm(uint8 idx, int timeout)
{
return ecx_srconfirm(&ecx_port, idx, timeout);
}
#endif
+109
View File
@@ -0,0 +1,109 @@
/*
* Licensed under the GNU General Public License version 2 with exceptions. See
* LICENSE file in the project root for full license information
*/
/** \file
* \brief
* Headerfile for nicdrv.c
*/
#ifndef _nicdrvh_
#define _nicdrvh_
/** pointer structure to Tx and Rx stacks */
typedef struct
{
/** socket connection used */
int *sock;
/** tx buffer */
ec_bufT (*txbuf)[EC_MAXBUF];
/** tx buffer lengths */
int (*txbuflength)[EC_MAXBUF];
/** temporary receive buffer */
ec_bufT *tempbuf;
/** rx buffers */
ec_bufT (*rxbuf)[EC_MAXBUF];
/** rx buffer status fields */
int (*rxbufstat)[EC_MAXBUF];
/** received MAC source address (middle word) */
int (*rxsa)[EC_MAXBUF];
} ec_stackT;
/** pointer structure to buffers for redundant port */
typedef struct
{
ec_stackT stack;
int sockhandle;
/** rx buffers */
ec_bufT rxbuf[EC_MAXBUF];
/** rx buffer status */
int rxbufstat[EC_MAXBUF];
/** rx MAC source address */
int rxsa[EC_MAXBUF];
/** temporary rx buffer */
ec_bufT tempinbuf;
} ecx_redportt;
/** pointer structure to buffers, vars and mutexes for port instantiation */
typedef struct
{
ec_stackT stack;
int sockhandle;
/** rx buffers */
ec_bufT rxbuf[EC_MAXBUF];
/** rx buffer status */
int rxbufstat[EC_MAXBUF];
/** rx MAC source address */
int rxsa[EC_MAXBUF];
/** temporary rx buffer */
ec_bufT tempinbuf;
/** temporary rx buffer status */
int tempinbufs;
/** transmit buffers */
ec_bufT txbuf[EC_MAXBUF];
/** transmit buffer lengths */
int txbuflength[EC_MAXBUF];
/** temporary tx buffer */
ec_bufT txbuf2;
/** temporary tx buffer length */
int txbuflength2;
/** last used frame index */
uint8 lastidx;
/** current redundancy state */
int redstate;
/** pointer to redundancy port and buffers */
ecx_redportt *redport;
mtx_t * getindex_mutex;
mtx_t * tx_mutex;
mtx_t * rx_mutex;
} ecx_portt;
extern const uint16 priMAC[3];
extern const uint16 secMAC[3];
#ifdef EC_VER1
extern ecx_portt ecx_port;
extern ecx_redportt ecx_redport;
int ec_setupnic(const char * ifname, int secondary);
int ec_closenic(void);
void ec_setbufstat(uint8 idx, int bufstat);
uint8 ec_getindex(void);
int ec_outframe(uint8 idx, int stacknumber);
int ec_outframe_red(uint8 idx);
int ec_waitinframe(uint8 idx, int timeout);
int ec_srconfirm(uint8 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, uint8 idx, int bufstat);
uint8 ecx_getindex(ecx_portt *port);
int ecx_outframe(ecx_portt *port, uint8 idx, int stacknumber);
int ecx_outframe_red(ecx_portt *port, uint8 idx);
int ecx_waitinframe(ecx_portt *port, uint8 idx, int timeout);
int ecx_srconfirm(ecx_portt *port, uint8 idx,int timeout);
#endif
+53
View File
@@ -0,0 +1,53 @@
/*
* Licensed under the GNU General Public License version 2 with exceptions. See
* LICENSE file in the project root for full license information
*/
#include "oshw.h"
#include <stdlib.h>
#include <lwip/inet.h>
/**
* Host to Network byte order (i.e. to big endian).
*
* Note that Ethercat uses little endian byte order, except for the Ethernet
* header which is big endian as usual.
*/
uint16 oshw_htons(const uint16 host)
{
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(const 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;
/* TODO if needed */
return ret_adapter;
}
/** Free memory allocated memory used by adapter collection.
* @param[in] adapter = First element in linked list of adapters
* EC_NOFRAME.
*/
void oshw_free_adapters(ec_adaptert * adapter)
{
/* TODO if needed */
}
+35
View File
@@ -0,0 +1,35 @@
/*
* Licensed under the GNU General Public License version 2 with exceptions. See
* LICENSE file in the project root for full license information
*/
/** \file
* \brief
* Headerfile for oshw.c
*/
#ifndef _oshw_
#define _oshw_
#ifdef __cplusplus
extern "C"
{
#endif
#include <kern.h>
#include "ethercattype.h"
#include "nicdrv.h"
#include "ethercatmain.h"
uint16 oshw_htons(uint16 host);
uint16 oshw_ntohs(uint16 network);
ec_adaptert * oshw_find_adapters(void);
void oshw_free_adapters(ec_adaptert * adapter);
#ifdef __cplusplus
}
#endif
#endif
+980
View File
@@ -0,0 +1,980 @@
/*
* 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 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 received.
* 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, instantiate 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 bottleneck.
*
* 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 <vxWorks.h>
#include <ctype.h>
#include <string.h>
#include <stdio.h>
#include <muxLib.h>
#include <ipProto.h>
#include <wvLib.h>
#include <sysLib.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 connection */
ECT_RED_DOUBLE
};
/** Primary source MAC address used for EtherCAT.
* This address is not the MAC address used from the NIC.
* EtherCAT does not care about MAC addressing, but it is used here to
* differentiate the route the packet traverses through the EtherCAT
* segment. This is needed to find out the packet flow in redundant
* configurations. */
const uint16 priMAC[3] = { 0x0101, 0x0101, 0x0101 };
/** Secondary source MAC address used for EtherCAT. */
const uint16 secMAC[3] = { 0x0404, 0x0404, 0x0404 };
/** second MAC word is used for identification */
#define RX_PRIM priMAC[1]
/** second MAC word is used for identification */
#define RX_SEC secMAC[1]
/* usec per tick for timeconversion, default to 1kHz */
#define USECS_PER_SEC 1000000
static unsigned int usec_per_tick = 1000;
/** 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;
/* Get systick info, sysClkRateGet return ticks per second */
usec_per_tick = USECS_PER_SEC / sysClkRateGet();
/* Don't allow 0 since it is used in DIV */
if(usec_per_tick == 0)
usec_per_tick = 1;
/* Make reference 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 = semMCreate(SEM_Q_PRIORITY | SEM_INVERSION_SAFE);
/* 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;
M_BLK_ID trash_can;
pPktDev = &(port->pktDev);
for (i = 0; i < EC_MAXBUF; i++)
{
if (port->msgQId[i] != MSG_Q_ID_NULL)
{
if (msgQReceive(port->msgQId[i],
(char *)&trash_can,
sizeof(M_BLK_ID),
NO_WAIT) != ERROR)
{
NIC_LOGMSG("ecx_closenic: idx %d MsgQ close\n", i,
2, 3, 4, 5, 6);
/* Free resources */
netMblkClChainFree(trash_can);
}
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)
{
if (msgQReceive(port->redport->msgQId[i],
(char *)&trash_can,
sizeof(M_BLK_ID),
NO_WAIT) != ERROR)
{
NIC_LOGMSG("ecx_closenic: idx %d MsgQ close\n", i,
2, 3, 4, 5, 6);
/* Free resources */
netMblkClChainFree(trash_can);
}
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 always broadcast.
* Ethertype is always 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.
*/
uint8 ecx_getindex(ecx_portt *port)
{
uint8 idx;
uint8 cnt;
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;
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, uint8 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] idx = index in tx buffer array
* @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, uint8 idx, void * buf, int len)
{
STATUS status = OK;
M_BLK_ID pPacket = NULL;
int rval = 0;
END_OBJ *endObj = (END_OBJ *)pPktDev->endObj;
MSG_Q_ID msgQId;
/* Clean up any abandoned frames and re-use the allocated buffer*/
msgQId = pPktDev->port->msgQId[idx];
if(msgQNumMsgs(msgQId) > 0)
{
pPktDev->abandoned_count++;
NIC_LOGMSG("ec_outfram_send: idx %d MsgQ abandoned\n", idx,
2, 3, 4, 5, 6);
if (msgQReceive(msgQId,
(char *)&pPacket,
sizeof(M_BLK_ID),
NO_WAIT) == ERROR)
{
pPacket = NULL;
NIC_LOGMSG("ec_outfram_send: idx %d MsgQ mBlk handled by receiver\n", idx,
2, 3, 4, 5, 6);
}
}
if (pPacket == NULL)
{
/* Allocate m_blk to send */
if ((pPacket = netTupleGet(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_PKTHDR;
pPacket->mBlkHdr.mData = pPacket->pClBlk->clNode.pClBuf;
pPacket->mBlkPktHdr.len = pPacket->m_len;
netMblkFromBufCopy(pPacket, buf, 0, pPacket->mBlkHdr.mLen, M_DONTWAIT, NULL);
status = muxSend(endObj, pPacket);
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, uint8 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);
}
(*stack->rxbufstat)[idx] = EC_BUF_TX;
rval = ec_outfram_send(pPktDev, idx, (char*)(*stack->txbuf)[idx],
(*stack->txbuflength)[idx]);
if (rval > 0)
{
port->pktDev.tx_count++;
}
else
{
(*stack->rxbufstat)[idx] = EC_BUF_EMPTY;
}
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, uint8 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 */
port->redport->rxbufstat[idx] = EC_BUF_TX;
rval = ec_outfram_send(&(port->redport->pktDev), idx, &(port->txbuf2), port->txbuflength2);
if (rval <= 0)
{
port->redport->rxbufstat[idx] = EC_BUF_EMPTY;
}
}
return rval;
}
/** Call back routine registered as hook with mux layer 2 driver
* @param[in] pCookie = Mux cookie
* @param[in] type = received 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 successfully 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;
uint8 idxf;
ec_comt *ecp;
ec_bufT * tempbuf;
ecx_portt * port;
MSG_Q_ID msgQId;
ETHERCAT_PKT_DEV * pPktDev;
int length;
int bufstat;
/* 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)
{
bufstat = port->redport->rxbufstat[idxf];
msgQId = port->redport->msgQId[idxf];
}
else
{
bufstat = port->rxbufstat[idxf];
msgQId = port->msgQId[idxf];
}
/* Check length and if someone expects the frame */
if (length > 0 && bufstat == EC_BUF_TX)
{
/* Post the frame to the receive 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) != ERROR)
{
/* 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, uint8 idx, int stacknumber, M_BLK_ID * pMblk, int timeout)
{
int bytesrx = 0;
MSG_Q_ID msgQId;
int tick_timeout = max((timeout / usec_per_tick), 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 allocated 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, uint8 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, uint8 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 received MAC source on primary socket */
primrx = 0;
if (wkc > EC_NOFRAME) primrx = port->rxsa[idx];
/* secrx if the received MAC source on psecondary socket */
secrx = 0;
if (wkc2 > EC_NOFRAME) secrx = port->redport->rxsa[idx];
/* primary socket got secondary frame and secondary socket got primary frame */
/* normal situation in redundant mode */
if ( ((primrx == RX_SEC) && (secrx == RX_PRIM)) )
{
/* copy secondary buffer to primary */
memcpy(&(port->rxbuf[idx]), &(port->redport->rxbuf[idx]), port->txbuflength[idx] - ETH_HEADERSIZE);
wkc = wkc2;
}
/* primary socket got nothing or primary frame, and secondary socket got secondary frame */
/* we need to resend TX packet */
if ( ((primrx == 0) && (secrx == RX_SEC)) ||
((primrx == RX_PRIM) && (secrx == RX_SEC)) )
{
/* If both primary and secondary have partial connection retransmit the primary received
* frame over the secondary socket. The result from the secondary received frame is a combined
* frame that traversed all slaves in standard order. */
if ( (primrx == RX_PRIM) && (secrx == RX_SEC) )
{
/* copy primary rx to tx buffer */
memcpy(&(port->txbuf[idx][ETH_HEADERSIZE]), &(port->rxbuf[idx]), port->txbuflength[idx] - ETH_HEADERSIZE);
}
osal_timer_start (&timer2, EC_TIMEOUTRET);
/* resend secondary tx */
ecx_outframe(port, idx, 1);
do
{
/* retrieve frame */
wkc2 = ecx_inframe(port, idx, 1, 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, uint8 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 receive frame function. Used for non processdata frames.
* A datagram is build into a frame and transmitted via this function. It waits
* for an answer and returns the workcounter. The function retries if time is
* left and the result is WKC=0 or no frame received.
*
* The function calls ec_outframe_red() and ec_waitinframe_red().
*
* @param[in] port = port context struct
* @param[in] idx = index of frame
* @param[in] timeout = timeout in us
* @return Workcounter or EC_NOFRAME
*/
int ecx_srconfirm(ecx_portt *port, uint8 idx, int timeout)
{
int wkc = EC_NOFRAME;
osal_timert timer1, timer2;
osal_timer_start (&timer1, timeout);
do
{
/* tx frame on primary and if in redundant mode a dummy on secondary */
ecx_outframe_red(port, idx);
if (timeout < EC_TIMEOUTRET)
{
osal_timer_start (&timer2, timeout);
}
else
{
/* normally use partial timeout for rx */
osal_timer_start (&timer2, EC_TIMEOUTRET);
}
/* get frame from primary or if in redundant mode possibly from secondary */
wkc = ecx_waitinframe_red(port, idx, &timer2, 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);
}
uint8 ec_getindex(void)
{
return ecx_getindex(&ecx_port);
}
void ec_setbufstat(uint8 idx, int bufstat)
{
ecx_setbufstat(&ecx_port, idx, bufstat);
}
int ec_outframe(uint8 idx, int stacknumber)
{
return ecx_outframe(&ecx_port, idx, stacknumber);
}
int ec_outframe_red(uint8 idx)
{
return ecx_outframe_red(&ecx_port, idx);
}
int ec_inframe(uint8 idx, int stacknumber, int timeout)
{
return ecx_inframe(&ecx_port, idx, stacknumber, timeout);
}
int ec_waitinframe(uint8 idx, int timeout)
{
return ecx_waitinframe(&ecx_port, idx, timeout);
}
int ec_srconfirm(uint8 idx, int timeout)
{
return ecx_srconfirm(&ecx_port, idx, timeout);
}
#endif
+130
View File
@@ -0,0 +1,130 @@
/*
* Licensed under the GNU General Public License version 2 with exceptions. See
* LICENSE file in the project root for full license information
*/
/** \file
* \brief
* Headerfile for nicdrv.c
*/
#ifndef _nicdrvh_
#define _nicdrvh_
#ifdef __cplusplus
extern "C"
{
#endif
#include <vxWorks.h>
/** structure to connect EtherCAT stack and VxWorks device */
typedef struct ETHERCAT_PKT_DEV
{
struct ecx_port *port;
void *pCookie;
void *endObj;
UINT32 redundant;
UINT32 tx_count;
UINT32 rx_count;
UINT32 overrun_count;
UINT32 abandoned_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 lengths */
int txbuflength[EC_MAXBUF];
/** temporary tx buffer */
ec_bufT txbuf2;
/** temporary tx buffer length */
int txbuflength2;
/** last used frame index */
uint8 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(uint8 idx, int bufstat);
uint8 ec_getindex(void);
int ec_outframe(uint8 idx, int sock);
int ec_outframe_red(uint8 idx);
int ec_waitinframe(uint8 idx, int timeout);
int ec_srconfirm(uint8 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, uint8 idx, int bufstat);
uint8 ecx_getindex(ecx_portt *port);
int ecx_outframe(ecx_portt *port, uint8 idx, int sock);
int ecx_outframe_red(ecx_portt *port, uint8 idx);
int ecx_waitinframe(ecx_portt *port, uint8 idx, int timeout);
int ecx_srconfirm(ecx_portt *port, uint8 idx,int timeout);
#ifdef __cplusplus
}
#endif
#endif
+60
View File
@@ -0,0 +1,60 @@
/*
* Licensed under the GNU General Public License version 2 with exceptions. See
* LICENSE file in the project root for full license information
*/
#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);
}
+23
View File
@@ -0,0 +1,23 @@
/*
* Licensed under the GNU General Public License version 2 with exceptions. See
* LICENSE file in the project root for full license information
*/
/** \file
* \brief
* Headerfile for oshw.c
*/
#ifndef _oshw_
#define _oshw_
#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
+648
View File
@@ -0,0 +1,648 @@
/*
* Licensed under the GNU General Public License version 2 with exceptions. See
* LICENSE file in the project root for full license information
*/
/** \file
* \brief
* EtherCAT RAW socket driver.
*
* Low level interface functions to send and receive EtherCAT packets.
* EtherCAT has the property that packets are only send by the master,
* and the send packets always return in the receive buffer.
* There can be multiple packets "on the wire" before they return.
* To combine the received packets with the original send packets a buffer
* system is installed. The identifier is put in the index item of the
* EtherCAT header. The index is stored and compared when a frame is received.
* If there is a match the packet can be combined with the transmit packet
* and returned to the higher level function.
*
* The socket layer can exhibit a reversal in the packet order (rare).
* If the Tx order is A-B-C the return order could be A-C-B. The indexed buffer
* will reorder the packets automatically.
*
* The "redundant" option will configure two sockets and two NIC interfaces.
* Slaves are connected to both interfaces, one on the IN port and one on the
* OUT port. Packets are send via both interfaces. Any one of the connections
* (also an interconnect) can be removed and the slaves are still serviced with
* packets. The software layer will detect the possible failure modes and
* compensate. If needed the packets from interface A are resent through interface B.
* This layer is fully transparent for the higher layers.
*/
#ifdef WIN32
#include <sys/types.h>
#include <stdio.h>
#include <fcntl.h>
#include <string.h>
#include <winsock2.h>
#include "ethercattype.h"
#include <Mmsystem.h>
#include "nicdrv.h"
#include "osal_win32.h"
#endif
/** Redundancy modes */
enum
{
/** No redundancy, single NIC mode */
ECT_RED_NONE,
/** Double redundant NIC connection */
ECT_RED_DOUBLE
};
/** Primary source MAC address used for EtherCAT.
* This address is not the MAC address used from the NIC.
* EtherCAT does not care about MAC addressing, but it is used here to
* differentiate the route the packet traverses through the EtherCAT
* segment. This is needed to fund 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]
static char errbuf[PCAP_ERRBUF_SIZE];
static void ecx_clear_rxbufstat(int *rxbufstat)
{
int i;
for(i = 0; i < EC_MAXBUF; i++)
{
rxbufstat[i] = EC_BUF_EMPTY;
}
}
/** Basic setup to connect NIC to socket.
* @param[in] port = port context struct
* @param[in] ifname = Name of NIC device, f.e. "eth0"
* @param[in] secondary = if >0 then use secondary stack instead of primary
* @return >0 if succeeded
*/
int ecx_setupnic(ecx_portt *port, const char *ifname, int secondary)
{
int i;
pcap_t **psock;
if (secondary)
{
/* secondary port struct available? */
if (port->redport)
{
/* when using secondary socket it is automatically a redundant setup */
psock = &(port->redport->sockhandle);
*psock = NULL;
port->redstate = ECT_RED_DOUBLE;
port->redport->stack.sock = &(port->redport->sockhandle);
port->redport->stack.txbuf = &(port->txbuf);
port->redport->stack.txbuflength = &(port->txbuflength);
port->redport->stack.tempbuf = &(port->redport->tempinbuf);
port->redport->stack.rxbuf = &(port->redport->rxbuf);
port->redport->stack.rxbufstat = &(port->redport->rxbufstat);
port->redport->stack.rxsa = &(port->redport->rxsa);
ecx_clear_rxbufstat(&(port->redport->rxbufstat[0]));
}
else
{
/* fail */
return 0;
}
}
else
{
InitializeCriticalSection(&(port->getindex_mutex));
InitializeCriticalSection(&(port->tx_mutex));
InitializeCriticalSection(&(port->rx_mutex));
port->sockhandle = NULL;
port->lastidx = 0;
port->redstate = ECT_RED_NONE;
port->stack.sock = &(port->sockhandle);
port->stack.txbuf = &(port->txbuf);
port->stack.txbuflength = &(port->txbuflength);
port->stack.tempbuf = &(port->tempinbuf);
port->stack.rxbuf = &(port->rxbuf);
port->stack.rxbufstat = &(port->rxbufstat);
port->stack.rxsa = &(port->rxsa);
ecx_clear_rxbufstat(&(port->rxbufstat[0]));
psock = &(port->sockhandle);
}
/* we use pcap socket to send RAW packets in windows user mode*/
*psock = pcap_open(ifname, 65536, PCAP_OPENFLAG_PROMISCUOUS |
PCAP_OPENFLAG_MAX_RESPONSIVENESS |
PCAP_OPENFLAG_NOCAPTURE_LOCAL, -1, NULL , errbuf);
if (NULL == *psock)
{
printf("interface %s could not open with pcap\n", ifname);
return 0;
}
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)
{
timeEndPeriod(1);
if (port->sockhandle != NULL)
{
DeleteCriticalSection(&(port->getindex_mutex));
DeleteCriticalSection(&(port->tx_mutex));
DeleteCriticalSection(&(port->rx_mutex));
pcap_close(port->sockhandle);
port->sockhandle = NULL;
}
if ((port->redport) && (port->redport->sockhandle != NULL))
{
pcap_close(port->redport->sockhandle);
port->redport->sockhandle = NULL;
}
return 0;
}
/** Fill buffer with ethernet header structure.
* Destination MAC is always broadcast.
* Ethertype is always ETH_P_ECAT.
* @param[out] p = buffer
*/
void ec_setupheader(void *p)
{
ec_etherheadert *bp;
bp = p;
bp->da0 = 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.
*/
uint8 ecx_getindex(ecx_portt *port)
{
uint8 idx;
uint8 cnt;
EnterCriticalSection(&(port->getindex_mutex));
idx = port->lastidx + 1;
/* index can't be larger than buffer array */
if (idx >= EC_MAXBUF)
{
idx = 0;
}
cnt = 0;
/* try to find unused index */
while ((port->rxbufstat[idx] != EC_BUF_EMPTY) && (cnt < EC_MAXBUF))
{
idx++;
cnt++;
if (idx >= EC_MAXBUF)
{
idx = 0;
}
}
port->rxbufstat[idx] = EC_BUF_ALLOC;
if (port->redstate != ECT_RED_NONE)
port->redport->rxbufstat[idx] = EC_BUF_ALLOC;
port->lastidx = idx;
LeaveCriticalSection(&(port->getindex_mutex));
return idx;
}
/** Set rx buffer status.
* @param[in] port = port context struct
* @param[in] idx = index in buffer array
* @param[in] bufstat = status to set
*/
void ecx_setbufstat(ecx_portt *port, uint8 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, uint8 idx, int stacknumber)
{
int lp, rval;
ec_stackT *stack;
if (!stacknumber)
{
stack = &(port->stack);
}
else
{
stack = &(port->redport->stack);
}
lp = (*stack->txbuflength)[idx];
(*stack->rxbufstat)[idx] = EC_BUF_TX;
rval = pcap_sendpacket(*stack->sock, (*stack->txbuf)[idx], lp);
if (rval == PCAP_ERROR)
{
(*stack->rxbufstat)[idx] = EC_BUF_EMPTY;
}
return rval;
}
/** Transmit buffer over socket (non blocking).
* @param[in] port = port context struct
* @param[in] idx = index in tx buffer array
* @return socket send result
*/
int ecx_outframe_red(ecx_portt *port, uint8 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)
{
EnterCriticalSection( &(port->tx_mutex) );
ehp = (ec_etherheadert *)&(port->txbuf2);
/* use dummy frame for secondary socket transmit (BRD) */
datagramP = (ec_comt*)&(port->txbuf2[ETH_HEADERSIZE]);
/* write index to frame */
datagramP->index = idx;
/* rewrite MAC source address 1 to secondary */
ehp->sa1 = htons(secMAC[1]);
/* transmit over secondary socket */
port->redport->rxbufstat[idx] = EC_BUF_TX;
if (pcap_sendpacket(port->redport->sockhandle, (u_char const *)&(port->txbuf2), port->txbuflength2) == PCAP_ERROR)
{
port->redport->rxbufstat[idx] = EC_BUF_EMPTY;
}
LeaveCriticalSection( &(port->tx_mutex) );
}
return rval;
}
/** Non blocking read of socket. Put frame in temporary buffer.
* @param[in] port = port context struct
* @param[in] stacknumber = 0=primary 1=secondary stack
* @return >0 if frame is available and read
*/
static int ecx_recvpkt(ecx_portt *port, int stacknumber)
{
int lp, bytesrx;
ec_stackT *stack;
struct pcap_pkthdr * header;
unsigned char const * pkt_data;
int res;
if (!stacknumber)
{
stack = &(port->stack);
}
else
{
stack = &(port->redport->stack);
}
lp = sizeof(port->tempinbuf);
res = pcap_next_ex(*stack->sock, &header, &pkt_data);
if (res <=0 )
{
port->tempinbufs = 0;
return 0;
}
bytesrx = header->len;
if (bytesrx > lp)
{
bytesrx = lp;
}
memcpy(*stack->tempbuf, pkt_data, bytesrx);
port->tempinbufs = bytesrx;
return (bytesrx > 0);
}
/** Non blocking receive frame function. Uses RX buffer and index to combine
* read frame with transmitted frame. To compensate for received frames that
* are out-of-order all frames are stored in their respective indexed buffer.
* If a frame was placed in the buffer previously, the function retrieves it
* from that buffer index without calling ec_recvpkt. If the requested index
* is not already in the buffer it calls ec_recvpkt to fetch it. There are
* three options now, 1 no frame read, so exit. 2 frame read but other
* than requested index, store in buffer and exit. 3 frame read with matching
* index, store in buffer, set completed flag in buffer status and exit.
*
* @param[in] port = port context struct
* @param[in] idx = requested index of frame
* @param[in] stacknumber = 0=primary 1=secondary stack
* @return Workcounter if a frame is found with corresponding index, otherwise
* EC_NOFRAME or EC_OTHERFRAME.
*/
int ecx_inframe(ecx_portt *port, uint8 idx, int stacknumber)
{
uint16 l;
int rval;
uint8 idxf;
ec_etherheadert *ehp;
ec_comt *ecp;
ec_stackT *stack;
ec_bufT *rxbuf;
if (!stacknumber)
{
stack = &(port->stack);
}
else
{
stack = &(port->redport->stack);
}
rval = EC_NOFRAME;
rxbuf = &(*stack->rxbuf)[idx];
/* check if requested index is already in buffer ? */
if ((idx < EC_MAXBUF) && ((*stack->rxbufstat)[idx] == EC_BUF_RCVD))
{
l = (*rxbuf)[0] + ((uint16)((*rxbuf)[1] & 0x0f) << 8);
/* return WKC */
rval = ((*rxbuf)[l] + ((uint16)(*rxbuf)[l + 1] << 8));
/* mark as completed */
(*stack->rxbufstat)[idx] = EC_BUF_COMPLETE;
}
else
{
EnterCriticalSection(&(port->rx_mutex));
/* non blocking call to retrieve frame from socket */
if (ecx_recvpkt(port, stacknumber))
{
rval = EC_OTHERFRAME;
ehp =(ec_etherheadert*)(stack->tempbuf);
/* check if it is an EtherCAT frame */
if (ehp->etype == htons(ETH_P_ECAT))
{
ecp =(ec_comt*)(&(*stack->tempbuf)[ETH_HEADERSIZE]);
l = etohs(ecp->elength) & 0x0fff;
idxf = ecp->index;
/* found index equals requested index ? */
if (idxf == idx)
{
/* yes, put it in the buffer array (strip ethernet header) */
memcpy(rxbuf, &(*stack->tempbuf)[ETH_HEADERSIZE], (*stack->txbuflength)[idx] - ETH_HEADERSIZE);
/* return WKC */
rval = ((*rxbuf)[l] + ((uint16)((*rxbuf)[l + 1]) << 8));
/* mark as completed */
(*stack->rxbufstat)[idx] = EC_BUF_COMPLETE;
/* store MAC source word 1 for redundant routing info */
(*stack->rxsa)[idx] = 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] = ntohs(ehp->sa1);
}
else
{
/* strange things happened */
}
}
}
}
LeaveCriticalSection( &(port->rx_mutex) );
}
/* WKC if matching frame found */
return rval;
}
/** Blocking redundant receive frame function. If redundant mode is not active then
* it skips the secondary stack and redundancy functions. In redundant mode it waits
* for both (primary and secondary) frames to come in. The result goes in an decision
* tree that decides, depending on the route of the packet and its possible missing arrival,
* how to reroute the original packet to get the data in an other try.
*
* @param[in] port = port context struct
* @param[in] idx = requested index of frame
* @param[in] tvs = timeout
* @return Workcounter if a frame is found with corresponding index, otherwise
* EC_NOFRAME.
*/
static int ecx_waitinframe_red(ecx_portt *port, uint8 idx, osal_timert *timer)
{
osal_timert timer2;
int wkc = EC_NOFRAME;
int wkc2 = EC_NOFRAME;
int primrx, secrx;
/* if not in redundant mode then always assume secondary is OK */
if (port->redstate == ECT_RED_NONE)
wkc2 = 0;
do
{
/* only read frame if not already in */
if (wkc <= EC_NOFRAME)
wkc = ecx_inframe(port, idx, 0);
/* only try secondary if in redundant mode */
if (port->redstate != ECT_RED_NONE)
{
/* only read frame if not already in */
if (wkc2 <= EC_NOFRAME)
wkc2 = ecx_inframe(port, idx, 1);
}
/* wait for both frames to arrive or timeout */
} while (((wkc <= EC_NOFRAME) || (wkc2 <= EC_NOFRAME)) && !osal_timer_is_expired(timer));
/* only do redundant functions when in redundant mode */
if (port->redstate != ECT_RED_NONE)
{
/* primrx if the received MAC source on primary socket */
primrx = 0;
if (wkc > EC_NOFRAME) primrx = port->rxsa[idx];
/* secrx if the received MAC source on psecondary socket */
secrx = 0;
if (wkc2 > EC_NOFRAME) secrx = port->redport->rxsa[idx];
/* primary socket got secondary frame and secondary socket got primary frame */
/* normal situation in redundant mode */
if ( ((primrx == RX_SEC) && (secrx == RX_PRIM)) )
{
/* copy secondary buffer to primary */
memcpy(&(port->rxbuf[idx]), &(port->redport->rxbuf[idx]), port->txbuflength[idx] - ETH_HEADERSIZE);
wkc = wkc2;
}
/* primary socket got nothing or primary frame, and secondary socket got secondary frame */
/* we need to resend TX packet */
if ( ((primrx == 0) && (secrx == RX_SEC)) ||
((primrx == RX_PRIM) && (secrx == RX_SEC)) )
{
/* If both primary and secondary have partial connection retransmit the primary received
* frame over the secondary socket. The result from the secondary received frame is a combined
* frame that traversed all slaves in standard order. */
if ( (primrx == RX_PRIM) && (secrx == RX_SEC) )
{
/* copy primary rx to tx buffer */
memcpy(&(port->txbuf[idx][ETH_HEADERSIZE]), &(port->rxbuf[idx]), port->txbuflength[idx] - ETH_HEADERSIZE);
}
osal_timer_start (&timer2, EC_TIMEOUTRET);
/* resend secondary tx */
ecx_outframe(port, idx, 1);
do
{
/* retrieve frame */
wkc2 = ecx_inframe(port, idx, 1);
} while ((wkc2 <= EC_NOFRAME) && !osal_timer_is_expired(&timer2));
if (wkc2 > EC_NOFRAME)
{
/* copy secondary result to primary rx buffer */
memcpy(&(port->rxbuf[idx]), &(port->redport->rxbuf[idx]), port->txbuflength[idx] - ETH_HEADERSIZE);
wkc = wkc2;
}
}
}
/* return WKC or EC_NOFRAME */
return wkc;
}
/** Blocking receive frame function. Calls ec_waitinframe_red().
* @param[in] port = port context struct
* @param[in] idx = requested index of frame
* @param[in] timeout = timeout in us
* @return Workcounter if a frame is found with corresponding index, otherwise
* EC_NOFRAME.
*/
int ecx_waitinframe(ecx_portt *port, uint8 idx, int timeout)
{
int wkc;
osal_timert timer;
osal_timer_start (&timer, timeout);
wkc = ecx_waitinframe_red(port, idx, &timer);
return wkc;
}
/** Blocking send and receive frame function. Used for non processdata frames.
* A datagram is build into a frame and transmitted via this function. It waits
* for an answer and returns the workcounter. The function retries if time is
* left and the result is WKC=0 or no frame received.
*
* The function calls ec_outframe_red() and ec_waitinframe_red().
*
* @param[in] port = port context struct
* @param[in] idx = index of frame
* @param[in] timeout = timeout in us
* @return Workcounter or EC_NOFRAME
*/
int ecx_srconfirm(ecx_portt *port, uint8 idx, int timeout)
{
int wkc = EC_NOFRAME;
osal_timert timer1, timer2;
osal_timer_start (&timer1, timeout);
do
{
/* tx frame on primary and if in redundant mode a dummy on secondary */
ecx_outframe_red(port, idx);
if (timeout < EC_TIMEOUTRET)
{
osal_timer_start (&timer2, timeout);
}
else
{
/* normally use partial timeout for rx */
osal_timer_start (&timer2, EC_TIMEOUTRET);
}
/* get frame from primary or if in redundant mode possibly from secondary */
wkc = ecx_waitinframe_red(port, idx, &timer2);
/* wait for answer with WKC>=0 or otherwise retry until timeout */
} while ((wkc <= EC_NOFRAME) && !osal_timer_is_expired (&timer1));
return wkc;
}
#ifdef EC_VER1
int ec_setupnic(const char *ifname, int secondary)
{
return ecx_setupnic(&ecx_port, ifname, secondary);
}
int ec_closenic(void)
{
return ecx_closenic(&ecx_port);
}
uint8 ec_getindex(void)
{
return ecx_getindex(&ecx_port);
}
void ec_setbufstat(uint8 idx, int bufstat)
{
ecx_setbufstat(&ecx_port, idx, bufstat);
}
int ec_outframe(uint8 idx, int stacknumber)
{
return ecx_outframe(&ecx_port, idx, stacknumber);
}
int ec_outframe_red(uint8 idx)
{
return ecx_outframe_red(&ecx_port, idx);
}
int ec_inframe(uint8 idx, int stacknumber)
{
return ecx_inframe(&ecx_port, idx, stacknumber);
}
int ec_waitinframe(uint8 idx, int timeout)
{
return ecx_waitinframe(&ecx_port, idx, timeout);
}
int ec_srconfirm(uint8 idx, int timeout)
{
return ecx_srconfirm(&ecx_port, idx, timeout);
}
#endif
+123
View File
@@ -0,0 +1,123 @@
/*
* Licensed under the GNU General Public License version 2 with exceptions. See
* LICENSE file in the project root for full license information
*/
/** \file
* \brief
* Headerfile for nicdrv.c
*/
#ifndef _nicdrvh_
#define _nicdrvh_
#ifdef __cplusplus
extern "C"
{
#endif
#define HAVE_REMOTE
#include <pcap.h>
#include <Packet32.h>
/** pointer structure to Tx and Rx stacks */
typedef struct
{
/** socket connection used */
pcap_t **sock;
/** tx buffer */
ec_bufT (*txbuf)[EC_MAXBUF];
/** tx buffer lengths */
int (*txbuflength)[EC_MAXBUF];
/** temporary receive buffer */
ec_bufT *tempbuf;
/** rx buffers */
ec_bufT (*rxbuf)[EC_MAXBUF];
/** rx buffer status fields */
int (*rxbufstat)[EC_MAXBUF];
/** received MAC source address (middle word) */
int (*rxsa)[EC_MAXBUF];
} ec_stackT;
/** pointer structure to buffers for redundant port */
typedef struct
{
ec_stackT stack;
pcap_t *sockhandle;
/** rx buffers */
ec_bufT rxbuf[EC_MAXBUF];
/** rx buffer status */
int rxbufstat[EC_MAXBUF];
/** rx MAC source address */
int rxsa[EC_MAXBUF];
/** temporary rx buffer */
ec_bufT tempinbuf;
} ecx_redportt;
/** pointer structure to buffers, vars and mutexes for port instantiation */
typedef struct
{
ec_stackT stack;
pcap_t *sockhandle;
/** rx buffers */
ec_bufT rxbuf[EC_MAXBUF];
/** rx buffer status */
int rxbufstat[EC_MAXBUF];
/** rx MAC source address */
int rxsa[EC_MAXBUF];
/** temporary rx buffer */
ec_bufT tempinbuf;
/** temporary rx buffer status */
int tempinbufs;
/** transmit buffers */
ec_bufT txbuf[EC_MAXBUF];
/** transmit buffer lengths */
int txbuflength[EC_MAXBUF];
/** temporary tx buffer */
ec_bufT txbuf2;
/** temporary tx buffer length */
int txbuflength2;
/** last used frame index */
uint8 lastidx;
/** current redundancy state */
int redstate;
/** pointer to redundancy port and buffers */
ecx_redportt *redport;
CRITICAL_SECTION getindex_mutex;
CRITICAL_SECTION tx_mutex;
CRITICAL_SECTION rx_mutex;
} ecx_portt;
extern const uint16 priMAC[3];
extern const uint16 secMAC[3];
#ifdef EC_VER1
extern ecx_portt ecx_port;
extern ecx_redportt ecx_redport;
int ec_setupnic(const char * ifname, int secondary);
int ec_closenic(void);
void ec_setbufstat(uint8 idx, int bufstat);
uint8 ec_getindex(void);
int ec_outframe(uint8 idx, int sock);
int ec_outframe_red(uint8 idx);
int ec_waitinframe(uint8 idx, int timeout);
int ec_srconfirm(uint8 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, uint8 idx, int bufstat);
uint8 ecx_getindex(ecx_portt *port);
int ecx_outframe(ecx_portt *port, uint8 idx, int sock);
int ecx_outframe_red(ecx_portt *port, uint8 idx);
int ecx_waitinframe(ecx_portt *port, uint8 idx, int timeout);
int ecx_srconfirm(ecx_portt *port, uint8 idx,int timeout);
#ifdef __cplusplus
}
#endif
#endif
+121
View File
@@ -0,0 +1,121 @@
/*
* Licensed under the GNU General Public License version 2 with exceptions. See
* LICENSE file in the project root for full license information
*/
#include "oshw.h"
#include <stdlib.h>
/**
* 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)
{
int i = 0;
pcap_if_t *alldevs;
pcap_if_t *d;
ec_adaptert * adapter;
ec_adaptert * prev_adapter;
ec_adaptert * ret_adapter = NULL;
char errbuf[PCAP_ERRBUF_SIZE];
/* find all devices */
if (pcap_findalldevs(&alldevs, errbuf) == -1)
{
fprintf(stderr,"Error in pcap_findalldevs_ex: %s\n", errbuf);
return (NULL);
}
/* Iterate all devices and create a local copy holding the name and
* description.
*/
for(d= alldevs; d != NULL; d= d->next)
{
adapter = (ec_adaptert *)malloc(sizeof(ec_adaptert));
/* If we got more than one adapter save link list pointer to previous
* adapter.
* Else save as pointer to return.
*/
if (i)
{
prev_adapter->next = adapter;
}
else
{
ret_adapter = adapter;
}
/* fetch description and name of the device from libpcap */
adapter->next = NULL;
if (d->name)
{
strncpy(adapter->name, d->name, EC_MAXLEN_ADAPTERNAME);
adapter->name[EC_MAXLEN_ADAPTERNAME-1] = '\0';
}
else
{
adapter->name[0] = '\0';
}
if (d->description)
{
strncpy(adapter->desc, d->description, EC_MAXLEN_ADAPTERNAME);
adapter->desc[EC_MAXLEN_ADAPTERNAME-1] = '\0';
}
else
{
adapter->desc[0] = '\0';
}
prev_adapter = adapter;
i++;
}
/* free all devices allocated */
pcap_freealldevs(alldevs);
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)
{
ec_adaptert * next_adapter;
/* Iterate the linked list and free all elements holding
* adapter information
*/
if(adapter)
{
next_adapter = adapter->next;
free (adapter);
while (next_adapter)
{
adapter = next_adapter;
next_adapter = adapter->next;
free (adapter);
}
}
}
+32
View File
@@ -0,0 +1,32 @@
/*
* Licensed under the GNU General Public License version 2 with exceptions. See
* LICENSE file in the project root for full license information
*/
/** \file
* \brief
* Headerfile for 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
@@ -0,0 +1,359 @@
/*
* Copyright (c) 1999 - 2005 NetGroup, Politecnico di Torino (Italy)
* Copyright (c) 2005 - 2007 CACE Technologies, Davis (California)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the Politecnico di Torino, CACE Technologies
* nor the names of its contributors may be used to endorse or promote
* products derived from this software without specific prior written
* permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
/** @ingroup packetapi
* @{
*/
/** @defgroup packet32h Packet.dll definitions and data structures
* Packet32.h contains the data structures and the definitions used by packet.dll.
* The file is used both by the Win9x and the WinNTx versions of packet.dll, and can be included
* by the applications that use the functions of this library
* @{
*/
#ifndef __PACKET32
#define __PACKET32
#include <winsock2.h>
#ifdef HAVE_AIRPCAP_API
#include <airpcap.h>
#else
#if !defined(AIRPCAP_HANDLE__EAE405F5_0171_9592_B3C2_C19EC426AD34__DEFINED_)
#define AIRPCAP_HANDLE__EAE405F5_0171_9592_B3C2_C19EC426AD34__DEFINED_
typedef struct _AirpcapHandle *PAirpcapHandle;
#endif /* AIRPCAP_HANDLE__EAE405F5_0171_9592_B3C2_C19EC426AD34__DEFINED_ */
#endif /* HAVE_AIRPCAP_API */
#ifdef HAVE_DAG_API
#include <dagc.h>
#endif /* HAVE_DAG_API */
// Working modes
#define PACKET_MODE_CAPT 0x0 ///< Capture mode
#define PACKET_MODE_STAT 0x1 ///< Statistical mode
#define PACKET_MODE_MON 0x2 ///< Monitoring mode
#define PACKET_MODE_DUMP 0x10 ///< Dump mode
#define PACKET_MODE_STAT_DUMP MODE_DUMP | MODE_STAT ///< Statistical dump Mode
/// Alignment macro. Defines the alignment size.
#define Packet_ALIGNMENT sizeof(int)
/// Alignment macro. Rounds up to the next even multiple of Packet_ALIGNMENT.
#define Packet_WORDALIGN(x) (((x)+(Packet_ALIGNMENT-1))&~(Packet_ALIGNMENT-1))
#define NdisMediumNull -1 ///< Custom linktype: NDIS doesn't provide an equivalent
#define NdisMediumCHDLC -2 ///< Custom linktype: NDIS doesn't provide an equivalent
#define NdisMediumPPPSerial -3 ///< Custom linktype: NDIS doesn't provide an equivalent
#define NdisMediumBare80211 -4 ///< Custom linktype: NDIS doesn't provide an equivalent
#define NdisMediumRadio80211 -5 ///< Custom linktype: NDIS doesn't provide an equivalent
#define NdisMediumPpi -6 ///< Custom linktype: NDIS doesn't provide an equivalent
// Loopback behaviour definitions
#define NPF_DISABLE_LOOPBACK 1 ///< Drop the packets sent by the NPF driver
#define NPF_ENABLE_LOOPBACK 2 ///< Capture the packets sent by the NPF driver
/*!
\brief Network type structure.
This structure is used by the PacketGetNetType() function to return information on the current adapter's type and speed.
*/
typedef struct NetType
{
UINT LinkType; ///< The MAC of the current network adapter (see function PacketGetNetType() for more information)
ULONGLONG LinkSpeed; ///< The speed of the network in bits per second
}NetType;
//some definitions stolen from libpcap
#ifndef BPF_MAJOR_VERSION
/*!
\brief A BPF pseudo-assembly program.
The program will be injected in the kernel by the PacketSetBPF() function and applied to every incoming packet.
*/
struct bpf_program
{
UINT bf_len; ///< Indicates the number of instructions of the program, i.e. the number of struct bpf_insn that will follow.
struct bpf_insn *bf_insns; ///< A pointer to the first instruction of the program.
};
/*!
\brief A single BPF pseudo-instruction.
bpf_insn contains a single instruction for the BPF register-machine. It is used to send a filter program to the driver.
*/
struct bpf_insn
{
USHORT code; ///< Instruction type and addressing mode.
UCHAR jt; ///< Jump if true
UCHAR jf; ///< Jump if false
int k; ///< Generic field used for various purposes.
};
/*!
\brief Structure that contains a couple of statistics values on the current capture.
It is used by packet.dll to return statistics about a capture session.
*/
struct bpf_stat
{
UINT bs_recv; ///< Number of packets that the driver received from the network adapter
///< from the beginning of the current capture. This value includes the packets
///< lost by the driver.
UINT bs_drop; ///< number of packets that the driver lost from the beginning of a capture.
///< Basically, a packet is lost when the the buffer of the driver is full.
///< In this situation the packet cannot be stored and the driver rejects it.
UINT ps_ifdrop; ///< drops by interface. XXX not yet supported
UINT bs_capt; ///< number of packets that pass the filter, find place in the kernel buffer and
///< thus reach the application.
};
/*!
\brief Packet header.
This structure defines the header associated with every packet delivered to the application.
*/
struct bpf_hdr
{
struct timeval bh_tstamp; ///< The timestamp associated with the captured packet.
///< It is stored in a TimeVal structure.
UINT bh_caplen; ///< Length of captured portion. The captured portion <b>can be different</b>
///< from the original packet, because it is possible (with a proper filter)
///< to instruct the driver to capture only a portion of the packets.
UINT bh_datalen; ///< Original length of packet
USHORT bh_hdrlen; ///< Length of bpf header (this struct plus alignment padding). In some cases,
///< a padding could be added between the end of this structure and the packet
///< data for performance reasons. This filed can be used to retrieve the actual data
///< of the packet.
};
/*!
\brief Dump packet header.
This structure defines the header associated with the packets in a buffer to be used with PacketSendPackets().
It is simpler than the bpf_hdr, because it corresponds to the header associated by WinPcap and libpcap to a
packet in a dump file. This makes straightforward sending WinPcap dump files to the network.
*/
struct dump_bpf_hdr{
struct timeval ts; ///< Time stamp of the packet
UINT caplen; ///< Length of captured portion. The captured portion can smaller than the
///< the original packet, because it is possible (with a proper filter) to
///< instruct the driver to capture only a portion of the packets.
UINT len; ///< Length of the original packet (off wire).
};
#endif
struct bpf_stat;
#define DOSNAMEPREFIX TEXT("Packet_") ///< Prefix added to the adapters device names to create the WinPcap devices
#define MAX_LINK_NAME_LENGTH 64 //< Maximum length of the devices symbolic links
#define NMAX_PACKET 65535
/*!
\brief Addresses of a network adapter.
This structure is used by the PacketGetNetInfoEx() function to return the IP addresses associated with
an adapter.
*/
typedef struct npf_if_addr {
struct sockaddr_storage IPAddress; ///< IP address.
struct sockaddr_storage SubnetMask; ///< Netmask for that address.
struct sockaddr_storage Broadcast; ///< Broadcast address.
}npf_if_addr;
#define ADAPTER_NAME_LENGTH 256 + 12 ///< Maximum length for the name of an adapter. The value is the same used by the IP Helper API.
#define ADAPTER_DESC_LENGTH 128 ///< Maximum length for the description of an adapter. The value is the same used by the IP Helper API.
#define MAX_MAC_ADDR_LENGTH 8 ///< Maximum length for the link layer address of an adapter. The value is the same used by the IP Helper API.
#define MAX_NETWORK_ADDRESSES 16 ///< Maximum length for the link layer address of an adapter. The value is the same used by the IP Helper API.
typedef struct WAN_ADAPTER_INT WAN_ADAPTER; ///< Describes an opened wan (dialup, VPN...) network adapter using the NetMon API
typedef WAN_ADAPTER *PWAN_ADAPTER; ///< Describes an opened wan (dialup, VPN...) network adapter using the NetMon API
#define INFO_FLAG_NDIS_ADAPTER 0 ///< Flag for ADAPTER_INFO: this is a traditional ndis adapter
#define INFO_FLAG_NDISWAN_ADAPTER 1 ///< Flag for ADAPTER_INFO: this is a NdisWan adapter, and it's managed by WANPACKET
#define INFO_FLAG_DAG_CARD 2 ///< Flag for ADAPTER_INFO: this is a DAG card
#define INFO_FLAG_DAG_FILE 6 ///< Flag for ADAPTER_INFO: this is a DAG file
#define INFO_FLAG_DONT_EXPORT 8 ///< Flag for ADAPTER_INFO: when this flag is set, the adapter will not be listed or openend by winpcap. This allows to prevent exporting broken network adapters, like for example FireWire ones.
#define INFO_FLAG_AIRPCAP_CARD 16 ///< Flag for ADAPTER_INFO: this is an airpcap card
#define INFO_FLAG_NPFIM_DEVICE 32
/*!
\brief Describes an opened network adapter.
This structure is the most important for the functioning of packet.dll, but the great part of its fields
should be ignored by the user, since the library offers functions that avoid to cope with low-level parameters
*/
typedef struct _ADAPTER {
HANDLE hFile; ///< \internal Handle to an open instance of the NPF driver.
CHAR SymbolicLink[MAX_LINK_NAME_LENGTH]; ///< \internal A string containing the name of the network adapter currently opened.
int NumWrites; ///< \internal Number of times a packets written on this adapter will be repeated
///< on the wire.
HANDLE ReadEvent; ///< A notification event associated with the read calls on the adapter.
///< It can be passed to standard Win32 functions (like WaitForSingleObject
///< or WaitForMultipleObjects) to wait until the driver's buffer contains some
///< data. It is particularly useful in GUI applications that need to wait
///< concurrently on several events. In Windows NT/2000 the PacketSetMinToCopy()
///< function can be used to define the minimum amount of data in the kernel buffer
///< that will cause the event to be signalled.
UINT ReadTimeOut; ///< \internal The amount of time after which a read on the driver will be released and
///< ReadEvent will be signaled, also if no packets were captured
CHAR Name[ADAPTER_NAME_LENGTH];
PWAN_ADAPTER pWanAdapter;
UINT Flags; ///< Adapter's flags. Tell if this adapter must be treated in a different way, using the Netmon API or the dagc API.
#ifdef HAVE_AIRPCAP_API
PAirpcapHandle AirpcapAd;
#endif // HAVE_AIRPCAP_API
#ifdef HAVE_NPFIM_API
void* NpfImHandle;
#endif // HAVE_NPFIM_API
#ifdef HAVE_DAG_API
dagc_t *pDagCard; ///< Pointer to the dagc API adapter descriptor for this adapter
PCHAR DagBuffer; ///< Pointer to the buffer with the packets that is received from the DAG card
struct timeval DagReadTimeout; ///< Read timeout. The dagc API requires a timeval structure
unsigned DagFcsLen; ///< Length of the frame check sequence attached to any packet by the card. Obtained from the registry
DWORD DagFastProcess; ///< True if the user requests fast capture processing on this card. Higher level applications can use this value to provide a faster but possibly unprecise capture (for example, libpcap doesn't convert the timestamps).
#endif // HAVE_DAG_API
} ADAPTER, *LPADAPTER;
/*!
\brief Structure that contains a group of packets coming from the driver.
This structure defines the header associated with every packet delivered to the application.
*/
typedef struct _PACKET {
HANDLE hEvent; ///< \deprecated Still present for compatibility with old applications.
OVERLAPPED OverLapped; ///< \deprecated Still present for compatibility with old applications.
PVOID Buffer; ///< Buffer with containing the packets. See the PacketReceivePacket() for
///< details about the organization of the data in this buffer
UINT Length; ///< Length of the buffer
DWORD ulBytesReceived; ///< Number of valid bytes present in the buffer, i.e. amount of data
///< received by the last call to PacketReceivePacket()
BOOLEAN bIoComplete; ///< \deprecated Still present for compatibility with old applications.
} PACKET, *LPPACKET;
/*!
\brief Structure containing an OID request.
It is used by the PacketRequest() function to send an OID to the interface card driver.
It can be used, for example, to retrieve the status of the error counters on the adapter, its MAC address,
the list of the multicast groups defined on it, and so on.
*/
struct _PACKET_OID_DATA {
ULONG Oid; ///< OID code. See the Microsoft DDK documentation or the file ntddndis.h
///< for a complete list of valid codes.
ULONG Length; ///< Length of the data field
UCHAR Data[1]; ///< variable-length field that contains the information passed to or received
///< from the adapter.
};
typedef struct _PACKET_OID_DATA PACKET_OID_DATA, *PPACKET_OID_DATA;
#ifdef __cplusplus
extern "C" {
#endif
/**
* @}
*/
/*
BOOLEAN QueryWinPcapRegistryStringA(CHAR *SubKeyName,
CHAR *Value,
UINT *pValueLen,
CHAR *DefaultVal);
BOOLEAN QueryWinPcapRegistryStringW(WCHAR *SubKeyName,
WCHAR *Value,
UINT *pValueLen,
WCHAR *DefaultVal);
*/
//---------------------------------------------------------------------------
// EXPORTED FUNCTIONS
//---------------------------------------------------------------------------
PCHAR PacketGetVersion();
PCHAR PacketGetDriverVersion();
BOOLEAN PacketSetMinToCopy(LPADAPTER AdapterObject,int nbytes);
BOOLEAN PacketSetNumWrites(LPADAPTER AdapterObject,int nwrites);
BOOLEAN PacketSetMode(LPADAPTER AdapterObject,int mode);
BOOLEAN PacketSetReadTimeout(LPADAPTER AdapterObject,int timeout);
BOOLEAN PacketSetBpf(LPADAPTER AdapterObject,struct bpf_program *fp);
BOOLEAN PacketSetLoopbackBehavior(LPADAPTER AdapterObject, UINT LoopbackBehavior);
INT PacketSetSnapLen(LPADAPTER AdapterObject,int snaplen);
BOOLEAN PacketGetStats(LPADAPTER AdapterObject,struct bpf_stat *s);
BOOLEAN PacketGetStatsEx(LPADAPTER AdapterObject,struct bpf_stat *s);
BOOLEAN PacketSetBuff(LPADAPTER AdapterObject,int dim);
BOOLEAN PacketGetNetType (LPADAPTER AdapterObject,NetType *type);
LPADAPTER PacketOpenAdapter(PCHAR AdapterName);
BOOLEAN PacketSendPacket(LPADAPTER AdapterObject,LPPACKET pPacket,BOOLEAN Sync);
INT PacketSendPackets(LPADAPTER AdapterObject,PVOID PacketBuff,ULONG Size, BOOLEAN Sync);
LPPACKET PacketAllocatePacket(void);
VOID PacketInitPacket(LPPACKET lpPacket,PVOID Buffer,UINT Length);
VOID PacketFreePacket(LPPACKET lpPacket);
BOOLEAN PacketReceivePacket(LPADAPTER AdapterObject,LPPACKET lpPacket,BOOLEAN Sync);
BOOLEAN PacketSetHwFilter(LPADAPTER AdapterObject,ULONG Filter);
BOOLEAN PacketGetAdapterNames(PTSTR pStr,PULONG BufferSize);
BOOLEAN PacketGetNetInfoEx(PCHAR AdapterName, npf_if_addr* buffer, PLONG NEntries);
BOOLEAN PacketRequest(LPADAPTER AdapterObject,BOOLEAN Set,PPACKET_OID_DATA OidData);
HANDLE PacketGetReadEvent(LPADAPTER AdapterObject);
BOOLEAN PacketSetDumpName(LPADAPTER AdapterObject, void *name, int len);
BOOLEAN PacketSetDumpLimits(LPADAPTER AdapterObject, UINT maxfilesize, UINT maxnpacks);
BOOLEAN PacketIsDumpEnded(LPADAPTER AdapterObject, BOOLEAN sync);
BOOL PacketStopDriver();
VOID PacketCloseAdapter(LPADAPTER lpAdapter);
BOOLEAN PacketStartOem(PCHAR errorString, UINT errorStringLength);
BOOLEAN PacketStartOemEx(PCHAR errorString, UINT errorStringLength, ULONG flags);
PAirpcapHandle PacketGetAirPcapHandle(LPADAPTER AdapterObject);
//
// Used by PacketStartOemEx
//
#define PACKET_START_OEM_NO_NETMON 0x00000001
#ifdef __cplusplus
}
#endif
#endif //__PACKET32
@@ -0,0 +1,113 @@
/*
* Copyright (c) 1999 - 2005 NetGroup, Politecnico di Torino (Italy)
* Copyright (c) 2005 - 2006 CACE Technologies, Davis (California)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the Politecnico di Torino, CACE Technologies
* nor the names of its contributors may be used to endorse or promote
* products derived from this software without specific prior written
* permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef __WIN32_EXTENSIONS_H__
#define __WIN32_EXTENSIONS_H__
#ifdef __cplusplus
extern "C" {
#endif
/* Definitions */
/*!
\brief A queue of raw packets that will be sent to the network with pcap_sendqueue_transmit().
*/
struct pcap_send_queue
{
u_int maxlen; ///< Maximum size of the the queue, in bytes. This variable contains the size of the buffer field.
u_int len; ///< Current size of the queue, in bytes.
char *buffer; ///< Buffer containing the packets to be sent.
};
typedef struct pcap_send_queue pcap_send_queue;
/*!
\brief This typedef is a support for the pcap_get_airpcap_handle() function
*/
#if !defined(AIRPCAP_HANDLE__EAE405F5_0171_9592_B3C2_C19EC426AD34__DEFINED_)
#define AIRPCAP_HANDLE__EAE405F5_0171_9592_B3C2_C19EC426AD34__DEFINED_
typedef struct _AirpcapHandle *PAirpcapHandle;
#endif
#define BPF_MEM_EX_IMM 0xc0
#define BPF_MEM_EX_IND 0xe0
/*used for ST*/
#define BPF_MEM_EX 0xc0
#define BPF_TME 0x08
#define BPF_LOOKUP 0x90
#define BPF_EXECUTE 0xa0
#define BPF_INIT 0xb0
#define BPF_VALIDATE 0xc0
#define BPF_SET_ACTIVE 0xd0
#define BPF_RESET 0xe0
#define BPF_SET_MEMORY 0x80
#define BPF_GET_REGISTER_VALUE 0x70
#define BPF_SET_REGISTER_VALUE 0x60
#define BPF_SET_WORKING 0x50
#define BPF_SET_ACTIVE_READ 0x40
#define BPF_SET_AUTODELETION 0x30
#define BPF_SEPARATION 0xff
/* Prototypes */
pcap_send_queue* pcap_sendqueue_alloc(u_int memsize);
void pcap_sendqueue_destroy(pcap_send_queue* queue);
int pcap_sendqueue_queue(pcap_send_queue* queue, const struct pcap_pkthdr *pkt_header, const u_char *pkt_data);
u_int pcap_sendqueue_transmit(pcap_t *p, pcap_send_queue* queue, int sync);
HANDLE pcap_getevent(pcap_t *p);
struct pcap_stat *pcap_stats_ex(pcap_t *p, int *pcap_stat_size);
int pcap_setuserbuffer(pcap_t *p, int size);
int pcap_live_dump(pcap_t *p, char *filename, int maxsize, int maxpacks);
int pcap_live_dump_ended(pcap_t *p, int sync);
int pcap_offline_filter(struct bpf_program *prog, const struct pcap_pkthdr *header, const u_char *pkt_data);
int pcap_start_oem(char* err_str, int flags);
PAirpcapHandle pcap_get_airpcap_handle(pcap_t *p);
#ifdef __cplusplus
}
#endif
#endif //__WIN32_EXTENSIONS_H__
@@ -0,0 +1,137 @@
/*
* Copyright (C) 1999 WIDE Project.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the project nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef _BITTYPES_H
#define _BITTYPES_H
#ifndef HAVE_U_INT8_T
#if SIZEOF_CHAR == 1
typedef unsigned char u_int8_t;
typedef signed char int8_t;
#elif SIZEOF_INT == 1
typedef unsigned int u_int8_t;
typedef signed int int8_t;
#else /* XXX */
#error "there's no appropriate type for u_int8_t"
#endif
#define HAVE_U_INT8_T 1
#define HAVE_INT8_T 1
#endif /* HAVE_U_INT8_T */
#ifndef HAVE_U_INT16_T
#if SIZEOF_SHORT == 2
typedef unsigned short u_int16_t;
typedef signed short int16_t;
#elif SIZEOF_INT == 2
typedef unsigned int u_int16_t;
typedef signed int int16_t;
#elif SIZEOF_CHAR == 2
typedef unsigned char u_int16_t;
typedef signed char int16_t;
#else /* XXX */
#error "there's no appropriate type for u_int16_t"
#endif
#define HAVE_U_INT16_T 1
#define HAVE_INT16_T 1
#endif /* HAVE_U_INT16_T */
#ifndef HAVE_U_INT32_T
#if SIZEOF_INT == 4
typedef unsigned int u_int32_t;
typedef signed int int32_t;
#elif SIZEOF_LONG == 4
typedef unsigned long u_int32_t;
typedef signed long int32_t;
#elif SIZEOF_SHORT == 4
typedef unsigned short u_int32_t;
typedef signed short int32_t;
#else /* XXX */
#error "there's no appropriate type for u_int32_t"
#endif
#define HAVE_U_INT32_T 1
#define HAVE_INT32_T 1
#endif /* HAVE_U_INT32_T */
#ifndef HAVE_U_INT64_T
#if SIZEOF_LONG_LONG == 8
typedef unsigned long long u_int64_t;
typedef long long int64_t;
#elif defined(_MSC_EXTENSIONS)
typedef unsigned _int64 u_int64_t;
typedef _int64 int64_t;
#elif SIZEOF_INT == 8
typedef unsigned int u_int64_t;
#elif SIZEOF_LONG == 8
typedef unsigned long u_int64_t;
#elif SIZEOF_SHORT == 8
typedef unsigned short u_int64_t;
#else /* XXX */
#error "there's no appropriate type for u_int64_t"
#endif
#endif /* HAVE_U_INT64_T */
#ifndef PRId64
#ifdef _MSC_EXTENSIONS
#define PRId64 "I64d"
#else /* _MSC_EXTENSIONS */
#define PRId64 "lld"
#endif /* _MSC_EXTENSIONS */
#endif /* PRId64 */
#ifndef PRIo64
#ifdef _MSC_EXTENSIONS
#define PRIo64 "I64o"
#else /* _MSC_EXTENSIONS */
#define PRIo64 "llo"
#endif /* _MSC_EXTENSIONS */
#endif /* PRIo64 */
#ifndef PRIx64
#ifdef _MSC_EXTENSIONS
#define PRIx64 "I64x"
#else /* _MSC_EXTENSIONS */
#define PRIx64 "llx"
#endif /* _MSC_EXTENSIONS */
#endif /* PRIx64 */
#ifndef PRIu64
#ifdef _MSC_EXTENSIONS
#define PRIu64 "I64u"
#else /* _MSC_EXTENSIONS */
#define PRIu64 "llu"
#endif /* _MSC_EXTENSIONS */
#endif /* PRIu64 */
#endif /* _BITTYPES_H */
@@ -0,0 +1,163 @@
/*
* Copyright (c) 1993, 1994, 1997
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that: (1) source code distributions
* retain the above copyright notice and this paragraph in its entirety, (2)
* distributions including binary code include the above copyright notice and
* this paragraph in its entirety in the documentation or other materials
* provided with the distribution, and (3) all advertising materials mentioning
* features or use of this software display the following acknowledgement:
* ``This product includes software developed by the University of California,
* Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
* the University nor the names of its contributors may be used to endorse
* or promote products derived from this software without specific prior
* written permission.
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*
* @(#) $Header: /tcpdump/master/libpcap/Win32/Include/ip6_misc.h,v 1.5 2006-01-22 18:02:18 gianluca Exp $ (LBL)
*/
/*
* This file contains a collage of declarations for IPv6 from FreeBSD not present in Windows
*/
#include <winsock2.h>
#include <ws2tcpip.h>
#ifndef __MINGW32__
#define IN_MULTICAST(a) IN_CLASSD(a)
#endif
#define IN_EXPERIMENTAL(a) ((((u_int32_t) (a)) & 0xf0000000) == 0xf0000000)
#define IN_LOOPBACKNET 127
#if defined(__MINGW32__) && defined(DEFINE_ADDITIONAL_IPV6_STUFF)
/* IPv6 address */
struct in6_addr
{
union
{
u_int8_t u6_addr8[16];
u_int16_t u6_addr16[8];
u_int32_t u6_addr32[4];
} in6_u;
#define s6_addr in6_u.u6_addr8
#define s6_addr16 in6_u.u6_addr16
#define s6_addr32 in6_u.u6_addr32
#define s6_addr64 in6_u.u6_addr64
};
#define IN6ADDR_ANY_INIT { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }
#define IN6ADDR_LOOPBACK_INIT { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 }
#endif /* __MINGW32__ */
#if (defined _MSC_VER) || (defined(__MINGW32__) && defined(DEFINE_ADDITIONAL_IPV6_STUFF))
typedef unsigned short sa_family_t;
#endif
#if defined(__MINGW32__) && defined(DEFINE_ADDITIONAL_IPV6_STUFF)
#define __SOCKADDR_COMMON(sa_prefix) \
sa_family_t sa_prefix##family
/* Ditto, for IPv6. */
struct sockaddr_in6
{
__SOCKADDR_COMMON (sin6_);
u_int16_t sin6_port; /* Transport layer port # */
u_int32_t sin6_flowinfo; /* IPv6 flow information */
struct in6_addr sin6_addr; /* IPv6 address */
};
#define IN6_IS_ADDR_V4MAPPED(a) \
((((u_int32_t *) (a))[0] == 0) && (((u_int32_t *) (a))[1] == 0) && \
(((u_int32_t *) (a))[2] == htonl (0xffff)))
#define IN6_IS_ADDR_MULTICAST(a) (((u_int8_t *) (a))[0] == 0xff)
#define IN6_IS_ADDR_LINKLOCAL(a) \
((((u_int32_t *) (a))[0] & htonl (0xffc00000)) == htonl (0xfe800000))
#define IN6_IS_ADDR_LOOPBACK(a) \
(((u_int32_t *) (a))[0] == 0 && ((u_int32_t *) (a))[1] == 0 && \
((u_int32_t *) (a))[2] == 0 && ((u_int32_t *) (a))[3] == htonl (1))
#endif /* __MINGW32__ */
#define ip6_vfc ip6_ctlun.ip6_un2_vfc
#define ip6_flow ip6_ctlun.ip6_un1.ip6_un1_flow
#define ip6_plen ip6_ctlun.ip6_un1.ip6_un1_plen
#define ip6_nxt ip6_ctlun.ip6_un1.ip6_un1_nxt
#define ip6_hlim ip6_ctlun.ip6_un1.ip6_un1_hlim
#define ip6_hops ip6_ctlun.ip6_un1.ip6_un1_hlim
#define nd_rd_type nd_rd_hdr.icmp6_type
#define nd_rd_code nd_rd_hdr.icmp6_code
#define nd_rd_cksum nd_rd_hdr.icmp6_cksum
#define nd_rd_reserved nd_rd_hdr.icmp6_data32[0]
/*
* IPV6 extension headers
*/
#define IPPROTO_HOPOPTS 0 /* IPv6 hop-by-hop options */
#define IPPROTO_IPV6 41 /* IPv6 header. */
#define IPPROTO_ROUTING 43 /* IPv6 routing header */
#define IPPROTO_FRAGMENT 44 /* IPv6 fragmentation header */
#define IPPROTO_ESP 50 /* encapsulating security payload */
#define IPPROTO_AH 51 /* authentication header */
#define IPPROTO_ICMPV6 58 /* ICMPv6 */
#define IPPROTO_NONE 59 /* IPv6 no next header */
#define IPPROTO_DSTOPTS 60 /* IPv6 destination options */
#define IPPROTO_PIM 103 /* Protocol Independent Multicast. */
#define IPV6_RTHDR_TYPE_0 0
/* Option types and related macros */
#define IP6OPT_PAD1 0x00 /* 00 0 00000 */
#define IP6OPT_PADN 0x01 /* 00 0 00001 */
#define IP6OPT_JUMBO 0xC2 /* 11 0 00010 = 194 */
#define IP6OPT_JUMBO_LEN 6
#define IP6OPT_ROUTER_ALERT 0x05 /* 00 0 00101 */
#define IP6OPT_RTALERT_LEN 4
#define IP6OPT_RTALERT_MLD 0 /* Datagram contains an MLD message */
#define IP6OPT_RTALERT_RSVP 1 /* Datagram contains an RSVP message */
#define IP6OPT_RTALERT_ACTNET 2 /* contains an Active Networks msg */
#define IP6OPT_MINLEN 2
#define IP6OPT_BINDING_UPDATE 0xc6 /* 11 0 00110 */
#define IP6OPT_BINDING_ACK 0x07 /* 00 0 00111 */
#define IP6OPT_BINDING_REQ 0x08 /* 00 0 01000 */
#define IP6OPT_HOME_ADDRESS 0xc9 /* 11 0 01001 */
#define IP6OPT_EID 0x8a /* 10 0 01010 */
#define IP6OPT_TYPE(o) ((o) & 0xC0)
#define IP6OPT_TYPE_SKIP 0x00
#define IP6OPT_TYPE_DISCARD 0x40
#define IP6OPT_TYPE_FORCEICMP 0x80
#define IP6OPT_TYPE_ICMP 0xC0
#define IP6OPT_MUTABLE 0x20
#if defined(__MINGW32__) && defined(DEFINE_ADDITIONAL_IPV6_STUFF)
#ifndef EAI_ADDRFAMILY
struct addrinfo {
int ai_flags; /* AI_PASSIVE, AI_CANONNAME */
int ai_family; /* PF_xxx */
int ai_socktype; /* SOCK_xxx */
int ai_protocol; /* 0 or IPPROTO_xxx for IPv4 and IPv6 */
size_t ai_addrlen; /* length of ai_addr */
char *ai_canonname; /* canonical name for hostname */
struct sockaddr *ai_addr; /* binary address */
struct addrinfo *ai_next; /* next structure in linked list */
};
#endif
#endif /* __MINGW32__ */
@@ -0,0 +1,47 @@
/*-
* Copyright (c) 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997
* The Regents of the University of California. All rights reserved.
*
* This code is derived from the Stanford/CMU enet packet filter,
* (net/enet.c) distributed as part of 4.3BSD, and code contributed
* to Berkeley by Steven McCanne and Van Jacobson both of Lawrence
* Berkeley Laboratory.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#) $Header: /tcpdump/master/libpcap/pcap-bpf.h,v 1.50 2007/04/01 21:43:55 guy Exp $ (LBL)
*/
/*
* For backwards compatibility.
*
* Note to OS vendors: do NOT get rid of this file! Some applications
* might expect to be able to include <pcap-bpf.h>.
*/
#include <pcap/bpf.h>
@@ -0,0 +1,42 @@
/*
* Copyright (c) 1994, 1996
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the Computer Systems
* Engineering Group at Lawrence Berkeley Laboratory.
* 4. Neither the name of the University nor of the Laboratory may be used
* to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#) $Header: /tcpdump/master/libpcap/pcap-namedb.h,v 1.13 2006/10/04 18:13:32 guy Exp $ (LBL)
*/
/*
* For backwards compatibility.
*
* Note to OS vendors: do NOT get rid of this file! Some applications
* might expect to be able to include <pcap-namedb.h>.
*/
#include <pcap/namedb.h>
@@ -0,0 +1,93 @@
/*
* Copyright (c) 2002 - 2005 NetGroup, Politecnico di Torino (Italy)
* Copyright (c) 2005 - 2009 CACE Technologies, Inc. Davis (California)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the Politecnico di Torino nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* @(#) $Header: /tcpdump/master/libpcap/pcap-stdinc.h,v 1.10.2.1 2008-10-06 15:38:39 gianluca Exp $ (LBL)
*/
#define SIZEOF_CHAR 1
#define SIZEOF_SHORT 2
#define SIZEOF_INT 4
#ifndef _MSC_EXTENSIONS
#define SIZEOF_LONG_LONG 8
#endif
/*
* Avoids a compiler warning in case this was already defined
* (someone defined _WINSOCKAPI_ when including 'windows.h', in order
* to prevent it from including 'winsock.h')
*/
#ifdef _WINSOCKAPI_
#undef _WINSOCKAPI_
#endif
#include <winsock2.h>
#include <fcntl.h>
#include "bittypes.h"
#include <time.h>
#include <io.h>
#ifndef __MINGW32__
#include "IP6_misc.h"
#endif
#define caddr_t char*
#if _MSC_VER < 1500
#define snprintf _snprintf
#define vsnprintf _vsnprintf
#define strdup _strdup
#endif
#define inline __inline
#ifdef __MINGW32__
#include <stdint.h>
#else /*__MINGW32__*/
/* MSVC compiler */
#ifndef _UINTPTR_T_DEFINED
#ifdef _WIN64
typedef unsigned __int64 uintptr_t;
#else
typedef _W64 unsigned int uintptr_t;
#endif
#define _UINTPTR_T_DEFINED
#endif
#ifndef _INTPTR_T_DEFINED
#ifdef _WIN64
typedef __int64 intptr_t;
#else
typedef _W64 int intptr_t;
#endif
#define _INTPTR_T_DEFINED
#endif
#endif /*__MINGW32__*/
+45
View File
@@ -0,0 +1,45 @@
/*
* Copyright (c) 1993, 1994, 1995, 1996, 1997
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the Computer Systems
* Engineering Group at Lawrence Berkeley Laboratory.
* 4. Neither the name of the University nor of the Laboratory may be used
* to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#) $Header: /tcpdump/master/libpcap/pcap.h,v 1.59 2006/10/04 18:09:22 guy Exp $ (LBL)
*/
/*
* For backwards compatibility.
*
* Note to OS vendors: do NOT get rid of this file! Many applications
* expect to be able to include <pcap.h>, and at least some of them
* go through contortions in their configure scripts to try to detect
* OSes that have "helpfully" moved pcap.h to <pcap/pcap.h> without
* leaving behind a <pcap.h> file.
*/
#include <pcap/pcap.h>
@@ -0,0 +1,48 @@
/*
* Copyright (c) 2006 Paolo Abeni (Italy)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote
* products derived from this software without specific prior written
* permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* bluetooth data struct
* By Paolo Abeni <paolo.abeni@email.it>
*
* @(#) $Header: /tcpdump/master/libpcap/pcap/bluetooth.h,v 1.1 2007/09/22 02:10:17 guy Exp $
*/
#ifndef _PCAP_BLUETOOTH_STRUCTS_H__
#define _PCAP_BLUETOOTH_STRUCTS_H__
/*
* Header prepended libpcap to each bluetooth h:4 frame.
* fields are in network byte order
*/
typedef struct _pcap_bluetooth_h4_header {
u_int32_t direction; /* if first bit is set direction is incoming */
} pcap_bluetooth_h4_header;
#endif
@@ -0,0 +1,934 @@
/*-
* Copyright (c) 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997
* The Regents of the University of California. All rights reserved.
*
* This code is derived from the Stanford/CMU enet packet filter,
* (net/enet.c) distributed as part of 4.3BSD, and code contributed
* to Berkeley by Steven McCanne and Van Jacobson both of Lawrence
* Berkeley Laboratory.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)bpf.h 7.1 (Berkeley) 5/7/91
*
* @(#) $Header: /tcpdump/master/libpcap/pcap/bpf.h,v 1.19.2.8 2008-09-22 20:16:01 guy Exp $ (LBL)
*/
/*
* This is libpcap's cut-down version of bpf.h; it includes only
* the stuff needed for the code generator and the userland BPF
* interpreter, and the libpcap APIs for setting filters, etc..
*
* "pcap-bpf.c" will include the native OS version, as it deals with
* the OS's BPF implementation.
*
* XXX - should this all just be moved to "pcap.h"?
*/
#ifndef BPF_MAJOR_VERSION
#ifdef __cplusplus
extern "C" {
#endif
/* BSD style release date */
#define BPF_RELEASE 199606
#ifdef MSDOS /* must be 32-bit */
typedef long bpf_int32;
typedef unsigned long bpf_u_int32;
#else
typedef int bpf_int32;
typedef u_int bpf_u_int32;
#endif
/*
* Alignment macros. BPF_WORDALIGN rounds up to the next
* even multiple of BPF_ALIGNMENT.
*/
#ifndef __NetBSD__
#define BPF_ALIGNMENT sizeof(bpf_int32)
#else
#define BPF_ALIGNMENT sizeof(long)
#endif
#define BPF_WORDALIGN(x) (((x)+(BPF_ALIGNMENT-1))&~(BPF_ALIGNMENT-1))
#define BPF_MAXBUFSIZE 0x8000
#define BPF_MINBUFSIZE 32
/*
* Structure for "pcap_compile()", "pcap_setfilter()", etc..
*/
struct bpf_program {
u_int bf_len;
struct bpf_insn *bf_insns;
};
/*
* Struct return by BIOCVERSION. This represents the version number of
* the filter language described by the instruction encodings below.
* bpf understands a program iff kernel_major == filter_major &&
* kernel_minor >= filter_minor, that is, if the value returned by the
* running kernel has the same major number and a minor number equal
* equal to or less than the filter being downloaded. Otherwise, the
* results are undefined, meaning an error may be returned or packets
* may be accepted haphazardly.
* It has nothing to do with the source code version.
*/
struct bpf_version {
u_short bv_major;
u_short bv_minor;
};
/* Current version number of filter architecture. */
#define BPF_MAJOR_VERSION 1
#define BPF_MINOR_VERSION 1
/*
* Data-link level type codes.
*
* Do *NOT* add new values to this list without asking
* "tcpdump-workers@lists.tcpdump.org" for a value. Otherwise, you run
* the risk of using a value that's already being used for some other
* purpose, and of having tools that read libpcap-format captures not
* being able to handle captures with your new DLT_ value, with no hope
* that they will ever be changed to do so (as that would destroy their
* ability to read captures using that value for that other purpose).
*/
/*
* These are the types that are the same on all platforms, and that
* have been defined by <net/bpf.h> for ages.
*/
#define DLT_NULL 0 /* BSD loopback encapsulation */
#define DLT_EN10MB 1 /* Ethernet (10Mb) */
#define DLT_EN3MB 2 /* Experimental Ethernet (3Mb) */
#define DLT_AX25 3 /* Amateur Radio AX.25 */
#define DLT_PRONET 4 /* Proteon ProNET Token Ring */
#define DLT_CHAOS 5 /* Chaos */
#define DLT_IEEE802 6 /* 802.5 Token Ring */
#define DLT_ARCNET 7 /* ARCNET, with BSD-style header */
#define DLT_SLIP 8 /* Serial Line IP */
#define DLT_PPP 9 /* Point-to-point Protocol */
#define DLT_FDDI 10 /* FDDI */
/*
* These are types that are different on some platforms, and that
* have been defined by <net/bpf.h> for ages. We use #ifdefs to
* detect the BSDs that define them differently from the traditional
* libpcap <net/bpf.h>
*
* XXX - DLT_ATM_RFC1483 is 13 in BSD/OS, and DLT_RAW is 14 in BSD/OS,
* but I don't know what the right #define is for BSD/OS.
*/
#define DLT_ATM_RFC1483 11 /* LLC-encapsulated ATM */
#ifdef __OpenBSD__
#define DLT_RAW 14 /* raw IP */
#else
#define DLT_RAW 12 /* raw IP */
#endif
/*
* Given that the only OS that currently generates BSD/OS SLIP or PPP
* is, well, BSD/OS, arguably everybody should have chosen its values
* for DLT_SLIP_BSDOS and DLT_PPP_BSDOS, which are 15 and 16, but they
* didn't. So it goes.
*/
#if defined(__NetBSD__) || defined(__FreeBSD__)
#ifndef DLT_SLIP_BSDOS
#define DLT_SLIP_BSDOS 13 /* BSD/OS Serial Line IP */
#define DLT_PPP_BSDOS 14 /* BSD/OS Point-to-point Protocol */
#endif
#else
#define DLT_SLIP_BSDOS 15 /* BSD/OS Serial Line IP */
#define DLT_PPP_BSDOS 16 /* BSD/OS Point-to-point Protocol */
#endif
/*
* 17 is used for DLT_OLD_PFLOG in OpenBSD;
* OBSOLETE: DLT_PFLOG is 117 in OpenBSD now as well. See below.
* 18 is used for DLT_PFSYNC in OpenBSD; don't use it for anything else.
*/
#define DLT_ATM_CLIP 19 /* Linux Classical-IP over ATM */
/*
* Apparently Redback uses this for its SmartEdge 400/800. I hope
* nobody else decided to use it, too.
*/
#define DLT_REDBACK_SMARTEDGE 32
/*
* These values are defined by NetBSD; other platforms should refrain from
* using them for other purposes, so that NetBSD savefiles with link
* types of 50 or 51 can be read as this type on all platforms.
*/
#define DLT_PPP_SERIAL 50 /* PPP over serial with HDLC encapsulation */
#define DLT_PPP_ETHER 51 /* PPP over Ethernet */
/*
* The Axent Raptor firewall - now the Symantec Enterprise Firewall - uses
* a link-layer type of 99 for the tcpdump it supplies. The link-layer
* header has 6 bytes of unknown data, something that appears to be an
* Ethernet type, and 36 bytes that appear to be 0 in at least one capture
* I've seen.
*/
#define DLT_SYMANTEC_FIREWALL 99
/*
* Values between 100 and 103 are used in capture file headers as
* link-layer types corresponding to DLT_ types that differ
* between platforms; don't use those values for new DLT_ new types.
*/
/*
* This value was defined by libpcap 0.5; platforms that have defined
* it with a different value should define it here with that value -
* a link type of 104 in a save file will be mapped to DLT_C_HDLC,
* whatever value that happens to be, so programs will correctly
* handle files with that link type regardless of the value of
* DLT_C_HDLC.
*
* The name DLT_C_HDLC was used by BSD/OS; we use that name for source
* compatibility with programs written for BSD/OS.
*
* libpcap 0.5 defined it as DLT_CHDLC; we define DLT_CHDLC as well,
* for source compatibility with programs written for libpcap 0.5.
*/
#define DLT_C_HDLC 104 /* Cisco HDLC */
#define DLT_CHDLC DLT_C_HDLC
#define DLT_IEEE802_11 105 /* IEEE 802.11 wireless */
/*
* 106 is reserved for Linux Classical IP over ATM; it's like DLT_RAW,
* except when it isn't. (I.e., sometimes it's just raw IP, and
* sometimes it isn't.) We currently handle it as DLT_LINUX_SLL,
* so that we don't have to worry about the link-layer header.)
*/
/*
* Frame Relay; BSD/OS has a DLT_FR with a value of 11, but that collides
* with other values.
* DLT_FR and DLT_FRELAY packets start with the Q.922 Frame Relay header
* (DLCI, etc.).
*/
#define DLT_FRELAY 107
/*
* OpenBSD DLT_LOOP, for loopback devices; it's like DLT_NULL, except
* that the AF_ type in the link-layer header is in network byte order.
*
* DLT_LOOP is 12 in OpenBSD, but that's DLT_RAW in other OSes, so
* we don't use 12 for it in OSes other than OpenBSD.
*/
#ifdef __OpenBSD__
#define DLT_LOOP 12
#else
#define DLT_LOOP 108
#endif
/*
* Encapsulated packets for IPsec; DLT_ENC is 13 in OpenBSD, but that's
* DLT_SLIP_BSDOS in NetBSD, so we don't use 13 for it in OSes other
* than OpenBSD.
*/
#ifdef __OpenBSD__
#define DLT_ENC 13
#else
#define DLT_ENC 109
#endif
/*
* Values between 110 and 112 are reserved for use in capture file headers
* as link-layer types corresponding to DLT_ types that might differ
* between platforms; don't use those values for new DLT_ types
* other than the corresponding DLT_ types.
*/
/*
* This is for Linux cooked sockets.
*/
#define DLT_LINUX_SLL 113
/*
* Apple LocalTalk hardware.
*/
#define DLT_LTALK 114
/*
* Acorn Econet.
*/
#define DLT_ECONET 115
/*
* Reserved for use with OpenBSD ipfilter.
*/
#define DLT_IPFILTER 116
/*
* OpenBSD DLT_PFLOG; DLT_PFLOG is 17 in OpenBSD, but that's DLT_LANE8023
* in SuSE 6.3, so we can't use 17 for it in capture-file headers.
*
* XXX: is there a conflict with DLT_PFSYNC 18 as well?
*/
#ifdef __OpenBSD__
#define DLT_OLD_PFLOG 17
#define DLT_PFSYNC 18
#endif
#define DLT_PFLOG 117
/*
* Registered for Cisco-internal use.
*/
#define DLT_CISCO_IOS 118
/*
* For 802.11 cards using the Prism II chips, with a link-layer
* header including Prism monitor mode information plus an 802.11
* header.
*/
#define DLT_PRISM_HEADER 119
/*
* Reserved for Aironet 802.11 cards, with an Aironet link-layer header
* (see Doug Ambrisko's FreeBSD patches).
*/
#define DLT_AIRONET_HEADER 120
/*
* Reserved for Siemens HiPath HDLC.
*/
#define DLT_HHDLC 121
/*
* This is for RFC 2625 IP-over-Fibre Channel.
*
* This is not for use with raw Fibre Channel, where the link-layer
* header starts with a Fibre Channel frame header; it's for IP-over-FC,
* where the link-layer header starts with an RFC 2625 Network_Header
* field.
*/
#define DLT_IP_OVER_FC 122
/*
* This is for Full Frontal ATM on Solaris with SunATM, with a
* pseudo-header followed by an AALn PDU.
*
* There may be other forms of Full Frontal ATM on other OSes,
* with different pseudo-headers.
*
* If ATM software returns a pseudo-header with VPI/VCI information
* (and, ideally, packet type information, e.g. signalling, ILMI,
* LANE, LLC-multiplexed traffic, etc.), it should not use
* DLT_ATM_RFC1483, but should get a new DLT_ value, so tcpdump
* and the like don't have to infer the presence or absence of a
* pseudo-header and the form of the pseudo-header.
*/
#define DLT_SUNATM 123 /* Solaris+SunATM */
/*
* Reserved as per request from Kent Dahlgren <kent@praesum.com>
* for private use.
*/
#define DLT_RIO 124 /* RapidIO */
#define DLT_PCI_EXP 125 /* PCI Express */
#define DLT_AURORA 126 /* Xilinx Aurora link layer */
/*
* Header for 802.11 plus a number of bits of link-layer information
* including radio information, used by some recent BSD drivers as
* well as the madwifi Atheros driver for Linux.
*/
#define DLT_IEEE802_11_RADIO 127 /* 802.11 plus radiotap radio header */
/*
* Reserved for the TZSP encapsulation, as per request from
* Chris Waters <chris.waters@networkchemistry.com>
* TZSP is a generic encapsulation for any other link type,
* which includes a means to include meta-information
* with the packet, e.g. signal strength and channel
* for 802.11 packets.
*/
#define DLT_TZSP 128 /* Tazmen Sniffer Protocol */
/*
* BSD's ARCNET headers have the source host, destination host,
* and type at the beginning of the packet; that's what's handed
* up to userland via BPF.
*
* Linux's ARCNET headers, however, have a 2-byte offset field
* between the host IDs and the type; that's what's handed up
* to userland via PF_PACKET sockets.
*
* We therefore have to have separate DLT_ values for them.
*/
#define DLT_ARCNET_LINUX 129 /* ARCNET */
/*
* Juniper-private data link types, as per request from
* Hannes Gredler <hannes@juniper.net>. The DLT_s are used
* for passing on chassis-internal metainformation such as
* QOS profiles, etc..
*/
#define DLT_JUNIPER_MLPPP 130
#define DLT_JUNIPER_MLFR 131
#define DLT_JUNIPER_ES 132
#define DLT_JUNIPER_GGSN 133
#define DLT_JUNIPER_MFR 134
#define DLT_JUNIPER_ATM2 135
#define DLT_JUNIPER_SERVICES 136
#define DLT_JUNIPER_ATM1 137
/*
* Apple IP-over-IEEE 1394, as per a request from Dieter Siegmund
* <dieter@apple.com>. The header that's presented is an Ethernet-like
* header:
*
* #define FIREWIRE_EUI64_LEN 8
* struct firewire_header {
* u_char firewire_dhost[FIREWIRE_EUI64_LEN];
* u_char firewire_shost[FIREWIRE_EUI64_LEN];
* u_short firewire_type;
* };
*
* with "firewire_type" being an Ethernet type value, rather than,
* for example, raw GASP frames being handed up.
*/
#define DLT_APPLE_IP_OVER_IEEE1394 138
/*
* Various SS7 encapsulations, as per a request from Jeff Morriss
* <jeff.morriss[AT]ulticom.com> and subsequent discussions.
*/
#define DLT_MTP2_WITH_PHDR 139 /* pseudo-header with various info, followed by MTP2 */
#define DLT_MTP2 140 /* MTP2, without pseudo-header */
#define DLT_MTP3 141 /* MTP3, without pseudo-header or MTP2 */
#define DLT_SCCP 142 /* SCCP, without pseudo-header or MTP2 or MTP3 */
/*
* DOCSIS MAC frames.
*/
#define DLT_DOCSIS 143
/*
* Linux-IrDA packets. Protocol defined at http://www.irda.org.
* Those packets include IrLAP headers and above (IrLMP...), but
* don't include Phy framing (SOF/EOF/CRC & byte stuffing), because Phy
* framing can be handled by the hardware and depend on the bitrate.
* This is exactly the format you would get capturing on a Linux-IrDA
* interface (irdaX), but not on a raw serial port.
* Note the capture is done in "Linux-cooked" mode, so each packet include
* a fake packet header (struct sll_header). This is because IrDA packet
* decoding is dependant on the direction of the packet (incomming or
* outgoing).
* When/if other platform implement IrDA capture, we may revisit the
* issue and define a real DLT_IRDA...
* Jean II
*/
#define DLT_LINUX_IRDA 144
/*
* Reserved for IBM SP switch and IBM Next Federation switch.
*/
#define DLT_IBM_SP 145
#define DLT_IBM_SN 146
/*
* Reserved for private use. If you have some link-layer header type
* that you want to use within your organization, with the capture files
* using that link-layer header type not ever be sent outside your
* organization, you can use these values.
*
* No libpcap release will use these for any purpose, nor will any
* tcpdump release use them, either.
*
* Do *NOT* use these in capture files that you expect anybody not using
* your private versions of capture-file-reading tools to read; in
* particular, do *NOT* use them in products, otherwise you may find that
* people won't be able to use tcpdump, or snort, or Ethereal, or... to
* read capture files from your firewall/intrusion detection/traffic
* monitoring/etc. appliance, or whatever product uses that DLT_ value,
* and you may also find that the developers of those applications will
* not accept patches to let them read those files.
*
* Also, do not use them if somebody might send you a capture using them
* for *their* private type and tools using them for *your* private type
* would have to read them.
*
* Instead, ask "tcpdump-workers@lists.tcpdump.org" for a new DLT_ value,
* as per the comment above, and use the type you're given.
*/
#define DLT_USER0 147
#define DLT_USER1 148
#define DLT_USER2 149
#define DLT_USER3 150
#define DLT_USER4 151
#define DLT_USER5 152
#define DLT_USER6 153
#define DLT_USER7 154
#define DLT_USER8 155
#define DLT_USER9 156
#define DLT_USER10 157
#define DLT_USER11 158
#define DLT_USER12 159
#define DLT_USER13 160
#define DLT_USER14 161
#define DLT_USER15 162
/*
* For future use with 802.11 captures - defined by AbsoluteValue
* Systems to store a number of bits of link-layer information
* including radio information:
*
* http://www.shaftnet.org/~pizza/software/capturefrm.txt
*
* but it might be used by some non-AVS drivers now or in the
* future.
*/
#define DLT_IEEE802_11_RADIO_AVS 163 /* 802.11 plus AVS radio header */
/*
* Juniper-private data link type, as per request from
* Hannes Gredler <hannes@juniper.net>. The DLT_s are used
* for passing on chassis-internal metainformation such as
* QOS profiles, etc..
*/
#define DLT_JUNIPER_MONITOR 164
/*
* Reserved for BACnet MS/TP.
*/
#define DLT_BACNET_MS_TP 165
/*
* Another PPP variant as per request from Karsten Keil <kkeil@suse.de>.
*
* This is used in some OSes to allow a kernel socket filter to distinguish
* between incoming and outgoing packets, on a socket intended to
* supply pppd with outgoing packets so it can do dial-on-demand and
* hangup-on-lack-of-demand; incoming packets are filtered out so they
* don't cause pppd to hold the connection up (you don't want random
* input packets such as port scans, packets from old lost connections,
* etc. to force the connection to stay up).
*
* The first byte of the PPP header (0xff03) is modified to accomodate
* the direction - 0x00 = IN, 0x01 = OUT.
*/
#define DLT_PPP_PPPD 166
/*
* Names for backwards compatibility with older versions of some PPP
* software; new software should use DLT_PPP_PPPD.
*/
#define DLT_PPP_WITH_DIRECTION DLT_PPP_PPPD
#define DLT_LINUX_PPP_WITHDIRECTION DLT_PPP_PPPD
/*
* Juniper-private data link type, as per request from
* Hannes Gredler <hannes@juniper.net>. The DLT_s are used
* for passing on chassis-internal metainformation such as
* QOS profiles, cookies, etc..
*/
#define DLT_JUNIPER_PPPOE 167
#define DLT_JUNIPER_PPPOE_ATM 168
#define DLT_GPRS_LLC 169 /* GPRS LLC */
#define DLT_GPF_T 170 /* GPF-T (ITU-T G.7041/Y.1303) */
#define DLT_GPF_F 171 /* GPF-F (ITU-T G.7041/Y.1303) */
/*
* Requested by Oolan Zimmer <oz@gcom.com> for use in Gcom's T1/E1 line
* monitoring equipment.
*/
#define DLT_GCOM_T1E1 172
#define DLT_GCOM_SERIAL 173
/*
* Juniper-private data link type, as per request from
* Hannes Gredler <hannes@juniper.net>. The DLT_ is used
* for internal communication to Physical Interface Cards (PIC)
*/
#define DLT_JUNIPER_PIC_PEER 174
/*
* Link types requested by Gregor Maier <gregor@endace.com> of Endace
* Measurement Systems. They add an ERF header (see
* http://www.endace.com/support/EndaceRecordFormat.pdf) in front of
* the link-layer header.
*/
#define DLT_ERF_ETH 175 /* Ethernet */
#define DLT_ERF_POS 176 /* Packet-over-SONET */
/*
* Requested by Daniele Orlandi <daniele@orlandi.com> for raw LAPD
* for vISDN (http://www.orlandi.com/visdn/). Its link-layer header
* includes additional information before the LAPD header, so it's
* not necessarily a generic LAPD header.
*/
#define DLT_LINUX_LAPD 177
/*
* Juniper-private data link type, as per request from
* Hannes Gredler <hannes@juniper.net>.
* The DLT_ are used for prepending meta-information
* like interface index, interface name
* before standard Ethernet, PPP, Frelay & C-HDLC Frames
*/
#define DLT_JUNIPER_ETHER 178
#define DLT_JUNIPER_PPP 179
#define DLT_JUNIPER_FRELAY 180
#define DLT_JUNIPER_CHDLC 181
/*
* Multi Link Frame Relay (FRF.16)
*/
#define DLT_MFR 182
/*
* Juniper-private data link type, as per request from
* Hannes Gredler <hannes@juniper.net>.
* The DLT_ is used for internal communication with a
* voice Adapter Card (PIC)
*/
#define DLT_JUNIPER_VP 183
/*
* Arinc 429 frames.
* DLT_ requested by Gianluca Varenni <gianluca.varenni@cacetech.com>.
* Every frame contains a 32bit A429 label.
* More documentation on Arinc 429 can be found at
* http://www.condoreng.com/support/downloads/tutorials/ARINCTutorial.pdf
*/
#define DLT_A429 184
/*
* Arinc 653 Interpartition Communication messages.
* DLT_ requested by Gianluca Varenni <gianluca.varenni@cacetech.com>.
* Please refer to the A653-1 standard for more information.
*/
#define DLT_A653_ICM 185
/*
* USB packets, beginning with a USB setup header; requested by
* Paolo Abeni <paolo.abeni@email.it>.
*/
#define DLT_USB 186
/*
* Bluetooth HCI UART transport layer (part H:4); requested by
* Paolo Abeni.
*/
#define DLT_BLUETOOTH_HCI_H4 187
/*
* IEEE 802.16 MAC Common Part Sublayer; requested by Maria Cruz
* <cruz_petagay@bah.com>.
*/
#define DLT_IEEE802_16_MAC_CPS 188
/*
* USB packets, beginning with a Linux USB header; requested by
* Paolo Abeni <paolo.abeni@email.it>.
*/
#define DLT_USB_LINUX 189
/*
* Controller Area Network (CAN) v. 2.0B packets.
* DLT_ requested by Gianluca Varenni <gianluca.varenni@cacetech.com>.
* Used to dump CAN packets coming from a CAN Vector board.
* More documentation on the CAN v2.0B frames can be found at
* http://www.can-cia.org/downloads/?269
*/
#define DLT_CAN20B 190
/*
* IEEE 802.15.4, with address fields padded, as is done by Linux
* drivers; requested by Juergen Schimmer.
*/
#define DLT_IEEE802_15_4_LINUX 191
/*
* Per Packet Information encapsulated packets.
* DLT_ requested by Gianluca Varenni <gianluca.varenni@cacetech.com>.
*/
#define DLT_PPI 192
/*
* Header for 802.16 MAC Common Part Sublayer plus a radiotap radio header;
* requested by Charles Clancy.
*/
#define DLT_IEEE802_16_MAC_CPS_RADIO 193
/*
* Juniper-private data link type, as per request from
* Hannes Gredler <hannes@juniper.net>.
* The DLT_ is used for internal communication with a
* integrated service module (ISM).
*/
#define DLT_JUNIPER_ISM 194
/*
* IEEE 802.15.4, exactly as it appears in the spec (no padding, no
* nothing); requested by Mikko Saarnivala <mikko.saarnivala@sensinode.com>.
*/
#define DLT_IEEE802_15_4 195
/*
* Various link-layer types, with a pseudo-header, for SITA
* (http://www.sita.aero/); requested by Fulko Hew (fulko.hew@gmail.com).
*/
#define DLT_SITA 196
/*
* Various link-layer types, with a pseudo-header, for Endace DAG cards;
* encapsulates Endace ERF records. Requested by Stephen Donnelly
* <stephen@endace.com>.
*/
#define DLT_ERF 197
/*
* Special header prepended to Ethernet packets when capturing from a
* u10 Networks board. Requested by Phil Mulholland
* <phil@u10networks.com>.
*/
#define DLT_RAIF1 198
/*
* IPMB packet for IPMI, beginning with the I2C slave address, followed
* by the netFn and LUN, etc.. Requested by Chanthy Toeung
* <chanthy.toeung@ca.kontron.com>.
*/
#define DLT_IPMB 199
/*
* Juniper-private data link type, as per request from
* Hannes Gredler <hannes@juniper.net>.
* The DLT_ is used for capturing data on a secure tunnel interface.
*/
#define DLT_JUNIPER_ST 200
/*
* Bluetooth HCI UART transport layer (part H:4), with pseudo-header
* that includes direction information; requested by Paolo Abeni.
*/
#define DLT_BLUETOOTH_HCI_H4_WITH_PHDR 201
/*
* AX.25 packet with a 1-byte KISS header; see
*
* http://www.ax25.net/kiss.htm
*
* as per Richard Stearn <richard@rns-stearn.demon.co.uk>.
*/
#define DLT_AX25_KISS 202
/*
* LAPD packets from an ISDN channel, starting with the address field,
* with no pseudo-header.
* Requested by Varuna De Silva <varunax@gmail.com>.
*/
#define DLT_LAPD 203
/*
* Variants of various link-layer headers, with a one-byte direction
* pseudo-header prepended - zero means "received by this host",
* non-zero (any non-zero value) means "sent by this host" - as per
* Will Barker <w.barker@zen.co.uk>.
*/
#define DLT_PPP_WITH_DIR 204 /* PPP - don't confuse with DLT_PPP_WITH_DIRECTION */
#define DLT_C_HDLC_WITH_DIR 205 /* Cisco HDLC */
#define DLT_FRELAY_WITH_DIR 206 /* Frame Relay */
#define DLT_LAPB_WITH_DIR 207 /* LAPB */
/*
* 208 is reserved for an as-yet-unspecified proprietary link-layer
* type, as requested by Will Barker.
*/
/*
* IPMB with a Linux-specific pseudo-header; as requested by Alexey Neyman
* <avn@pigeonpoint.com>.
*/
#define DLT_IPMB_LINUX 209
/*
* FlexRay automotive bus - http://www.flexray.com/ - as requested
* by Hannes Kaelber <hannes.kaelber@x2e.de>.
*/
#define DLT_FLEXRAY 210
/*
* Media Oriented Systems Transport (MOST) bus for multimedia
* transport - http://www.mostcooperation.com/ - as requested
* by Hannes Kaelber <hannes.kaelber@x2e.de>.
*/
#define DLT_MOST 211
/*
* Local Interconnect Network (LIN) bus for vehicle networks -
* http://www.lin-subbus.org/ - as requested by Hannes Kaelber
* <hannes.kaelber@x2e.de>.
*/
#define DLT_LIN 212
/*
* X2E-private data link type used for serial line capture,
* as requested by Hannes Kaelber <hannes.kaelber@x2e.de>.
*/
#define DLT_X2E_SERIAL 213
/*
* X2E-private data link type used for the Xoraya data logger
* family, as requested by Hannes Kaelber <hannes.kaelber@x2e.de>.
*/
#define DLT_X2E_XORAYA 214
/*
* IEEE 802.15.4, exactly as it appears in the spec (no padding, no
* nothing), but with the PHY-level data for non-ASK PHYs (4 octets
* of 0 as preamble, one octet of SFD, one octet of frame length+
* reserved bit, and then the MAC-layer data, starting with the
* frame control field).
*
* Requested by Max Filippov <jcmvbkbc@gmail.com>.
*/
#define DLT_IEEE802_15_4_NONASK_PHY 215
/*
* DLT and savefile link type values are split into a class and
* a member of that class. A class value of 0 indicates a regular
* DLT_/LINKTYPE_ value.
*/
#define DLT_CLASS(x) ((x) & 0x03ff0000)
/*
* NetBSD-specific generic "raw" link type. The class value indicates
* that this is the generic raw type, and the lower 16 bits are the
* address family we're dealing with. Those values are NetBSD-specific;
* do not assume that they correspond to AF_ values for your operating
* system.
*/
#define DLT_CLASS_NETBSD_RAWAF 0x02240000
#define DLT_NETBSD_RAWAF(af) (DLT_CLASS_NETBSD_RAWAF | (af))
#define DLT_NETBSD_RAWAF_AF(x) ((x) & 0x0000ffff)
#define DLT_IS_NETBSD_RAWAF(x) (DLT_CLASS(x) == DLT_CLASS_NETBSD_RAWAF)
/*
* The instruction encodings.
*/
/* instruction classes */
#define BPF_CLASS(code) ((code) & 0x07)
#define BPF_LD 0x00
#define BPF_LDX 0x01
#define BPF_ST 0x02
#define BPF_STX 0x03
#define BPF_ALU 0x04
#define BPF_JMP 0x05
#define BPF_RET 0x06
#define BPF_MISC 0x07
/* ld/ldx fields */
#define BPF_SIZE(code) ((code) & 0x18)
#define BPF_W 0x00
#define BPF_H 0x08
#define BPF_B 0x10
#define BPF_MODE(code) ((code) & 0xe0)
#define BPF_IMM 0x00
#define BPF_ABS 0x20
#define BPF_IND 0x40
#define BPF_MEM 0x60
#define BPF_LEN 0x80
#define BPF_MSH 0xa0
/* alu/jmp fields */
#define BPF_OP(code) ((code) & 0xf0)
#define BPF_ADD 0x00
#define BPF_SUB 0x10
#define BPF_MUL 0x20
#define BPF_DIV 0x30
#define BPF_OR 0x40
#define BPF_AND 0x50
#define BPF_LSH 0x60
#define BPF_RSH 0x70
#define BPF_NEG 0x80
#define BPF_JA 0x00
#define BPF_JEQ 0x10
#define BPF_JGT 0x20
#define BPF_JGE 0x30
#define BPF_JSET 0x40
#define BPF_SRC(code) ((code) & 0x08)
#define BPF_K 0x00
#define BPF_X 0x08
/* ret - BPF_K and BPF_X also apply */
#define BPF_RVAL(code) ((code) & 0x18)
#define BPF_A 0x10
/* misc */
#define BPF_MISCOP(code) ((code) & 0xf8)
#define BPF_TAX 0x00
#define BPF_TXA 0x80
/*
* The instruction data structure.
*/
struct bpf_insn {
u_short code;
u_char jt;
u_char jf;
bpf_u_int32 k;
};
/*
* Macros for insn array initializers.
*/
#define BPF_STMT(code, k) { (u_short)(code), 0, 0, k }
#define BPF_JUMP(code, k, jt, jf) { (u_short)(code), jt, jf, k }
#if __STDC__ || defined(__cplusplus)
extern int bpf_validate(const struct bpf_insn *, int);
extern u_int bpf_filter(const struct bpf_insn *, const u_char *, u_int, u_int);
#else
extern int bpf_validate();
extern u_int bpf_filter();
#endif
/*
* Number of scratch memory words (for BPF_LD|BPF_MEM and BPF_ST).
*/
#define BPF_MEMWORDS 16
#ifdef __cplusplus
}
#endif
#endif
@@ -0,0 +1,89 @@
/*
* Copyright (c) 1994, 1996
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the Computer Systems
* Engineering Group at Lawrence Berkeley Laboratory.
* 4. Neither the name of the University nor of the Laboratory may be used
* to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#) $Header: /tcpdump/master/libpcap/pcap/namedb.h,v 1.1 2006/10/04 18:09:22 guy Exp $ (LBL)
*/
#ifndef lib_pcap_namedb_h
#define lib_pcap_namedb_h
#ifdef __cplusplus
extern "C" {
#endif
/*
* As returned by the pcap_next_etherent()
* XXX this stuff doesn't belong in this interface, but this
* library already must do name to address translation, so
* on systems that don't have support for /etc/ethers, we
* export these hooks since they'll
*/
struct pcap_etherent {
u_char addr[6];
char name[122];
};
#ifndef PCAP_ETHERS_FILE
#define PCAP_ETHERS_FILE "/etc/ethers"
#endif
struct pcap_etherent *pcap_next_etherent(FILE *);
u_char *pcap_ether_hostton(const char*);
u_char *pcap_ether_aton(const char *);
bpf_u_int32 **pcap_nametoaddr(const char *);
#ifdef INET6
struct addrinfo *pcap_nametoaddrinfo(const char *);
#endif
bpf_u_int32 pcap_nametonetaddr(const char *);
int pcap_nametoport(const char *, int *, int *);
int pcap_nametoportrange(const char *, int *, int *, int *);
int pcap_nametoproto(const char *);
int pcap_nametoeproto(const char *);
int pcap_nametollc(const char *);
/*
* If a protocol is unknown, PROTO_UNDEF is returned.
* Also, pcap_nametoport() returns the protocol along with the port number.
* If there are ambiguous entried in /etc/services (i.e. domain
* can be either tcp or udp) PROTO_UNDEF is returned.
*/
#define PROTO_UNDEF -1
/* XXX move these to pcap-int.h? */
int __pcap_atodn(const char *, bpf_u_int32 *);
int __pcap_atoin(const char *, bpf_u_int32 *);
u_short __pcap_nametodnaddr(const char *);
#ifdef __cplusplus
}
#endif
#endif
@@ -0,0 +1,407 @@
/* -*- Mode: c; tab-width: 8; indent-tabs-mode: 1; c-basic-offset: 8; -*- */
/*
* Copyright (c) 1993, 1994, 1995, 1996, 1997
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the Computer Systems
* Engineering Group at Lawrence Berkeley Laboratory.
* 4. Neither the name of the University nor of the Laboratory may be used
* to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#) $Header: /tcpdump/master/libpcap/pcap/pcap.h,v 1.4.2.11 2008-10-06 15:38:39 gianluca Exp $ (LBL)
*/
#ifndef lib_pcap_pcap_h
#define lib_pcap_pcap_h
#if defined(WIN32)
#include <pcap-stdinc.h>
#elif defined(MSDOS)
#include <sys/types.h>
#include <sys/socket.h> /* u_int, u_char etc. */
#else /* UN*X */
#include <sys/types.h>
#include <sys/time.h>
#endif /* WIN32/MSDOS/UN*X */
#ifndef PCAP_DONT_INCLUDE_PCAP_BPF_H
#include <pcap/bpf.h>
#endif
#include <stdio.h>
#ifdef HAVE_REMOTE
// We have to define the SOCKET here, although it has been defined in sockutils.h
// This is to avoid the distribution of the 'sockutils.h' file around
// (for example in the WinPcap developer's pack)
#ifndef SOCKET
#ifdef WIN32
#define SOCKET unsigned int
#else
#define SOCKET int
#endif
#endif
#endif
#ifdef __cplusplus
extern "C" {
#endif
#define PCAP_VERSION_MAJOR 2
#define PCAP_VERSION_MINOR 4
#define PCAP_ERRBUF_SIZE 256
/*
* Compatibility for systems that have a bpf.h that
* predates the bpf typedefs for 64-bit support.
*/
#if BPF_RELEASE - 0 < 199406
typedef int bpf_int32;
typedef u_int bpf_u_int32;
#endif
typedef struct pcap pcap_t;
typedef struct pcap_dumper pcap_dumper_t;
typedef struct pcap_if pcap_if_t;
typedef struct pcap_addr pcap_addr_t;
/*
* The first record in the file contains saved values for some
* of the flags used in the printout phases of tcpdump.
* Many fields here are 32 bit ints so compilers won't insert unwanted
* padding; these files need to be interchangeable across architectures.
*
* Do not change the layout of this structure, in any way (this includes
* changes that only affect the length of fields in this structure).
*
* Also, do not change the interpretation of any of the members of this
* structure, in any way (this includes using values other than
* LINKTYPE_ values, as defined in "savefile.c", in the "linktype"
* field).
*
* Instead:
*
* introduce a new structure for the new format, if the layout
* of the structure changed;
*
* send mail to "tcpdump-workers@lists.tcpdump.org", requesting
* a new magic number for your new capture file format, and, when
* you get the new magic number, put it in "savefile.c";
*
* use that magic number for save files with the changed file
* header;
*
* make the code in "savefile.c" capable of reading files with
* the old file header as well as files with the new file header
* (using the magic number to determine the header format).
*
* Then supply the changes as a patch at
*
* http://sourceforge.net/projects/libpcap/
*
* so that future versions of libpcap and programs that use it (such as
* tcpdump) will be able to read your new capture file format.
*/
struct pcap_file_header {
bpf_u_int32 magic;
u_short version_major;
u_short version_minor;
bpf_int32 thiszone; /* gmt to local correction */
bpf_u_int32 sigfigs; /* accuracy of timestamps */
bpf_u_int32 snaplen; /* max length saved portion of each pkt */
bpf_u_int32 linktype; /* data link type (LINKTYPE_*) */
};
/*
* Macros for the value returned by pcap_datalink_ext().
*
* If LT_FCS_LENGTH_PRESENT(x) is true, the LT_FCS_LENGTH(x) macro
* gives the FCS length of packets in the capture.
*/
#define LT_FCS_LENGTH_PRESENT(x) ((x) & 0x04000000)
#define LT_FCS_LENGTH(x) (((x) & 0xF0000000) >> 28)
#define LT_FCS_DATALINK_EXT(x) ((((x) & 0xF) << 28) | 0x04000000)
typedef enum {
PCAP_D_INOUT = 0,
PCAP_D_IN,
PCAP_D_OUT
} pcap_direction_t;
/*
* Generic per-packet information, as supplied by libpcap.
*
* The time stamp can and should be a "struct timeval", regardless of
* whether your system supports 32-bit tv_sec in "struct timeval",
* 64-bit tv_sec in "struct timeval", or both if it supports both 32-bit
* and 64-bit applications. The on-disk format of savefiles uses 32-bit
* tv_sec (and tv_usec); this structure is irrelevant to that. 32-bit
* and 64-bit versions of libpcap, even if they're on the same platform,
* should supply the appropriate version of "struct timeval", even if
* that's not what the underlying packet capture mechanism supplies.
*/
struct pcap_pkthdr {
struct timeval ts; /* time stamp */
bpf_u_int32 caplen; /* length of portion present */
bpf_u_int32 len; /* length this packet (off wire) */
};
/*
* As returned by the pcap_stats()
*/
struct pcap_stat {
u_int ps_recv; /* number of packets received */
u_int ps_drop; /* number of packets dropped */
u_int ps_ifdrop; /* drops by interface XXX not yet supported */
#ifdef HAVE_REMOTE
u_int ps_capt; /* number of packets that are received by the application; please get rid off the Win32 ifdef */
u_int ps_sent; /* number of packets sent by the server on the network */
u_int ps_netdrop; /* number of packets lost on the network */
#endif /* HAVE_REMOTE */
};
#ifdef MSDOS
/*
* As returned by the pcap_stats_ex()
*/
struct pcap_stat_ex {
u_long rx_packets; /* total packets received */
u_long tx_packets; /* total packets transmitted */
u_long rx_bytes; /* total bytes received */
u_long tx_bytes; /* total bytes transmitted */
u_long rx_errors; /* bad packets received */
u_long tx_errors; /* packet transmit problems */
u_long rx_dropped; /* no space in Rx buffers */
u_long tx_dropped; /* no space available for Tx */
u_long multicast; /* multicast packets received */
u_long collisions;
/* detailed rx_errors: */
u_long rx_length_errors;
u_long rx_over_errors; /* receiver ring buff overflow */
u_long rx_crc_errors; /* recv'd pkt with crc error */
u_long rx_frame_errors; /* recv'd frame alignment error */
u_long rx_fifo_errors; /* recv'r fifo overrun */
u_long rx_missed_errors; /* recv'r missed packet */
/* detailed tx_errors */
u_long tx_aborted_errors;
u_long tx_carrier_errors;
u_long tx_fifo_errors;
u_long tx_heartbeat_errors;
u_long tx_window_errors;
};
#endif
/*
* Item in a list of interfaces.
*/
struct pcap_if {
struct pcap_if *next;
char *name; /* name to hand to "pcap_open_live()" */
char *description; /* textual description of interface, or NULL */
struct pcap_addr *addresses;
bpf_u_int32 flags; /* PCAP_IF_ interface flags */
};
#define PCAP_IF_LOOPBACK 0x00000001 /* interface is loopback */
/*
* Representation of an interface address.
*/
struct pcap_addr {
struct pcap_addr *next;
struct sockaddr *addr; /* address */
struct sockaddr *netmask; /* netmask for that address */
struct sockaddr *broadaddr; /* broadcast address for that address */
struct sockaddr *dstaddr; /* P2P destination address for that address */
};
typedef void (*pcap_handler)(u_char *, const struct pcap_pkthdr *,
const u_char *);
/*
* Error codes for the pcap API.
* These will all be negative, so you can check for the success or
* failure of a call that returns these codes by checking for a
* negative value.
*/
#define PCAP_ERROR -1 /* generic error code */
#define PCAP_ERROR_BREAK -2 /* loop terminated by pcap_breakloop */
#define PCAP_ERROR_NOT_ACTIVATED -3 /* the capture needs to be activated */
#define PCAP_ERROR_ACTIVATED -4 /* the operation can't be performed on already activated captures */
#define PCAP_ERROR_NO_SUCH_DEVICE -5 /* no such device exists */
#define PCAP_ERROR_RFMON_NOTSUP -6 /* this device doesn't support rfmon (monitor) mode */
#define PCAP_ERROR_NOT_RFMON -7 /* operation supported only in monitor mode */
#define PCAP_ERROR_PERM_DENIED -8 /* no permission to open the device */
#define PCAP_ERROR_IFACE_NOT_UP -9 /* interface isn't up */
/*
* Warning codes for the pcap API.
* These will all be positive and non-zero, so they won't look like
* errors.
*/
#define PCAP_WARNING 1 /* generic warning code */
#define PCAP_WARNING_PROMISC_NOTSUP 2 /* this device doesn't support promiscuous mode */
char *pcap_lookupdev(char *);
int pcap_lookupnet(const char *, bpf_u_int32 *, bpf_u_int32 *, char *);
pcap_t *pcap_create(const char *, char *);
int pcap_set_snaplen(pcap_t *, int);
int pcap_set_promisc(pcap_t *, int);
int pcap_can_set_rfmon(pcap_t *);
int pcap_set_rfmon(pcap_t *, int);
int pcap_set_timeout(pcap_t *, int);
int pcap_set_buffer_size(pcap_t *, int);
int pcap_activate(pcap_t *);
pcap_t *pcap_open_live(const char *, int, int, int, char *);
pcap_t *pcap_open_dead(int, int);
pcap_t *pcap_open_offline(const char *, char *);
#if defined(WIN32)
pcap_t *pcap_hopen_offline(intptr_t, char *);
#if !defined(LIBPCAP_EXPORTS)
#define pcap_fopen_offline(f,b) \
pcap_hopen_offline(_get_osfhandle(_fileno(f)), b)
#else /*LIBPCAP_EXPORTS*/
static pcap_t *pcap_fopen_offline(FILE *, char *);
#endif
#else /*WIN32*/
pcap_t *pcap_fopen_offline(FILE *, char *);
#endif /*WIN32*/
void pcap_close(pcap_t *);
int pcap_loop(pcap_t *, int, pcap_handler, u_char *);
int pcap_dispatch(pcap_t *, int, pcap_handler, u_char *);
const u_char*
pcap_next(pcap_t *, struct pcap_pkthdr *);
int pcap_next_ex(pcap_t *, struct pcap_pkthdr **, const u_char **);
void pcap_breakloop(pcap_t *);
int pcap_stats(pcap_t *, struct pcap_stat *);
int pcap_setfilter(pcap_t *, struct bpf_program *);
int pcap_setdirection(pcap_t *, pcap_direction_t);
int pcap_getnonblock(pcap_t *, char *);
int pcap_setnonblock(pcap_t *, int, char *);
int pcap_inject(pcap_t *, const void *, size_t);
int pcap_sendpacket(pcap_t *, const u_char *, int);
const char *pcap_statustostr(int);
const char *pcap_strerror(int);
char *pcap_geterr(pcap_t *);
void pcap_perror(pcap_t *, char *);
int pcap_compile(pcap_t *, struct bpf_program *, const char *, int,
bpf_u_int32);
int pcap_compile_nopcap(int, int, struct bpf_program *,
const char *, int, bpf_u_int32);
void pcap_freecode(struct bpf_program *);
int pcap_offline_filter(struct bpf_program *, const struct pcap_pkthdr *,
const u_char *);
int pcap_datalink(pcap_t *);
int pcap_datalink_ext(pcap_t *);
int pcap_list_datalinks(pcap_t *, int **);
int pcap_set_datalink(pcap_t *, int);
void pcap_free_datalinks(int *);
int pcap_datalink_name_to_val(const char *);
const char *pcap_datalink_val_to_name(int);
const char *pcap_datalink_val_to_description(int);
int pcap_snapshot(pcap_t *);
int pcap_is_swapped(pcap_t *);
int pcap_major_version(pcap_t *);
int pcap_minor_version(pcap_t *);
/* XXX */
FILE *pcap_file(pcap_t *);
int pcap_fileno(pcap_t *);
pcap_dumper_t *pcap_dump_open(pcap_t *, const char *);
pcap_dumper_t *pcap_dump_fopen(pcap_t *, FILE *fp);
FILE *pcap_dump_file(pcap_dumper_t *);
long pcap_dump_ftell(pcap_dumper_t *);
int pcap_dump_flush(pcap_dumper_t *);
void pcap_dump_close(pcap_dumper_t *);
void pcap_dump(u_char *, const struct pcap_pkthdr *, const u_char *);
int pcap_findalldevs(pcap_if_t **, char *);
void pcap_freealldevs(pcap_if_t *);
const char *pcap_lib_version(void);
/* XXX this guy lives in the bpf tree */
u_int bpf_filter(const struct bpf_insn *, const u_char *, u_int, u_int);
int bpf_validate(const struct bpf_insn *f, int len);
char *bpf_image(const struct bpf_insn *, int);
void bpf_dump(const struct bpf_program *, int);
#if defined(WIN32)
/*
* Win32 definitions
*/
int pcap_setbuff(pcap_t *p, int dim);
int pcap_setmode(pcap_t *p, int mode);
int pcap_setmintocopy(pcap_t *p, int size);
#ifdef WPCAP
/* Include file with the wpcap-specific extensions */
#include <Win32-Extensions.h>
#endif /* WPCAP */
#define MODE_CAPT 0
#define MODE_STAT 1
#define MODE_MON 2
#elif defined(MSDOS)
/*
* MS-DOS definitions
*/
int pcap_stats_ex (pcap_t *, struct pcap_stat_ex *);
void pcap_set_wait (pcap_t *p, void (*yield)(void), int wait);
u_long pcap_mac_packets (void);
#else /* UN*X */
/*
* UN*X definitions
*/
int pcap_get_selectable_fd(pcap_t *);
#endif /* WIN32/MSDOS/UN*X */
#ifdef HAVE_REMOTE
/* Includes most of the public stuff that is needed for the remote capture */
#include <remote-ext.h>
#endif /* HAVE_REMOTE */
#ifdef __cplusplus
}
#endif
#endif
@@ -0,0 +1,129 @@
/*-
* Copyright (c) 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997
* The Regents of the University of California. All rights reserved.
*
* This code is derived from the Stanford/CMU enet packet filter,
* (net/enet.c) distributed as part of 4.3BSD, and code contributed
* to Berkeley by Steven McCanne and Van Jacobson both of Lawrence
* Berkeley Laboratory.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#) $Header: /tcpdump/master/libpcap/pcap/sll.h,v 1.2.2.1 2008-05-30 01:36:06 guy Exp $ (LBL)
*/
/*
* For captures on Linux cooked sockets, we construct a fake header
* that includes:
*
* a 2-byte "packet type" which is one of:
*
* LINUX_SLL_HOST packet was sent to us
* LINUX_SLL_BROADCAST packet was broadcast
* LINUX_SLL_MULTICAST packet was multicast
* LINUX_SLL_OTHERHOST packet was sent to somebody else
* LINUX_SLL_OUTGOING packet was sent *by* us;
*
* a 2-byte Ethernet protocol field;
*
* a 2-byte link-layer type;
*
* a 2-byte link-layer address length;
*
* an 8-byte source link-layer address, whose actual length is
* specified by the previous value.
*
* All fields except for the link-layer address are in network byte order.
*
* DO NOT change the layout of this structure, or change any of the
* LINUX_SLL_ values below. If you must change the link-layer header
* for a "cooked" Linux capture, introduce a new DLT_ type (ask
* "tcpdump-workers@lists.tcpdump.org" for one, so that you don't give it
* a value that collides with a value already being used), and use the
* new header in captures of that type, so that programs that can
* handle DLT_LINUX_SLL captures will continue to handle them correctly
* without any change, and so that capture files with different headers
* can be told apart and programs that read them can dissect the
* packets in them.
*/
#ifndef lib_pcap_sll_h
#define lib_pcap_sll_h
/*
* A DLT_LINUX_SLL fake link-layer header.
*/
#define SLL_HDR_LEN 16 /* total header length */
#define SLL_ADDRLEN 8 /* length of address field */
struct sll_header {
u_int16_t sll_pkttype; /* packet type */
u_int16_t sll_hatype; /* link-layer address type */
u_int16_t sll_halen; /* link-layer address length */
u_int8_t sll_addr[SLL_ADDRLEN]; /* link-layer address */
u_int16_t sll_protocol; /* protocol */
};
/*
* The LINUX_SLL_ values for "sll_pkttype"; these correspond to the
* PACKET_ values on Linux, but are defined here so that they're
* available even on systems other than Linux, and so that they
* don't change even if the PACKET_ values change.
*/
#define LINUX_SLL_HOST 0
#define LINUX_SLL_BROADCAST 1
#define LINUX_SLL_MULTICAST 2
#define LINUX_SLL_OTHERHOST 3
#define LINUX_SLL_OUTGOING 4
/*
* The LINUX_SLL_ values for "sll_protocol"; these correspond to the
* ETH_P_ values on Linux, but are defined here so that they're
* available even on systems other than Linux. We assume, for now,
* that the ETH_P_ values won't change in Linux; if they do, then:
*
* if we don't translate them in "pcap-linux.c", capture files
* won't necessarily be readable if captured on a system that
* defines ETH_P_ values that don't match these values;
*
* if we do translate them in "pcap-linux.c", that makes life
* unpleasant for the BPF code generator, as the values you test
* for in the kernel aren't the values that you test for when
* reading a capture file, so the fixup code run on BPF programs
* handed to the kernel ends up having to do more work.
*
* Add other values here as necessary, for handling packet types that
* might show up on non-Ethernet, non-802.x networks. (Not all the ones
* in the Linux "if_ether.h" will, I suspect, actually show up in
* captures.)
*/
#define LINUX_SLL_P_802_3 0x0001 /* Novell 802.3 frames without 802.2 LLC header */
#define LINUX_SLL_P_802_2 0x0004 /* 802.2 frames (not D/I/X Ethernet) */
#endif
@@ -0,0 +1,90 @@
/*
* Copyright (c) 2006 Paolo Abeni (Italy)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote
* products derived from this software without specific prior written
* permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Basic USB data struct
* By Paolo Abeni <paolo.abeni@email.it>
*
* @(#) $Header: /tcpdump/master/libpcap/pcap/usb.h,v 1.6 2007/09/22 02:06:08 guy Exp $
*/
#ifndef _PCAP_USB_STRUCTS_H__
#define _PCAP_USB_STRUCTS_H__
/*
* possible transfer mode
*/
#define URB_TRANSFER_IN 0x80
#define URB_ISOCHRONOUS 0x0
#define URB_INTERRUPT 0x1
#define URB_CONTROL 0x2
#define URB_BULK 0x3
/*
* possible event type
*/
#define URB_SUBMIT 'S'
#define URB_COMPLETE 'C'
#define URB_ERROR 'E'
/*
* USB setup header as defined in USB specification.
* Appears at the front of each packet in DLT_USB captures.
*/
typedef struct _usb_setup {
u_int8_t bmRequestType;
u_int8_t bRequest;
u_int16_t wValue;
u_int16_t wIndex;
u_int16_t wLength;
} pcap_usb_setup;
/*
* Header prepended by linux kernel to each event.
* Appears at the front of each packet in DLT_USB_LINUX captures.
*/
typedef struct _usb_header {
u_int64_t id;
u_int8_t event_type;
u_int8_t transfer_type;
u_int8_t endpoint_number;
u_int8_t device_address;
u_int16_t bus_id;
char setup_flag;/*if !=0 the urb setup header is not present*/
char data_flag; /*if !=0 no urb data is present*/
int64_t ts_sec;
int32_t ts_usec;
int32_t status;
u_int32_t urb_len;
u_int32_t data_len; /* amount of urb data really present in this event*/
pcap_usb_setup setup;
} pcap_usb_header;
#endif
@@ -0,0 +1,46 @@
/*-
* Copyright (c) 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#) $Header: /tcpdump/master/libpcap/pcap/vlan.h,v 1.1.2.2 2008-08-06 07:45:59 guy Exp $
*/
#ifndef lib_pcap_vlan_h
#define lib_pcap_vlan_h
struct vlan_tag {
u_int16_t vlan_tpid; /* ETH_P_8021Q */
u_int16_t vlan_tci; /* VLAN TCI */
};
#define VLAN_TAG_LEN 4
#endif
@@ -0,0 +1,444 @@
/*
* Copyright (c) 2002 - 2003
* NetGroup, Politecnico di Torino (Italy)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the Politecnico di Torino nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef __REMOTE_EXT_H__
#define __REMOTE_EXT_H__
#ifndef HAVE_REMOTE
#error Please do not include this file directly. Just define HAVE_REMOTE and then include pcap.h
#endif
// Definition for Microsoft Visual Studio
#if _MSC_VER > 1000
#pragma once
#endif
#ifdef __cplusplus
extern "C" {
#endif
/*!
\file remote-ext.h
The goal of this file it to include most of the new definitions that should be
placed into the pcap.h file.
It includes all new definitions (structures and functions like pcap_open().
Some of the functions are not really a remote feature, but, right now,
they are placed here.
*/
// All this stuff is public
/*! \addtogroup remote_struct
\{
*/
/*!
\brief Defines the maximum buffer size in which address, port, interface names are kept.
In case the adapter name or such is larger than this value, it is truncated.
This is not used by the user; however it must be aware that an hostname / interface
name longer than this value will be truncated.
*/
#define PCAP_BUF_SIZE 1024
/*! \addtogroup remote_source_ID
\{
*/
/*!
\brief Internal representation of the type of source in use (file,
remote/local interface).
This indicates a file, i.e. the user want to open a capture from a local file.
*/
#define PCAP_SRC_FILE 2
/*!
\brief Internal representation of the type of source in use (file,
remote/local interface).
This indicates a local interface, i.e. the user want to open a capture from
a local interface. This does not involve the RPCAP protocol.
*/
#define PCAP_SRC_IFLOCAL 3
/*!
\brief Internal representation of the type of source in use (file,
remote/local interface).
This indicates a remote interface, i.e. the user want to open a capture from
an interface on a remote host. This does involve the RPCAP protocol.
*/
#define PCAP_SRC_IFREMOTE 4
/*!
\}
*/
/*! \addtogroup remote_source_string
The formats allowed by the pcap_open() are the following:
- file://path_and_filename [opens a local file]
- rpcap://devicename [opens the selected device devices available on the local host, without using the RPCAP protocol]
- rpcap://host/devicename [opens the selected device available on a remote host]
- rpcap://host:port/devicename [opens the selected device available on a remote host, using a non-standard port for RPCAP]
- adaptername [to open a local adapter; kept for compability, but it is strongly discouraged]
- (NULL) [to open the first local adapter; kept for compability, but it is strongly discouraged]
The formats allowed by the pcap_findalldevs_ex() are the following:
- file://folder/ [lists all the files in the given folder]
- rpcap:// [lists all local adapters]
- rpcap://host:port/ [lists the devices available on a remote host]
Referring to the 'host' and 'port' paramters, they can be either numeric or literal. Since
IPv6 is fully supported, these are the allowed formats:
- host (literal): e.g. host.foo.bar
- host (numeric IPv4): e.g. 10.11.12.13
- host (numeric IPv4, IPv6 style): e.g. [10.11.12.13]
- host (numeric IPv6): e.g. [1:2:3::4]
- port: can be either numeric (e.g. '80') or literal (e.g. 'http')
Here you find some allowed examples:
- rpcap://host.foo.bar/devicename [everything literal, no port number]
- rpcap://host.foo.bar:1234/devicename [everything literal, with port number]
- rpcap://10.11.12.13/devicename [IPv4 numeric, no port number]
- rpcap://10.11.12.13:1234/devicename [IPv4 numeric, with port number]
- rpcap://[10.11.12.13]:1234/devicename [IPv4 numeric with IPv6 format, with port number]
- rpcap://[1:2:3::4]/devicename [IPv6 numeric, no port number]
- rpcap://[1:2:3::4]:1234/devicename [IPv6 numeric, with port number]
- rpcap://[1:2:3::4]:http/devicename [IPv6 numeric, with literal port number]
\{
*/
/*!
\brief String that will be used to determine the type of source in use (file,
remote/local interface).
This string will be prepended to the interface name in order to create a string
that contains all the information required to open the source.
This string indicates that the user wants to open a capture from a local file.
*/
#define PCAP_SRC_FILE_STRING "file://"
/*!
\brief String that will be used to determine the type of source in use (file,
remote/local interface).
This string will be prepended to the interface name in order to create a string
that contains all the information required to open the source.
This string indicates that the user wants to open a capture from a network interface.
This string does not necessarily involve the use of the RPCAP protocol. If the
interface required resides on the local host, the RPCAP protocol is not involved
and the local functions are used.
*/
#define PCAP_SRC_IF_STRING "rpcap://"
/*!
\}
*/
/*!
\addtogroup remote_open_flags
\{
*/
/*!
\brief Defines if the adapter has to go in promiscuous mode.
It is '1' if you have to open the adapter in promiscuous mode, '0' otherwise.
Note that even if this parameter is false, the interface could well be in promiscuous
mode for some other reason (for example because another capture process with
promiscuous mode enabled is currently using that interface).
On on Linux systems with 2.2 or later kernels (that have the "any" device), this
flag does not work on the "any" device; if an argument of "any" is supplied,
the 'promisc' flag is ignored.
*/
#define PCAP_OPENFLAG_PROMISCUOUS 1
/*!
\brief Defines if the data trasfer (in case of a remote
capture) has to be done with UDP protocol.
If it is '1' if you want a UDP data connection, '0' if you want
a TCP data connection; control connection is always TCP-based.
A UDP connection is much lighter, but it does not guarantee that all
the captured packets arrive to the client workstation. Moreover,
it could be harmful in case of network congestion.
This flag is meaningless if the source is not a remote interface.
In that case, it is simply ignored.
*/
#define PCAP_OPENFLAG_DATATX_UDP 2
/*!
\brief Defines if the remote probe will capture its own generated traffic.
In case the remote probe uses the same interface to capture traffic and to send
data back to the caller, the captured traffic includes the RPCAP traffic as well.
If this flag is turned on, the RPCAP traffic is excluded from the capture, so that
the trace returned back to the collector is does not include this traffic.
*/
#define PCAP_OPENFLAG_NOCAPTURE_RPCAP 4
/*!
\brief Defines if the local adapter will capture its own generated traffic.
This flag tells the underlying capture driver to drop the packets that were sent by itself.
This is usefult when building applications like bridges, that should ignore the traffic
they just sent.
*/
#define PCAP_OPENFLAG_NOCAPTURE_LOCAL 8
/*!
\brief This flag configures the adapter for maximum responsiveness.
In presence of a large value for nbytes, WinPcap waits for the arrival of several packets before
copying the data to the user. This guarantees a low number of system calls, i.e. lower processor usage,
i.e. better performance, which is good for applications like sniffers. If the user sets the
PCAP_OPENFLAG_MAX_RESPONSIVENESS flag, the capture driver will copy the packets as soon as the application
is ready to receive them. This is suggested for real time applications (like, for example, a bridge)
that need the best responsiveness.*/
#define PCAP_OPENFLAG_MAX_RESPONSIVENESS 16
/*!
\}
*/
/*!
\addtogroup remote_samp_methods
\{
*/
/*!
\brief No sampling has to be done on the current capture.
In this case, no sampling algorithms are applied to the current capture.
*/
#define PCAP_SAMP_NOSAMP 0
/*!
\brief It defines that only 1 out of N packets must be returned to the user.
In this case, the 'value' field of the 'pcap_samp' structure indicates the
number of packets (minus 1) that must be discarded before one packet got accepted.
In other words, if 'value = 10', the first packet is returned to the caller, while
the following 9 are discarded.
*/
#define PCAP_SAMP_1_EVERY_N 1
/*!
\brief It defines that we have to return 1 packet every N milliseconds.
In this case, the 'value' field of the 'pcap_samp' structure indicates the 'waiting
time' in milliseconds before one packet got accepted.
In other words, if 'value = 10', the first packet is returned to the caller; the next
returned one will be the first packet that arrives when 10ms have elapsed.
*/
#define PCAP_SAMP_FIRST_AFTER_N_MS 2
/*!
\}
*/
/*!
\addtogroup remote_auth_methods
\{
*/
/*!
\brief It defines the NULL authentication.
This value has to be used within the 'type' member of the pcap_rmtauth structure.
The 'NULL' authentication has to be equal to 'zero', so that old applications
can just put every field of struct pcap_rmtauth to zero, and it does work.
*/
#define RPCAP_RMTAUTH_NULL 0
/*!
\brief It defines the username/password authentication.
With this type of authentication, the RPCAP protocol will use the username/
password provided to authenticate the user on the remote machine. If the
authentication is successful (and the user has the right to open network devices)
the RPCAP connection will continue; otherwise it will be dropped.
This value has to be used within the 'type' member of the pcap_rmtauth structure.
*/
#define RPCAP_RMTAUTH_PWD 1
/*!
\}
*/
/*!
\brief This structure keeps the information needed to autheticate
the user on a remote machine.
The remote machine can either grant or refuse the access according
to the information provided.
In case the NULL authentication is required, both 'username' and
'password' can be NULL pointers.
This structure is meaningless if the source is not a remote interface;
in that case, the functions which requires such a structure can accept
a NULL pointer as well.
*/
struct pcap_rmtauth
{
/*!
\brief Type of the authentication required.
In order to provide maximum flexibility, we can support different types
of authentication based on the value of this 'type' variable. The currently
supported authentication methods are defined into the
\link remote_auth_methods Remote Authentication Methods Section\endlink.
*/
int type;
/*!
\brief Zero-terminated string containing the username that has to be
used on the remote machine for authentication.
This field is meaningless in case of the RPCAP_RMTAUTH_NULL authentication
and it can be NULL.
*/
char *username;
/*!
\brief Zero-terminated string containing the password that has to be
used on the remote machine for authentication.
This field is meaningless in case of the RPCAP_RMTAUTH_NULL authentication
and it can be NULL.
*/
char *password;
};
/*!
\brief This structure defines the information related to sampling.
In case the sampling is requested, the capturing device should read
only a subset of the packets coming from the source. The returned packets depend
on the sampling parameters.
\warning The sampling process is applied <strong>after</strong> the filtering process.
In other words, packets are filtered first, then the sampling process selects a
subset of the 'filtered' packets and it returns them to the caller.
*/
struct pcap_samp
{
/*!
Method used for sampling. Currently, the supported methods are listed in the
\link remote_samp_methods Sampling Methods Section\endlink.
*/
int method;
/*!
This value depends on the sampling method defined. For its meaning, please check
at the \link remote_samp_methods Sampling Methods Section\endlink.
*/
int value;
};
//! Maximum length of an host name (needed for the RPCAP active mode)
#define RPCAP_HOSTLIST_SIZE 1024
/*!
\}
*/ // end of public documentation
// Exported functions
/** \name New WinPcap functions
This section lists the new functions that are able to help considerably in writing
WinPcap programs because of their easiness of use.
*/
//\{
pcap_t *pcap_open(const char *source, int snaplen, int flags, int read_timeout, struct pcap_rmtauth *auth, char *errbuf);
int pcap_createsrcstr(char *source, int type, const char *host, const char *port, const char *name, char *errbuf);
int pcap_parsesrcstr(const char *source, int *type, char *host, char *port, char *name, char *errbuf);
int pcap_findalldevs_ex(char *source, struct pcap_rmtauth *auth, pcap_if_t **alldevs, char *errbuf);
struct pcap_samp *pcap_setsampling(pcap_t *p);
//\}
// End of new winpcap functions
/** \name Remote Capture functions
*/
//\{
SOCKET pcap_remoteact_accept(const char *address, const char *port, const char *hostlist, char *connectinghost, struct pcap_rmtauth *auth, char *errbuf);
int pcap_remoteact_list(char *hostlist, char sep, int size, char *errbuf);
int pcap_remoteact_close(const char *host, char *errbuf);
void pcap_remoteact_cleanup();
//\}
// End of remote capture functions
#ifdef __cplusplus
}
#endif
#endif