From b978b4cb5c82d0390733c7343863913ec0062bdf Mon Sep 17 00:00:00 2001 From: Andreas Karlsson Date: Fri, 24 May 2019 13:43:01 +0200 Subject: [PATCH 01/12] Improve documentation fixes #226 and fixes #270 --- Doxyfile | 2 +- doc/images/legacy_iomap.png | Bin 0 -> 25053 bytes doc/images/overlapping_iomap.png | Bin 0 -> 28169 bytes doc/soem.dox | 10 +++ doc/tutorial.txt | 118 ++++++++++++++++++++++++++++++- 5 files changed, 128 insertions(+), 2 deletions(-) create mode 100644 doc/images/legacy_iomap.png create mode 100644 doc/images/overlapping_iomap.png diff --git a/Doxyfile b/Doxyfile index cee7d54..4ae5f82 100644 --- a/Doxyfile +++ b/Doxyfile @@ -32,7 +32,7 @@ PROJECT_NAME = SOEM # This could be handy for archiving the generated documentation or # if some version control system is used. -PROJECT_NUMBER = v1.3.3 +PROJECT_NUMBER = v1.4.0 # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer diff --git a/doc/images/legacy_iomap.png b/doc/images/legacy_iomap.png new file mode 100644 index 0000000000000000000000000000000000000000..1fa39144098e416afb43a06635ec8441774e1e3f GIT binary patch literal 25053 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 U^Ckh0Z@Zx&t1MIS;{BKZ0_GUMTmS$7 literal 0 HcmV?d00001 diff --git a/doc/soem.dox b/doc/soem.dox index 4785dc9..f03da0f 100644 --- a/doc/soem.dox +++ b/doc/soem.dox @@ -114,6 +114,13 @@ * - Error messages updated to latest ETG1020 document. * - FoE transfers now support busy response. * + * Features as of 1.4.0 : + * + * Added ERIKA target. + * Added macOS target. + * Support for EoE over existing mailbox API. + * + * * \section build Build instructions * * See README.md in the root folder. @@ -187,6 +194,9 @@ * - Added rtems target. * - Added support for overlapping IOmap. * + * Version 1.4.0 : 2019-05 + * - Various fixes and improvements + * * \section legal Legal notice * * Licensed under the GNU General Public License version 2 with exceptions. See diff --git a/doc/tutorial.txt b/doc/tutorial.txt index f722aa8..5f9a8ed 100644 --- a/doc/tutorial.txt +++ b/doc/tutorial.txt @@ -5,7 +5,7 @@ The SOEM is a library that provides the user application with the means to send and receive EtherCAT frames. It is up to the application to provide means for: - Reading and writing process data to be sent/received by SOEM - - Keeping local IO data synchronised with the global IOmap + - Keeping local IO data synchronized with the global IOmap - Detecting errors reported by SOEM - Managing errors reported by SOEM @@ -150,6 +150,122 @@ handling is split into ec_send_processdata and ec_receive_processdata. - Now we have a system up and running, all slaves are in state operational. +\section configuration_custom Custom Configuration + +\subsection iomap_config PDO Assign and PDO Config + +Do custom configuration with PDO Assign or PDO Config. SOEM support custom configuration during start via a +PreOP to SafeOP configuration hook. It can be done per slave and should be set before calling +the configuration and mapping of process data, e.g. the call to ec_config_map. Setting the configuration +hook ensure that the custom configuration will be applied when calling recover and re-configuration +of a slave, as described below. + +\code + +int EL7031setup(uint16 slave) +{ + int retval; + uint16 u16val; + + retval = 0; + + /* Map velocity PDO assignment via Complete Access*/ + uint16 map_1c12[4] = {0x0003, 0x1601, 0x1602, 0x1604}; + uint16 map_1c13[3] = {0x0002, 0x1a01, 0x1a03}; + + retval += ec_SDOwrite(slave, 0x1c12, 0x00, TRUE, sizeof(map_1c12), &map_1c12, EC_TIMEOUTSAFE); + retval += ec_SDOwrite(slave, 0x1c13, 0x00, TRUE, sizeof(map_1c13), &map_1c13, EC_TIMEOUTSAFE); + + /* set some motor parameters, just as example */ + u16val = 1200; // max motor current in mA + retval += ec_SDOwrite(slave, 0x8010, 0x01, FALSE, sizeof(u16val), &u16val, EC_TIMEOUTSAFE); + u16val = 150; // motor coil resistance in 0.01ohm + retval += ec_SDOwrite(slave, 0x8010, 0x04, FALSE, sizeof(u16val), &u16val, EC_TIMEOUTSAFE); + + /* set other necessary parameters as needed */ +... + printf("EL7031 slave %d set, retval = %d\n", slave, retval); + return 1; +} + +void simpletest(char *ifname) +{ +... + /* Detect slave beckhoff EL7031 from vendor ID and product code */ + if((ec_slave[slc].eep_man == 0x00000002) && (ec_slave[slc].eep_id == 0x1b773052)) + { + printf("Found %s at position %d\n", ec_slave[slc].name, slc); + /* link slave specific setup to preop->safeop hook */ + ec_slave[slc].PO2SOconfig = EL7031setup; + } +... +\endcode + +\subsection iomap_layout Legacy versus overlapping IOmap + +IOmap options legacy versus overlapping. Overlapping IOmap was introduced to handle +the TI ESC that doesn't support RW access to non-interleaved input and output process +data of multiple slaves. The difference is that legacy IOmapping will send IOmap as is +on the EtherCAT network while the overlapping will re-use logic addressing per slave to +replace RxPDO process data coming from the Master with TxPDO process data generated by the slave +sent back to the master. + +Overview of legacy pdo map +\image html legacy_iomap.png "Legacy IOmapping" +\image latex legacy_iomap.png "Legacy IOmapping" width=15cm + +Overview of overlapping pdo map +\image html overlapping_iomap.png "Overlapping IOmapping" +\image latex overlapping_iomap.png "Overlapping IOmapping" width=15cm + +\subsection iomap_groups EtherCAT slave groups + +Slave groups can be used to group slaves into separate logic groups within an EtherCAT network. +Each group will have its own logic address space mapped to an IOmap address and make it possible to +send and receive process data at different update rate. + +Below is an example on how to assign a slave to a group. OBS! A slave can only be member in one group. + +\code + for (cnt = 1; cnt <= ec_slavecount; cnt++) + { + if ( ) + { + ec_slave[cnt].group = ; + } + else + { + ec_slave[cnt].group = ; + } + } +\endcode + +Alternative 1, configure all slave groups at once, call ec_config_map or ec_config_map_group with arg 0. +This option will share IOmap and store the group IOmap data at offset EC_LOGGROUPOFFSET. + +\code + ec_config_map_group(&IOmap, 0); +\endcode + +Alternative 2, configure the slave groups one by one, call ec_config_map or ec_config_map_group with arg , . +This option will use different, supplied by the user, IOmaps. + +\code + ec_config_map_group(&IOmap1, ); + ec_config_map_group(&IOmap2, ); +\endcode + +To exchange process data for given group(s) the user must call send/recv process data per group. +The send and receive stack of process data don't consider groups, so the application has to send +and receive the process data for one group before sending/receiving process data for another group. + +\code + ec_send_processdata_group(); + ec_receive_processdata_group(, EC_TIMEOUTRET); + ec_send_processdata_group(); + ec_receive_processdata_group(, EC_TIMEOUTRET); +\endcode + \section application Application \subsection iomap Accessing data through IOmap From 151045ed56a83f7442461406363a9d79cc43cd22 Mon Sep 17 00:00:00 2001 From: Andreas Karlsson Date: Fri, 24 May 2019 13:49:10 +0200 Subject: [PATCH 02/12] Adjust Linux and Win32 application SAFEOP->OP timeouts, fixes #141 --- test/linux/eoe_test/eoe_test.c | 2 +- test/linux/red_test/red_test.c | 2 +- test/linux/simple_test/simple_test.c | 2 +- test/win32/red_test/red_test.c | 2 +- test/win32/simple_test/simple_test.c | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/test/linux/eoe_test/eoe_test.c b/test/linux/eoe_test/eoe_test.c index 7a69d8f..98f8997 100644 --- a/test/linux/eoe_test/eoe_test.c +++ b/test/linux/eoe_test/eoe_test.c @@ -238,7 +238,7 @@ void teststarter(char *ifname) /* request OP state for all slaves */ ec_writestate(0); - chk = 40; + chk = 200; /* wait for all slaves to reach OP state */ do { diff --git a/test/linux/red_test/red_test.c b/test/linux/red_test/red_test.c index d42e20f..e8a601f 100644 --- a/test/linux/red_test/red_test.c +++ b/test/linux/red_test/red_test.c @@ -91,7 +91,7 @@ void redtest(char *ifname, char *ifname2) /* activate cyclic process data */ dorun = 1; /* wait for all slaves to reach OP state */ - ec_statecheck(0, EC_STATE_OPERATIONAL, EC_TIMEOUTSTATE); + ec_statecheck(0, EC_STATE_OPERATIONAL, 5 * EC_TIMEOUTSTATE); oloop = ec_slave[0].Obytes; if ((oloop == 0) && (ec_slave[0].Obits > 0)) oloop = 1; if (oloop > 8) oloop = 8; diff --git a/test/linux/simple_test/simple_test.c b/test/linux/simple_test/simple_test.c index 684ff52..a76353f 100644 --- a/test/linux/simple_test/simple_test.c +++ b/test/linux/simple_test/simple_test.c @@ -70,7 +70,7 @@ void simpletest(char *ifname) ec_receive_processdata(EC_TIMEOUTRET); /* request OP state for all slaves */ ec_writestate(0); - chk = 40; + chk = 200; /* wait for all slaves to reach OP state */ do { diff --git a/test/win32/red_test/red_test.c b/test/win32/red_test/red_test.c index 9adf373..4bf9705 100644 --- a/test/win32/red_test/red_test.c +++ b/test/win32/red_test/red_test.c @@ -81,7 +81,7 @@ void redtest(char *ifname, char *ifname2) /* request OP state for all slaves */ ec_writestate(0); /* wait for all slaves to reach OP state */ - ec_statecheck(0, EC_STATE_OPERATIONAL, EC_TIMEOUTSTATE); + ec_statecheck(0, EC_STATE_OPERATIONAL, 5 * EC_TIMEOUTSTATE); oloop = ec_slave[0].Obytes; if ((oloop == 0) && (ec_slave[0].Obits > 0)) oloop = 1; if (oloop > 8) oloop = 8; diff --git a/test/win32/simple_test/simple_test.c b/test/win32/simple_test/simple_test.c index 2e93c68..d38fbeb 100644 --- a/test/win32/simple_test/simple_test.c +++ b/test/win32/simple_test/simple_test.c @@ -186,7 +186,7 @@ void simpletest(char *ifname) /* request OP state for all slaves */ ec_writestate(0); - chk = 40; + chk = 200; /* wait for all slaves to reach OP state */ do { From c22025560416d65df963040f9fa03c46a292718d Mon Sep 17 00:00:00 2001 From: Andreas Karlsson Date: Fri, 24 May 2019 13:55:15 +0200 Subject: [PATCH 03/12] Don't re-define TRUE and FALSE if already defined --- osal/osal.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/osal/osal.h b/osal/osal.h index 6ff9eab..ed07c51 100644 --- a/osal/osal.h +++ b/osal/osal.h @@ -15,9 +15,13 @@ extern "C" #include /* General types */ -typedef uint8_t boolean; +#ifndef TRUE #define TRUE 1 +#endif +#ifndef FALSE #define FALSE 0 +#endif +typedef uint8_t boolean; typedef int8_t int8; typedef int16_t int16; typedef int32_t int32; From 49810a5adf1a2e578d1824544ee2cf14e9227d8a Mon Sep 17 00:00:00 2001 From: Andreas Karlsson Date: Fri, 24 May 2019 13:58:41 +0200 Subject: [PATCH 04/12] VxWorks: Remove usage of muxTkSend, improve abandoned frame handling. bugfix for msgQRecv return value, add cleanup on NIC close --- oshw/vxworks/nicdrv.c | 295 ++++++++++++++++++++++++------------------ oshw/vxworks/nicdrv.h | 4 +- 2 files changed, 170 insertions(+), 129 deletions(-) diff --git a/oshw/vxworks/nicdrv.c b/oshw/vxworks/nicdrv.c index e749023..fb4392e 100644 --- a/oshw/vxworks/nicdrv.c +++ b/oshw/vxworks/nicdrv.c @@ -31,10 +31,12 @@ * This layer if fully transparent for the higher layers. */ +#include #include #include +#include +#include #include -#include #include #include "oshw.h" @@ -146,7 +148,7 @@ int ecx_setupnic(ecx_portt *port, const char *ifname, int secondary) pPktDev->overrun_count = 0; /* Create multi-thread support semaphores */ - port->sem_get_index = semBCreate(SEM_Q_FIFO, SEM_FULL); + port->sem_get_index = semMCreate(SEM_Q_PRIORITY | SEM_INVERSION_SAFE); /* Get the dev name and unit from ifname * We assume form gei1, fei0... @@ -172,13 +174,22 @@ int ecx_setupnic(ecx_portt *port, const char *ifname, int secondary) * as user reference */ /* Bind to mux */ - pPktDev->pCookie = muxBind(ifn, unit_no, mux_rx_callback, NULL, NULL, NULL, MUX_PROTO_SNARF, "ECAT SNARF", pPktDev); + 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; + /* 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 */ @@ -186,11 +197,11 @@ int ecx_setupnic(ecx_portt *port, const char *ifname, int secondary) 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; - } + /* fail */ + NIC_LOGMSG("error_hook: endFindByName failed, device gei: %d not found\n", + unit_no, 2, 3, 4, 5, 6); + goto exit; + } if (secondary) { @@ -236,13 +247,13 @@ int ecx_setupnic(ecx_portt *port, const char *ifname, int secondary) /* 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; - } + 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])); } @@ -271,20 +282,31 @@ 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) - { - msgQDelete(port->msgQId[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); + muxUnbind(pPktDev->pCookie, MUX_PROTO_SNARF, mux_rx_callback); } /* Clean redundant resources if available*/ @@ -295,6 +317,16 @@ int ecx_closenic(ecx_portt *port) { 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]); } } @@ -333,8 +365,6 @@ int ecx_getindex(ecx_portt *port) { int idx; int cnt; - MSG_Q_ID msgQId; - M_BLK_ID trash_can; semTake(port->sem_get_index, WAIT_FOREVER); @@ -356,27 +386,6 @@ int ecx_getindex(ecx_portt *port) } } port->rxbufstat[idx] = EC_BUF_ALLOC; - - /* Clean up any abandoned frames */ - msgQId = port->msgQId[idx]; - if (msgQReceive(msgQId, (char *)&trash_can, sizeof(M_BLK_ID), NO_WAIT) == OK) - { - /* Free resources */ - netMblkClChainFree(trash_can); - } - - if (port->redstate != ECT_RED_NONE) - { - port->redport->rxbufstat[idx] = EC_BUF_ALLOC; - /* Clean up any abandoned frames */ - msgQId = port->redport->msgQId[idx]; - if (msgQReceive(msgQId, (char *)&trash_can, sizeof(M_BLK_ID), NO_WAIT) == OK) - { - /* Free resources */ - netMblkClChainFree(trash_can); - } - } - port->lastidx = idx; semGive(port->sem_get_index); @@ -398,35 +407,59 @@ void ecx_setbufstat(ecx_portt *port, int idx, int bufstat) /** Low level transmit buffer over mux layer 2 driver * -* @param[in] pPktDev = packet device to send buffer over +* @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, void * buf, int len) +static int ec_outfram_send(ETHERCAT_PKT_DEV * pPktDev, int idx, void * buf, int len) { - STATUS status = OK; - M_BLK_ID pPacket; - int rval = 0; + STATUS status = OK; + M_BLK_ID pPacket = NULL; + int rval = 0; + END_OBJ *endObj = (END_OBJ *)pPktDev->endObj; + MSG_Q_ID msgQId; - /* Allocate m_blk to send */ - if ((pPacket = netTupleGet(pPktDev->endObj->pNetPool, - len, - M_DONTWAIT, - MT_DATA, - TRUE)) == NULL) + /* Clean up any abandoned frames and re-use the allocated buffer*/ + msgQId = pPktDev->port->msgQId[idx]; + if(msgQNumMsgs(msgQId) > 0) { - NIC_LOGMSG("ec_outfram_send: Could not allocate MBLK memory!\n", 1, 2, 3, 4, 5, 6); - return ERROR; + 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_HEADER; + pPacket->mBlkHdr.mFlags |= M_PKTHDR; pPacket->mBlkHdr.mData = pPacket->pClBlk->clNode.pClBuf; - pPacket->mBlkPktHdr.len = len; + pPacket->mBlkPktHdr.len = pPacket->m_len; netMblkFromBufCopy(pPacket, buf, 0, pPacket->mBlkHdr.mLen, M_DONTWAIT, NULL); - status = muxTkSend(pPktDev->pCookie, pPacket, NULL, htons(ETH_P_ECAT), NULL); + status = muxSend(endObj, pPacket); if (status == OK) { @@ -475,7 +508,7 @@ int ecx_outframe(ecx_portt *port, int idx, int stacknumber) } (*stack->rxbufstat)[idx] = EC_BUF_TX; - rval = ec_outfram_send(pPktDev, (char*)(*stack->txbuf)[idx], + rval = ec_outfram_send(pPktDev, idx, (char*)(*stack->txbuf)[idx], (*stack->txbuflength)[idx]); if (rval > 0) { @@ -516,12 +549,12 @@ int ecx_outframe_red(ecx_portt *port, int idx) ehp->sa1 = htons(secMAC[1]); /* transmit over secondary interface */ port->redport->rxbufstat[idx] = EC_BUF_TX; - rval = ec_outfram_send(&(port->redport->pktDev), &(port->txbuf2), port->txbuflength2); + rval = ec_outfram_send(&(port->redport->pktDev), idx, &(port->txbuf2), port->txbuflength2); if (rval <= 0) { port->redport->rxbufstat[idx] = EC_BUF_EMPTY; } - } + } return rval; } @@ -545,77 +578,85 @@ static int mux_rx_callback(void* pCookie, long type, M_BLK_ID pMblk, LL_HDR_INFO 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; + 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; - } + /* Get ethercat frame header */ + ecp = (ec_comt*)&(*tempbuf)[ETH_HEADERSIZE]; + idxf = ecp->index; + if (idxf >= EC_MAXBUF) + { + NIC_LOGMSG("mux_rx_callback: idx %d out of bounds\n", idxf, + 2, 3, 4, 5, 6); + return ret; + } - /* Check if it is the redundant port or not */ - if (pPktDev->redundant == 1) - { - msgQId = port->redport->msgQId[idxf]; - } - else - { - msgQId = port->msgQId[idxf]; - } + /* 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]; + } - if (length > 0) - { - /* Post the frame to the reqceive Q for the EtherCAT stack */ - STATUS status; - status = msgQSend(msgQId, (char *)&pMblk, sizeof(M_BLK_ID), - NO_WAIT, MSG_PRI_NORMAL); + /* 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_OK, (char *)&length, sizeof(length)); - ret = TRUE; + NIC_WVEVENT(ECAT_RECV_RETRY_OK, (char *)&length, sizeof(length)); + ret = TRUE; } - else if ((status == ERROR) && (errno == S_objLib_OBJ_UNAVAILABLE)) - { - /* Try to empty the MSGQ since we for some strange reason - * already have a frame in the MsqQ, - * is it due to timeout when receiving? - * We want the latest received frame in the buffer - */ - port->pktDev.overrun_count++; - NIC_LOGMSG("mux_rx_callback: idx %d MsgQ overrun\n", idxf, - 2, 3, 4, 5, 6); - M_BLK_ID trash_can; - if (msgQReceive(msgQId, (char *)&trash_can, - sizeof(M_BLK_ID), NO_WAIT) == OK) - { - /* Free resources */ - netMblkClChainFree(trash_can); - } - - status = msgQSend(msgQId, (char *)&pMblk, sizeof(M_BLK_ID), - NO_WAIT, MSG_PRI_NORMAL); - if (status == OK) - { - NIC_WVEVENT(ECAT_RECV_RETRY_OK, (char *)&length, sizeof(length)); - ret = TRUE; - } - } - else - { - NIC_WVEVENT(ECAT_RECV_FAILED, (char *)&length, sizeof(length)); - } - } + } + else + { + NIC_WVEVENT(ECAT_RECV_FAILED, (char *)&length, sizeof(length)); + } + } } return ret; @@ -912,9 +953,9 @@ int ec_outframe_red(int idx) return ecx_outframe_red(&ecx_port, idx); } -int ec_inframe(int idx, int stacknumber) +int ec_inframe(int idx, int stacknumber, int timeout) { - return ecx_inframe(&ecx_port, idx, stacknumber); + return ecx_inframe(&ecx_port, idx, stacknumber, timeout); } int ec_waitinframe(int idx, int timeout) diff --git a/oshw/vxworks/nicdrv.h b/oshw/vxworks/nicdrv.h index 277d055..e009399 100644 --- a/oshw/vxworks/nicdrv.h +++ b/oshw/vxworks/nicdrv.h @@ -17,18 +17,18 @@ extern "C" #endif #include -#include /** structure to connect EtherCAT stack and VxWorks device */ typedef struct ETHERCAT_PKT_DEV { struct ecx_port *port; void *pCookie; - END_OBJ *endObj; + 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 */ From c892921d7e2e846baf5b0cc9e5c684570893e5a6 Mon Sep 17 00:00:00 2001 From: Andreas Karlsson Date: Fri, 24 May 2019 14:01:40 +0200 Subject: [PATCH 05/12] Add ec_error to string print function, fixes #227 and fixes #232 --- soem/ethercatprint.c | 106 +++++++++++++++++++++++-------------------- soem/ethercatprint.h | 2 + 2 files changed, 60 insertions(+), 48 deletions(-) diff --git a/soem/ethercatprint.c b/soem/ethercatprint.c index 071eb98..6e4f801 100644 --- a/soem/ethercatprint.c +++ b/soem/ethercatprint.c @@ -291,6 +291,63 @@ char* ec_mbxerror2string( uint16 errorcode) return (char *) ec_mbxerrorlist[i].errordescription; } +/** Convert an error to text string. + * + * @param[in] Ec = Struct describing the error. + * @return readable string + */ +char* ecx_err2string(const ec_errort Ec) +{ + char timestr[20]; + sprintf(timestr, "Time:%12.3f", Ec.Time.sec + (Ec.Time.usec / 1000000.0) ); + switch (Ec.Etype) + { + case EC_ERR_TYPE_SDO_ERROR: + { + sprintf(estring, "%s SDO slave:%d index:%4.4x.%2.2x error:%8.8x %s\n", + timestr, Ec.Slave, Ec.Index, Ec.SubIdx, (unsigned)Ec.AbortCode, ec_sdoerror2string(Ec.AbortCode)); + break; + } + case EC_ERR_TYPE_EMERGENCY: + { + sprintf(estring, "%s EMERGENCY slave:%d error:%4.4x\n", + timestr, Ec.Slave, Ec.ErrorCode); + break; + } + case EC_ERR_TYPE_PACKET_ERROR: + { + sprintf(estring, "%s PACKET slave:%d index:%4.4x.%2.2x error:%d\n", + timestr, Ec.Slave, Ec.Index, Ec.SubIdx, Ec.ErrorCode); + break; + } + case EC_ERR_TYPE_SDOINFO_ERROR: + { + sprintf(estring, "%s SDO slave:%d index:%4.4x.%2.2x error:%8.8x %s\n", + timestr, Ec.Slave, Ec.Index, Ec.SubIdx, (unsigned)Ec.AbortCode, ec_sdoerror2string(Ec.AbortCode)); + break; + } + case EC_ERR_TYPE_SOE_ERROR: + { + sprintf(estring, "%s SoE slave:%d IDN:%4.4x error:%4.4x %s\n", + timestr, Ec.Slave, Ec.Index, (unsigned)Ec.AbortCode, ec_soeerror2string(Ec.ErrorCode)); + break; + } + case EC_ERR_TYPE_MBX_ERROR: + { + sprintf(estring, "%s MBX slave:%d error:%4.4x %s\n", + timestr, Ec.Slave, Ec.ErrorCode, ec_mbxerror2string(Ec.ErrorCode)); + break; + } + default: + { + sprintf(estring, "%s error:%8.8x\n", + timestr, (unsigned)Ec.AbortCode); + break; + } + } + return (char*) estring; +} + /** Look up error in ec_errorlist and convert to text string. * * @param[in] context = context struct @@ -299,57 +356,10 @@ char* ec_mbxerror2string( uint16 errorcode) char* ecx_elist2string(ecx_contextt *context) { ec_errort Ec; - char timestr[20]; if (ecx_poperror(context, &Ec)) { - sprintf(timestr, "Time:%12.3f", Ec.Time.sec + (Ec.Time.usec / 1000000.0) ); - switch (Ec.Etype) - { - case EC_ERR_TYPE_SDO_ERROR: - { - sprintf(estring, "%s SDO slave:%d index:%4.4x.%2.2x error:%8.8x %s\n", - timestr, Ec.Slave, Ec.Index, Ec.SubIdx, (unsigned)Ec.AbortCode, ec_sdoerror2string(Ec.AbortCode)); - break; - } - case EC_ERR_TYPE_EMERGENCY: - { - sprintf(estring, "%s EMERGENCY slave:%d error:%4.4x\n", - timestr, Ec.Slave, Ec.ErrorCode); - break; - } - case EC_ERR_TYPE_PACKET_ERROR: - { - sprintf(estring, "%s PACKET slave:%d index:%4.4x.%2.2x error:%d\n", - timestr, Ec.Slave, Ec.Index, Ec.SubIdx, Ec.ErrorCode); - break; - } - case EC_ERR_TYPE_SDOINFO_ERROR: - { - sprintf(estring, "%s SDO slave:%d index:%4.4x.%2.2x error:%8.8x %s\n", - timestr, Ec.Slave, Ec.Index, Ec.SubIdx, (unsigned)Ec.AbortCode, ec_sdoerror2string(Ec.AbortCode)); - break; - } - case EC_ERR_TYPE_SOE_ERROR: - { - sprintf(estring, "%s SoE slave:%d IDN:%4.4x error:%4.4x %s\n", - timestr, Ec.Slave, Ec.Index, (unsigned)Ec.AbortCode, ec_soeerror2string(Ec.ErrorCode)); - break; - } - case EC_ERR_TYPE_MBX_ERROR: - { - sprintf(estring, "%s MBX slave:%d error:%4.4x %s\n", - timestr, Ec.Slave, Ec.ErrorCode, ec_mbxerror2string(Ec.ErrorCode)); - break; - } - default: - { - sprintf(estring, "%s error:%8.8x\n", - timestr, (unsigned)Ec.AbortCode); - break; - } - } - return (char*) estring; + return ecx_err2string(Ec); } else { diff --git a/soem/ethercatprint.h b/soem/ethercatprint.h index d862cb7..43acc2e 100644 --- a/soem/ethercatprint.h +++ b/soem/ethercatprint.h @@ -19,6 +19,8 @@ extern "C" char* ec_sdoerror2string( uint32 sdoerrorcode); char* ec_ALstatuscode2string( uint16 ALstatuscode); char* ec_soeerror2string( uint16 errorcode); +char* ec_mbxerror2string( uint16 errorcode); +char* ecx_err2string(const ec_errort Ec); char* ecx_elist2string(ecx_contextt *context); #ifdef EC_VER1 From 7b1ea32343c18d35778f65e2cfc4718e0dd72744 Mon Sep 17 00:00:00 2001 From: Andreas Karlsson Date: Fri, 24 May 2019 14:02:46 +0200 Subject: [PATCH 06/12] Make it possible to set EC_VER from osal layer --- soem/ethercattype.h | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/soem/ethercattype.h b/soem/ethercattype.h index 86df704..ba5b774 100644 --- a/soem/ethercattype.h +++ b/soem/ethercattype.h @@ -23,12 +23,15 @@ extern "C" { #endif -/** define EC_VER1 if version 1 default context and functions are needed - * comment if application uses only ecx_ functions and own context */ -#define EC_VER1 - #include "osal.h" +/** define EC_VER1 if version 1 default context and functions are needed + * define EC_VER2 if application uses only ecx_ functions and own context */ +#if !defined(EC_VER1) && !defined(EC_VER2) +# define EC_VER1 +#endif + + /** Define little endian target by default if no endian is set */ #if !defined(EC_LITTLE_ENDIAN) && !defined(EC_BIG_ENDIAN) # define EC_LITTLE_ENDIAN From 9c921d8d4e29771f5a1a48f975a494c60611760a Mon Sep 17 00:00:00 2001 From: Andreas Karlsson Date: Fri, 24 May 2019 14:05:53 +0200 Subject: [PATCH 07/12] Make ERROR slave count exceeded unique --- soem/ethercatconfig.c | 2 +- soem/ethercattype.h | 10 ++++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/soem/ethercatconfig.c b/soem/ethercatconfig.c index 923ffbf..08a6ab0 100644 --- a/soem/ethercatconfig.c +++ b/soem/ethercatconfig.c @@ -142,7 +142,7 @@ int ecx_detect_slaves(ecx_contextt *context) { EC_PRINT("Error: too many slaves on network: num_slaves=%d, EC_MAXSLAVE=%d\n", wkc, EC_MAXSLAVE); - return -2; + return EC_SLAVECOUNTEXCEEDED; } } return wkc; diff --git a/soem/ethercattype.h b/soem/ethercattype.h index ba5b774..9dc2f84 100644 --- a/soem/ethercattype.h +++ b/soem/ethercattype.h @@ -37,12 +37,14 @@ extern "C" # define EC_LITTLE_ENDIAN #endif -/** return value general error */ -#define EC_ERROR -3 /** return value no frame returned */ -#define EC_NOFRAME -1 +#define EC_NOFRAME -1 /** return value unknown frame received */ -#define EC_OTHERFRAME -2 +#define EC_OTHERFRAME -2 +/** return value general error */ +#define EC_ERROR -3 +/** return value too many slaves */ +#define EC_SLAVECOUNTEXCEEDED -4 /** maximum EtherCAT frame length in bytes */ #define EC_MAXECATFRAME 1518 /** maximum EtherCAT LRW frame length in bytes */ From 8c1e83ee8e86819e77b7cc9042b914cd12e5443f Mon Sep 17 00:00:00 2001 From: Andreas Karlsson Date: Fri, 24 May 2019 14:27:41 +0200 Subject: [PATCH 08/12] Add extra ERROR on mailbox receive timeout to improv diagnostics possibilities --- soem/ethercatmain.c | 3 ++- soem/ethercattype.h | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/soem/ethercatmain.c b/soem/ethercatmain.c index e91c7e8..795a21c 100644 --- a/soem/ethercatmain.c +++ b/soem/ethercatmain.c @@ -1097,7 +1097,8 @@ int ecx_mbxreceive(ecx_contextt *context, uint16 slave, ec_mbxbuft *mbx, int tim } else /* no read mailbox available */ { - wkc = 0; + if (wkc > 0) + wkc = EC_TIMEOUT; } } diff --git a/soem/ethercattype.h b/soem/ethercattype.h index 9dc2f84..6b4d17c 100644 --- a/soem/ethercattype.h +++ b/soem/ethercattype.h @@ -45,6 +45,8 @@ extern "C" #define EC_ERROR -3 /** return value too many slaves */ #define EC_SLAVECOUNTEXCEEDED -4 +/** return value request timeout */ +#define EC_TIMEOUT -5 /** maximum EtherCAT frame length in bytes */ #define EC_MAXECATFRAME 1518 /** maximum EtherCAT LRW frame length in bytes */ From cc417d4c0ce6b2de20785217311be726fa0c3298 Mon Sep 17 00:00:00 2001 From: Andreas Karlsson Date: Mon, 10 Jun 2019 09:23:02 +0200 Subject: [PATCH 09/12] Add PO2SO hook including context, fixes #230 --- soem/ethercatconfig.c | 4 ++++ soem/ethercatmain.h | 7 +++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/soem/ethercatconfig.c b/soem/ethercatconfig.c index 08a6ab0..6fb8ad6 100644 --- a/soem/ethercatconfig.c +++ b/soem/ethercatconfig.c @@ -645,6 +645,10 @@ static int ecx_map_coe_soe(ecx_contextt *context, uint16 slave, int thread_n) { context->slavelist[slave].PO2SOconfig(slave); } + if (context->slavelist[slave].PO2SOconfigx) /* only if registered */ + { + context->slavelist[slave].PO2SOconfigx(context, slave); + } /* if slave not found in configlist find IO mapping in slave self */ if (!context->slavelist[slave].configindex) { diff --git a/soem/ethercatmain.h b/soem/ethercatmain.h index 3c39674..42ad54b 100644 --- a/soem/ethercatmain.h +++ b/soem/ethercatmain.h @@ -100,6 +100,8 @@ PACKED_END #define EC_SMENABLEMASK 0xfffeffff +typedef struct ecx_context ecx_contextt; + /** for list of ethercat slaves detected */ typedef struct ec_slave { @@ -225,8 +227,10 @@ typedef struct ec_slave uint8 FMMUunused; /** Boolean for tracking whether the slave is (not) responding, not used/set by the SOEM library */ boolean islost; - /** registered configuration function PO->SO */ + /** registered configuration function PO->SO, (DEPRECATED)*/ int (*PO2SOconfig)(uint16 slave); + /** registered configuration function PO->SO */ + int (*PO2SOconfigx)(ecx_contextt * context, uint16 slave); /** readable name */ char name[EC_MAXNAME + 1]; } ec_slavet; @@ -377,7 +381,6 @@ typedef struct PACKED ec_PDOdesc PACKED_END /** Context structure , referenced by all ecx functions*/ -typedef struct ecx_context ecx_contextt; struct ecx_context { /** port reference, may include red_port */ From 817435066f53006f42899bf602e3b7e4182aa57a Mon Sep 17 00:00:00 2001 From: Andreas Karlsson Date: Mon, 10 Jun 2019 11:48:41 +0200 Subject: [PATCH 10/12] Add support to choose if manual or automatic state change is done by config functions, fixes #189 --- soem/ethercatconfig.c | 36 ++++++++++++++++++++++++++++++------ soem/ethercatmain.c | 3 ++- soem/ethercatmain.h | 2 ++ 3 files changed, 34 insertions(+), 7 deletions(-) diff --git a/soem/ethercatconfig.c b/soem/ethercatconfig.c index 6fb8ad6..af667a6 100644 --- a/soem/ethercatconfig.c +++ b/soem/ethercatconfig.c @@ -589,8 +589,16 @@ int ecx_config_init(ecx_contextt *context, uint8 usetable) } /* some slaves need eeprom available to PDI in init->preop transition */ ecx_eeprom2pdi(context, slave); - /* request pre_op for slave */ - ecx_FPWRw(context->port, configadr, ECT_REG_ALCTL, htoes(EC_STATE_PRE_OP | EC_STATE_ACK) , EC_TIMEOUTRET3); /* set preop status */ + /* User may override automatic state change */ + if (context->manualstatechange == 0) + { + /* request pre_op for slave */ + ecx_FPWRw(context->port, + configadr, + ECT_REG_ALCTL, + htoes(EC_STATE_PRE_OP | EC_STATE_ACK), + EC_TIMEOUTRET3); /* set preop status */ + } } } return wkc; @@ -1273,8 +1281,16 @@ int ecx_config_map_group(ecx_contextt *context, void *pIOmap, uint8 group) } ecx_eeprom2pdi(context, slave); /* set Eeprom control to PDI */ - ecx_FPWRw(context->port, configadr, ECT_REG_ALCTL, htoes(EC_STATE_SAFE_OP) , EC_TIMEOUTRET3); /* set safeop status */ - + /* User may override automatic state change */ + if (context->manualstatechange == 0) + { + /* request safe_op for slave */ + ecx_FPWRw(context->port, + configadr, + ECT_REG_ALCTL, + htoes(EC_STATE_SAFE_OP), + EC_TIMEOUTRET3); /* set safeop status */ + } if (context->slavelist[slave].blockLRW) { context->grouplist[group].blockLRW++; @@ -1409,8 +1425,16 @@ int ecx_config_overlap_map_group(ecx_contextt *context, void *pIOmap, uint8 grou } ecx_eeprom2pdi(context, slave); /* set Eeprom control to PDI */ - ecx_FPWRw(context->port, configadr, ECT_REG_ALCTL, htoes(EC_STATE_SAFE_OP), EC_TIMEOUTRET3); /* set safeop status */ - + /* User may override automatic state change */ + if (context->manualstatechange == 0) + { + /* request safe_op for slave */ + ecx_FPWRw(context->port, + configadr, + ECT_REG_ALCTL, + htoes(EC_STATE_SAFE_OP), + EC_TIMEOUTRET3); + } if (context->slavelist[slave].blockLRW) { context->grouplist[group].blockLRW++; diff --git a/soem/ethercatmain.c b/soem/ethercatmain.c index 795a21c..30065b6 100644 --- a/soem/ethercatmain.c +++ b/soem/ethercatmain.c @@ -119,7 +119,8 @@ ecx_contextt ecx_context = { &ec_SM, // .eepSM = &ec_FMMU, // .eepFMMU = NULL, // .FOEhook() - NULL // .EOEhook() + NULL, // .EOEhook() + 0 // .manualstatechange }; #endif diff --git a/soem/ethercatmain.h b/soem/ethercatmain.h index 42ad54b..7001572 100644 --- a/soem/ethercatmain.h +++ b/soem/ethercatmain.h @@ -427,6 +427,8 @@ struct ecx_context int (*FOEhook)(uint16 slave, int packetnumber, int datasize); /** registered EoE hook */ int (*EOEhook)(ecx_contextt * context, uint16 slave, void * eoembx); + /** flag to control legacy automatic state change or manual state change */ + int manualstatechange; }; #ifdef EC_VER1 From 131158dda8338d48718494f0aea863392df820a3 Mon Sep 17 00:00:00 2001 From: Andreas Karlsson Date: Wed, 12 Jun 2019 14:42:26 +0200 Subject: [PATCH 11/12] VxWorks, use monotonic clock, fixes #309 --- osal/vxworks/osal.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/osal/vxworks/osal.c b/osal/vxworks/osal.c index a9f7f84..cfb0d02 100644 --- a/osal/vxworks/osal.c +++ b/osal/vxworks/osal.c @@ -72,10 +72,14 @@ int osal_usleep (uint32 usec) int osal_gettimeofday(struct timeval *tv, struct timezone *tz) { + struct timespec ts; int return_value; + (void)tz; /* Not used */ - return_value = gettimeofday(tv, tz); - + /* Use clock_gettime CLOCK_MONOTONIC to a avoid NTP time adjustments */ + return_value = clock_gettime(CLOCK_MONOTONIC, &ts); + tv->tv_sec = ts.tv_sec; + tv->tv_usec = ts.tv_nsec / 1000; return return_value; } From 5c71c281fcce71b48e723f33884252100831d174 Mon Sep 17 00:00:00 2001 From: Andreas Karlsson Date: Wed, 12 Jun 2019 14:50:38 +0200 Subject: [PATCH 12/12] VxWorks, calculate tick receive timouts from systick, fixes #310 --- oshw/vxworks/nicdrv.c | 34 ++++++++++++++++++++++------------ 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/oshw/vxworks/nicdrv.c b/oshw/vxworks/nicdrv.c index fb4392e..0e45787 100644 --- a/oshw/vxworks/nicdrv.c +++ b/oshw/vxworks/nicdrv.c @@ -35,9 +35,10 @@ #include #include #include -#include -#include -#include +#include +#include +#include +#include #include "oshw.h" #include "osal.h" @@ -98,6 +99,10 @@ const uint16 secMAC[3] = { 0x0404, 0x0404, 0x0404 }; /** 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); @@ -128,6 +133,11 @@ int ecx_setupnic(ecx_portt *port, const char *ifname, int secondary) 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. */ @@ -141,7 +151,7 @@ int ecx_setupnic(ecx_portt *port, const char *ifname, int secondary) pPktDev = &(port->pktDev); pPktDev->redundant = 0; } - + /* Clear frame counters*/ pPktDev->tx_count = 0; pPktDev->rx_count = 0; @@ -164,12 +174,12 @@ int ecx_setupnic(ecx_portt *port, const char *ifname, int secondary) 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 */ @@ -256,8 +266,8 @@ int ecx_setupnic(ecx_portt *port, const char *ifname, int secondary) } } 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++) { @@ -265,9 +275,9 @@ int ecx_setupnic(ecx_portt *port, const char *ifname, int secondary) port->rxbufstat[i] = EC_BUF_EMPTY; } ec_setupheader(&(port->txbuf2)); - + return 1; - + exit: return 0; @@ -672,7 +682,7 @@ static int ecx_recvpkt(ecx_portt *port, int idx, int stacknumber, M_BLK_ID * pMb { int bytesrx = 0; MSG_Q_ID msgQId; - int tick_timeout = max((timeout / 1000), 1); + int tick_timeout = max((timeout / usec_per_tick), 1); if (stacknumber == 1) {