diff --git a/osal/vxworks/osal.c b/osal/vxworks/osal.c new file mode 100644 index 0000000..608f908 --- /dev/null +++ b/osal/vxworks/osal.c @@ -0,0 +1,217 @@ +/* + * Simple Open EtherCAT Master Library + * + * File : osal.c + * Version : 1.3.1 + * Date : 10-01-2017 + * Copyright (C) 2005-2017 Speciaal Machinefabriek Ketels v.o.f. + * Copyright (C) 2005-2017 Arthur Ketels + * Copyright (C) 2008-2009 TU/e Technische Universiteit Eindhoven + * Copyright (C) 2012-2017 rt-labs AB , Sweden + * + * SOEM is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 as published by the Free + * Software Foundation. + * + * SOEM is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * As a special exception, if other files instantiate templates or use macros + * or inline functions from this file, or you compile this file and link it + * with other works to produce a work based on this file, this file does not + * by itself cause the resulting work to be covered by the GNU General Public + * License. However the source code for this file must still be made available + * in accordance with section (3) of the GNU General Public License. + * + * This exception does not invalidate any other reasons why a work based on + * this file might be covered by the GNU General Public License. + * + * The EtherCAT Technology, the trade name and logo “EtherCAT” are the intellectual + * property of, and protected by Beckhoff Automation GmbH. You can use SOEM for + * the sole purpose of creating, using and/or selling or otherwise distributing + * an EtherCAT network master provided that an EtherCAT Master License is obtained + * from Beckhoff Automation GmbH. + * + * In case you did not receive a copy of the EtherCAT Master License along with + * SOEM write to Beckhoff Automation GmbH, Eiserstraße 5, D-33415 Verl, Germany + * (www.beckhoff.com). + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#define timercmp(a, b, CMP) \ + (((a)->tv_sec == (b)->tv_sec) ? \ + ((a)->tv_usec CMP (b)->tv_usec) : \ + ((a)->tv_sec CMP (b)->tv_sec)) +#define timeradd(a, b, result) \ + do { \ + (result)->tv_sec = (a)->tv_sec + (b)->tv_sec; \ + (result)->tv_usec = (a)->tv_usec + (b)->tv_usec; \ + if ((result)->tv_usec >= 1000000) \ + { \ + ++(result)->tv_sec; \ + (result)->tv_usec -= 1000000; \ + } \ + } while (0) +#define timersub(a, b, result) \ + do { \ + (result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \ + (result)->tv_usec = (a)->tv_usec - (b)->tv_usec; \ + if ((result)->tv_usec < 0) { \ + --(result)->tv_sec; \ + (result)->tv_usec += 1000000; \ + } \ + } while (0) + +#define USECS_PER_SEC 1000000 + +/* OBS! config worker threads must have higher prio that task running ec_configuration */ +#define ECAT_TASK_PRIO_HIGH 20 /* Priority for high performance network task */ +#define ECAT_TASK_PRIO_LOW 80 /* Priority for high performance network task */ +#define ECAT_STACK_SIZE 10000 /* Stack size for high performance task */ +static int ecatTaskOptions = VX_SUPERVISOR_MODE | VX_UNBREAKABLE; +static int ecatTaskIndex = 0; + +#ifndef use_task_delay +#define use_task_delay 1 +#endif + +int osal_usleep (uint32 usec) +{ +#if (use_task_delay == 1) + /* Task delay 0 only yields */ + taskDelay(usec / 1000); + return 0; +#else + /* The suspension may be longer than requested due to the rounding up of + * the request to the timer's resolution or to other scheduling activities + * (e.g., a higher priority task intervenes). + */ + struct timespec ts; + ts.tv_sec = usec / USECS_PER_SEC; + ts.tv_nsec = (usec % USECS_PER_SEC) * 1000; + return nanosleep(&ts, NULL); +#endif + +} + +int osal_gettimeofday(struct timeval *tv, struct timezone *tz) +{ + int return_value; + + return_value = gettimeofday(tv, tz); + + return return_value; +} + +ec_timet osal_current_time(void) +{ + struct timeval current_time; + ec_timet return_value; + + osal_gettimeofday(¤t_time, 0); + return_value.sec = current_time.tv_sec; + return_value.usec = current_time.tv_usec; + return return_value; +} + +void osal_time_diff(ec_timet *start, ec_timet *end, ec_timet *diff) +{ + diff->sec = end->sec - start->sec; + diff->usec = end->usec - start->usec; + if (diff->usec < 0) { + --diff->sec; + diff->usec += 1000000; + } +} + +void osal_timer_start(osal_timert * self, uint32 timeout_usec) +{ + struct timeval start_time; + struct timeval timeout; + struct timeval stop_time; + + osal_gettimeofday(&start_time, 0); + timeout.tv_sec = timeout_usec / USECS_PER_SEC; + timeout.tv_usec = timeout_usec % USECS_PER_SEC; + timeradd(&start_time, &timeout, &stop_time); + + self->stop_time.sec = stop_time.tv_sec; + self->stop_time.usec = stop_time.tv_usec; +} + +boolean osal_timer_is_expired (osal_timert * self) +{ + struct timeval current_time; + struct timeval stop_time; + int is_not_yet_expired; + + osal_gettimeofday(¤t_time, 0); + stop_time.tv_sec = self->stop_time.sec; + stop_time.tv_usec = self->stop_time.usec; + is_not_yet_expired = timercmp(¤t_time, &stop_time, <); + + return is_not_yet_expired == FALSE; +} + +void *osal_malloc(size_t size) +{ + return malloc(size); +} + +void osal_free(void *ptr) +{ + free(ptr); +} + +int osal_thread_create(void *thandle, int stacksize, void *func, void *param) +{ + char task_name[20]; + TASK_ID * tid = (TASK_ID *)thandle; + FUNCPTR func_ptr = func; + _Vx_usr_arg_t arg1 = (_Vx_usr_arg_t)param; + + snprintf(task_name,sizeof(task_name),"worker_%d",ecatTaskIndex++); + + *tid = taskSpawn (task_name, ECAT_TASK_PRIO_LOW, + ecatTaskOptions, ECAT_STACK_SIZE, + func_ptr, arg1, 0, 0, 0, 0, 0, 0, 0, 0, 0); + if(*tid == TASK_ID_ERROR) + { + return 0; + } + + return 1; +} + +int osal_thread_create_rt(void *thandle, int stacksize, void *func, void *param) +{ + char task_name[20]; + TASK_ID * tid = (TASK_ID *)thandle; + FUNCPTR func_ptr = func; + _Vx_usr_arg_t arg1 = (_Vx_usr_arg_t)param; + + snprintf(task_name,sizeof(task_name),"worker_rt_%d",ecatTaskIndex++); + + *tid = taskSpawn (task_name, ECAT_TASK_PRIO_HIGH, + ecatTaskOptions, ECAT_STACK_SIZE, + func_ptr, arg1, 0, 0, 0, 0, 0, 0, 0, 0, 0); + + if(*tid == TASK_ID_ERROR) + { + return 0; + } + return 1; +} + diff --git a/osal/vxworks/osal_defs.h b/osal/vxworks/osal_defs.h new file mode 100644 index 0000000..e5e48be --- /dev/null +++ b/osal/vxworks/osal_defs.h @@ -0,0 +1,55 @@ +/* + * Simple Open EtherCAT Master Library + * + * File : osal_defs.h + * Version : 1.3.1 + * Date : 10-01-2017 + * Copyright (C) 2005-2017 Speciaal Machinefabriek Ketels v.o.f. + * Copyright (C) 2005-2017 Arthur Ketels + * Copyright (C) 2008-2009 TU/e Technische Universiteit Eindhoven + * Copyright (C) 2012-2017 rt-labs AB , Sweden + * + * SOEM is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 as published by the Free + * Software Foundation. + * + * SOEM is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * As a special exception, if other files instantiate templates or use macros + * or inline functions from this file, or you compile this file and link it + * with other works to produce a work based on this file, this file does not + * by itself cause the resulting work to be covered by the GNU General Public + * License. However the source code for this file must still be made available + * in accordance with section (3) of the GNU General Public License. + * + * This exception does not invalidate any other reasons why a work based on + * this file might be covered by the GNU General Public License. + * + * The EtherCAT Technology, the trade name and logo “EtherCAT” are the intellectual + * property of, and protected by Beckhoff Automation GmbH. You can use SOEM for + * the sole purpose of creating, using and/or selling or otherwise distributing + * an EtherCAT network master provided that an EtherCAT Master License is obtained + * from Beckhoff Automation GmbH. + * + * In case you did not receive a copy of the EtherCAT Master License along with + * SOEM write to Beckhoff Automation GmbH, Eiserstraße 5, D-33415 Verl, Germany + * (www.beckhoff.com). + */ + +#ifndef _osal_defs_ +#define _osal_defs_ + +#ifndef PACKED +#define PACKED_BEGIN +#define PACKED __attribute__((__packed__)) +#define PACKED_END +#endif + +#define OSAL_THREAD_HANDLE TASK_ID +#define OSAL_THREAD_FUNC void +#define OSAL_THREAD_FUNC_RT void + +#endif diff --git a/oshw/vxworks/nicdrv.c b/oshw/vxworks/nicdrv.c new file mode 100644 index 0000000..2009290 --- /dev/null +++ b/oshw/vxworks/nicdrv.c @@ -0,0 +1,956 @@ +/* + * Simple Open EtherCAT Master Library + * + * File : nicdrv.c + * Version : 1.3.1 + * Date : 10-01-2017 + * Copyright (C) 2005-2017 Speciaal Machinefabriek Ketels v.o.f. + * Copyright (C) 2005-2017 Arthur Ketels + * Copyright (C) 2008-2009 TU/e Technische Universiteit Eindhoven + * Copyright (C) 2014-2017 rt-labs AB , Sweden + * + * SOEM is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 as published by the Free + * Software Foundation. + * + * SOEM is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * As a special exception, if other files instantiate templates or use macros + * or inline functions from this file, or you compile this file and link it + * with other works to produce a work based on this file, this file does not + * by itself cause the resulting work to be covered by the GNU General Public + * License. However the source code for this file must still be made available + * in accordance with section (3) of the GNU General Public License. + * + * This exception does not invalidate any other reasons why a work based on + * this file might be covered by the GNU General Public License. + * + * The EtherCAT Technology, the trade name and logo “EtherCAT” are the intellectual + * property of, and protected by Beckhoff Automation GmbH. You can use SOEM for + * the sole purpose of creating, using and/or selling or otherwise distributing + * an EtherCAT network master provided that an EtherCAT Master License is obtained + * from Beckhoff Automation GmbH. + * + * In case you did not receive a copy of the EtherCAT Master License along with + * SOEM write to Beckhoff Automation GmbH, Eiserstraße 5, D-33415 Verl, Germany + * (www.beckhoff.com). + */ + +/** \file + * \brief + * EtherCAT VxWorks SNARF Mux driver. + * + * Low level interface functions to send and receive EtherCAT packets. + * EtherCAT has the property that packets are only send by the master, + * and the send packets always return in the receive buffer. + * There can be multiple packets "on the wire" before they return. + * To combine the received packets with the original send packets a buffer + * system is installed. The identifier is put in the index item of the + * EtherCAT header. The index is stored and compared when a frame is recieved. + * If there is a match the packet can be combined with the transmit packet + * and returned to the higher level function. + * + * If EtherCAT is run in parallel with normal IP traffic and EtherCAT have a + * dedicated NIC, instatiate an extra tNetX task and redirect the NIC workQ + * to be handle by the extra tNetX task, if needed raise the tNetX task prio. + * This prevents from having tNet0 becoming a bootleneck. + * + * The "redundant" option will configure two Mux drivers and two NIC interfaces. + * Slaves are connected to both interfaces, one on the IN port and one on the + * OUT port. Packets are send via both interfaces. Any one of the connections + * (also an interconnect) can be removed and the slaves are still serviced with + * packets. The software layer will detect the possible failure modes and + * compensate. If needed the packets from interface A are resend through interface B. + * This layer if fully transparent for the higher layers. + */ + +#include +#include +#include +#include +#include + +#include "oshw.h" +#include "osal.h" +#include "nicdrv.h" + +#define NIC_DEBUG /* Print debugging info? */ + +// wvEvent flags +#define ECAT_RECV_FAILED 0x664 +#define ECAT_RECV_OK 0x665 +#define ECAT_RECV_RETRY_OK 0x666 +#define ECAT_STACK_RECV 0x667 +#define ECAT_SEND_START 0x675 +#define ECAT_SEND_COMPLETE 0x676 +#define ECAT_SEND_FAILED 0x677 + +#ifdef NIC_DEBUG + +#define NIC_LOGMSG(x,a,b,c,d,e,f) \ + do { \ + logMsg (x,a,b,c,d,e,f); \ + } while (0) +#define NIC_WVEVENT(a,b,c) \ + do { \ + wvEvent(a, b, c); \ + } while (0) + + +#else +#define NIC_LOGMSG(x,a,b,c,d,e,f) +#define NIC_WVEVENT(a,b,c) +#endif /* NIC_DEBUG */ + +#define IF_NAME_SIZE 8 + +/** Redundancy modes */ +enum +{ + /** No redundancy, single NIC mode */ + ECT_RED_NONE, + /** Double redundant NIC connecetion */ + ECT_RED_DOUBLE +}; + + +/** Primary source MAC address used for EtherCAT. + * This address is not the MAC address used from the NIC. + * EtherCAT does not care about MAC addressing, but it is used here to + * differentiate the route the packet traverses through the EtherCAT + * segment. This is needed to find out the packet flow in redundant + * configurations. */ +const uint16 priMAC[3] = { 0x0101, 0x0101, 0x0101 }; +/** Secondary source MAC address used for EtherCAT. */ +const uint16 secMAC[3] = { 0x0404, 0x0404, 0x0404 }; + +/** second MAC word is used for identification */ +#define RX_PRIM priMAC[1] +/** second MAC word is used for identification */ +#define RX_SEC secMAC[1] + +/** Receive hook called by Mux driver. */ +static int mux_rx_callback(void* pCookie, long type, M_BLK_ID pMblk, LL_HDR_INFO *llHdrInfo, void *muxUserArg); + +static void ecx_clear_rxbufstat(int *rxbufstat) +{ + int i; + for(i = 0; i < EC_MAXBUF; i++) + { + rxbufstat[i] = EC_BUF_EMPTY; + } +} + +void print_nicversion(void) +{ + printf("Generic is used\n"); +} + +/** Basic setup to connect NIC to socket. +* @param[in] port = port context struct +* @param[in] ifname = Name of NIC device, f.e. "gei0" +* @param[in] secondary = if >0 then use secondary stack instead of primary +* @return >0 if succeeded +*/ +int ecx_setupnic(ecx_portt *port, const char *ifname, int secondary) +{ + int i; + char ifn[IF_NAME_SIZE]; + int unit_no = -1; + ETHERCAT_PKT_DEV * pPktDev; + + /* Make refrerece to packet device struct, keep track if the packet + * device is the redundant or not. + */ + if (secondary) + { + pPktDev = &(port->redport->pktDev); + pPktDev->redundant = 1; + } + else + { + pPktDev = &(port->pktDev); + pPktDev->redundant = 0; + } + + /* Clear frame counters*/ + pPktDev->tx_count = 0; + pPktDev->rx_count = 0; + pPktDev->overrun_count = 0; + + /* Create multi-thread support semaphores */ + port->sem_get_index = semBCreate(SEM_Q_FIFO, SEM_FULL); + + /* Get the dev name and unit from ifname + * We assume form gei1, fei0... + */ + memset(ifn,0x0,sizeof(ifn)); + + for(i=0; i < strlen(ifname);i++) + { + if(isdigit(ifname[i])) + { + strncpy(ifn, ifname, i); + unit_no = atoi(&ifname[i]); + break; + } + } + + /* Detach IP stack */ + //ipDetach(pktDev.unit,pktDev.name); + + pPktDev->port = port; + + /* Bind to mux driver for given interface, include ethercat driver pointer + * as user reference + */ + /* Bind to mux */ + pPktDev->pCookie = muxBind(ifn, unit_no, mux_rx_callback, NULL, NULL, NULL, MUX_PROTO_SNARF, "ECAT SNARF", pPktDev); + + if (pPktDev->pCookie == NULL) + { + /* fail */ + NIC_LOGMSG("ecx_setupnic: muxBind init for gei: %d failed\n", unit_no, 2, 3, 4, 5, 6); + goto exit; + } + + /* Get reference tp END obje */ + pPktDev->endObj = endFindByName(ifn, unit_no); + + if (port->pktDev.endObj == NULL) + { + /* fail */ + NIC_LOGMSG("error_hook: endFindByName failed, device gei: %d not found\n", + unit_no, 2, 3, 4, 5, 6); + goto exit; + } + + if (secondary) + { + /* secondary port struct available? */ + if (port->redport) + { + port->redstate = ECT_RED_DOUBLE; + port->redport->stack.txbuf = &(port->txbuf); + port->redport->stack.txbuflength = &(port->txbuflength); + port->redport->stack.rxbuf = &(port->redport->rxbuf); + port->redport->stack.rxbufstat = &(port->redport->rxbufstat); + port->redport->stack.rxsa = &(port->redport->rxsa); + /* Create mailboxes for each potential EtherCAT frame index */ + for (i = 0; i < EC_MAXBUF; i++) + { + port->redport->msgQId[i] = msgQCreate(1, sizeof(M_BLK_ID), MSG_Q_FIFO); + if (port->redport->msgQId[i] == MSG_Q_ID_NULL) + { + NIC_LOGMSG("ecx_setupnic: Failed to create redundant MsgQ[%d]", + i, 2, 3, 4, 5, 6); + goto exit; + } + } + ecx_clear_rxbufstat(&(port->redport->rxbufstat[0])); + } + else + { + /* fail */ + NIC_LOGMSG("ecx_setupnic: Redundant port not allocated", + unit_no, 2, 3, 4, 5, 6); + goto exit; + } + } + else + { + port->lastidx = 0; + port->redstate = ECT_RED_NONE; + port->stack.txbuf = &(port->txbuf); + port->stack.txbuflength = &(port->txbuflength); + port->stack.rxbuf = &(port->rxbuf); + port->stack.rxbufstat = &(port->rxbufstat); + port->stack.rxsa = &(port->rxsa); + /* Create mailboxes for each potential EtherCAT frame index */ + for (i = 0; i < EC_MAXBUF; i++) + { + port->msgQId[i] = msgQCreate(1, sizeof(M_BLK_ID), MSG_Q_FIFO); + if (port->msgQId[i] == MSG_Q_ID_NULL) + { + NIC_LOGMSG("ecx_setupnic: Failed to create MsgQ[%d]", + i, 2, 3, 4, 5, 6); + goto exit; + } + } + ecx_clear_rxbufstat(&(port->rxbufstat[0])); + } + + /* setup ethernet headers in tx buffers so we don't have to repeat it */ + for (i = 0; i < EC_MAXBUF; i++) + { + ec_setupheader(&(port->txbuf[i])); + port->rxbufstat[i] = EC_BUF_EMPTY; + } + ec_setupheader(&(port->txbuf2)); + + return 1; + +exit: + + return 0; + +} + +/** Close sockets used + * @param[in] port = port context struct + * @return 0 + */ +int ecx_closenic(ecx_portt *port) +{ + int i; + ETHERCAT_PKT_DEV * pPktDev; + + pPktDev = &(port->pktDev); + + for (i = 0; i < EC_MAXBUF; i++) + { + if (port->msgQId[i] != MSG_Q_ID_NULL) + { + msgQDelete(port->msgQId[i]); + } + } + + if (pPktDev->pCookie != NULL) + { + muxUnbind(pPktDev->pCookie, MUX_PROTO_SNARF, mux_rx_callback); + } + + /* Clean redundant resources if available*/ + if (port->redport) + { + pPktDev = &(port->redport->pktDev); + for (i = 0; i < EC_MAXBUF; i++) + { + if (port->redport->msgQId[i] != MSG_Q_ID_NULL) + { + msgQDelete(port->redport->msgQId[i]); + } + } + if (pPktDev->pCookie != NULL) + { + muxUnbind(pPktDev->pCookie, MUX_PROTO_SNARF, mux_rx_callback); + } + } + + return 0; +} + +/** Fill buffer with ethernet header structure. + * Destination MAC is allways broadcast. + * Ethertype is allways ETH_P_ECAT. + * @param[out] p = buffer + */ +void ec_setupheader(void *p) +{ + ec_etherheadert *bp; + bp = p; + bp->da0 = htons(0xffff); + bp->da1 = htons(0xffff); + bp->da2 = htons(0xffff); + bp->sa0 = htons(priMAC[0]); + bp->sa1 = htons(priMAC[1]); + bp->sa2 = htons(priMAC[2]); + bp->etype = htons(ETH_P_ECAT); +} + +/** Get new frame identifier index and allocate corresponding rx buffer. + * @param[in] port = port context struct + * @return new index. + */ +int ecx_getindex(ecx_portt *port) +{ + int idx; + int cnt; + MSG_Q_ID msgQId; + M_BLK_ID trash_can; + + semTake(port->sem_get_index, WAIT_FOREVER); + + idx = port->lastidx + 1; + /* index can't be larger than buffer array */ + if (idx >= EC_MAXBUF) + { + idx = 0; + } + cnt = 0; + /* try to find unused index */ + while ((port->rxbufstat[idx] != EC_BUF_EMPTY) && (cnt < EC_MAXBUF)) + { + idx++; + cnt++; + if (idx >= EC_MAXBUF) + { + idx = 0; + } + } + port->rxbufstat[idx] = EC_BUF_ALLOC; + + /* Clean up any abandoned frames */ + msgQId = port->msgQId[idx]; + if (msgQReceive(msgQId, (char *)&trash_can, sizeof(M_BLK_ID), NO_WAIT) == OK) + { + /* Free resources */ + netMblkClChainFree(trash_can); + } + + if (port->redstate != ECT_RED_NONE) + { + port->redport->rxbufstat[idx] = EC_BUF_ALLOC; + /* Clean up any abandoned frames */ + msgQId = port->redport->msgQId[idx]; + if (msgQReceive(msgQId, (char *)&trash_can, sizeof(M_BLK_ID), NO_WAIT) == OK) + { + /* Free resources */ + netMblkClChainFree(trash_can); + } + } + + port->lastidx = idx; + + semGive(port->sem_get_index); + + return idx; +} + +/** Set rx buffer status. + * @param[in] port = port context struct + * @param[in] idx = index in buffer array + * @param[in] bufstat = status to set + */ +void ecx_setbufstat(ecx_portt *port, int idx, int bufstat) +{ + port->rxbufstat[idx] = bufstat; + if (port->redstate != ECT_RED_NONE) + port->redport->rxbufstat[idx] = bufstat; +} + +/** Low level transmit buffer over mux layer 2 driver +* +* @param[in] pPktDev = packet device to send buffer over +* @param[in] buf = buff to send +* @param[in] len = bytes to send +* @return driver send result +*/ +static int ec_outfram_send(ETHERCAT_PKT_DEV * pPktDev, void * buf, int len) +{ + STATUS status = OK; + M_BLK_ID pPacket; + int rval = 0; + + /* Allocate m_blk to send */ + if ((pPacket = netTupleGet(pPktDev->endObj->pNetPool, + len, + M_DONTWAIT, + MT_DATA, + TRUE)) == NULL) + { + NIC_LOGMSG("ec_outfram_send: Could not allocate MBLK memory!\n", 1, 2, 3, 4, 5, 6); + return ERROR; + } + + pPacket->mBlkHdr.mLen = len; + pPacket->mBlkHdr.mFlags |= M_HEADER; + pPacket->mBlkHdr.mData = pPacket->pClBlk->clNode.pClBuf; + + netMblkFromBufCopy(pPacket, buf, 0, pPacket->mBlkHdr.mLen, M_DONTWAIT, NULL); + status = muxTkSend(pPktDev->pCookie, pPacket, NULL, htons(ETH_P_ECAT), NULL); + + if (status == OK) + { + rval = len; + NIC_WVEVENT(ECAT_SEND_COMPLETE, (char *)&rval, sizeof(rval)); + } + else + { + netMblkClChainFree(pPacket); + /* fail */ + static int print_once; + if (print_once == 0) + { + NIC_LOGMSG("ec_outfram_send: failed\n", + 1, 2, 3, 4, 5, 6); + print_once = 1; + } + NIC_WVEVENT(ECAT_SEND_FAILED, (char *)&rval, sizeof(rval)); + } + + return rval; +} + +/** High level transmit buffer over mux layer 2 driver, passing buffer +* and packet device to send on as arguments +* @param[in] port = port context holding reference to packet device +* @param[in] idx = index in tx buffer array +* @param[in] stacknumber = 0=Primary 1=Secondary stack +* @return socket send result +*/ +int ecx_outframe(ecx_portt *port, int idx, int stacknumber) +{ + int rval = 0; + ec_stackT *stack; + ETHERCAT_PKT_DEV * pPktDev; + + if (!stacknumber) + { + stack = &(port->stack); + pPktDev = &(port->pktDev); + } + else + { + stack = &(port->redport->stack); + pPktDev = &(port->redport->pktDev); + } + + rval = ec_outfram_send(pPktDev, (char*)(*stack->txbuf)[idx], + (*stack->txbuflength)[idx]); + if (rval > 0) + { + (*stack->rxbufstat)[idx] = EC_BUF_TX; + port->pktDev.tx_count++; + } + + return rval; +} + +/** High level transmit frame to send as index. + * @param[in] port = port context + * @param[in] idx = index in tx buffer array + * @return socket send result + */ +int ecx_outframe_red(ecx_portt *port, int idx) +{ + ec_comt *datagramP; + ec_etherheadert *ehp; + int rval; + + ehp = (ec_etherheadert *)&(port->txbuf[idx]); + /* rewrite MAC source address 1 to primary */ + ehp->sa1 = htons(priMAC[1]); + /* transmit over primary socket*/ + rval = ecx_outframe(port, idx, 0); + if (port->redstate != ECT_RED_NONE) + { + ehp = (ec_etherheadert *)&(port->txbuf2); + /* use dummy frame for secondary socket transmit (BRD) */ + datagramP = (ec_comt*)&(port->txbuf2[ETH_HEADERSIZE]); + /* write index to frame */ + datagramP->index = idx; + /* rewrite MAC source address 1 to secondary */ + ehp->sa1 = htons(secMAC[1]); + /* transmit over secondary interface */ + rval = ec_outfram_send(&(port->redport->pktDev), &(port->txbuf2), port->txbuflength2); + port->redport->rxbufstat[idx] = EC_BUF_TX; + } + + return rval; +} + + +/** Call back routine registered as hook with mux layer 2 driver +* @param[in] pCookie = Mux cookie +* @param[in] type = recived type +* @param[in] pMblk = the received packet reference +* @param[in] llHdrInfo = header info +* @param[in] muxUserArg = assigned reference to packet device when init called +* @return TRUE if frame was succesfully read and passed to MsgQ +*/ +static int mux_rx_callback(void* pCookie, long type, M_BLK_ID pMblk, LL_HDR_INFO *llHdrInfo, void *muxUserArg) +{ + BOOL ret = FALSE; + int idxf; + ec_comt *ecp; + ec_bufT * tempbuf; + ecx_portt * port; + MSG_Q_ID msgQId; + ETHERCAT_PKT_DEV * pPktDev; + int length; + + /* check if it is an EtherCAT frame */ + if (type == ETH_P_ECAT) + { + length = pMblk->mBlkHdr.mLen; + tempbuf = (ec_bufT *)pMblk->mBlkHdr.mData; + pPktDev = (ETHERCAT_PKT_DEV *)muxUserArg; + port = pPktDev->port; + + /* Get ethercat frame header */ + ecp = (ec_comt*)&(*tempbuf)[ETH_HEADERSIZE]; + idxf = ecp->index; + if (idxf >= EC_MAXBUF) + { + NIC_LOGMSG("mux_rx_callback: idx %d out of bounds\n", idxf, + 2, 3, 4, 5, 6); + return ret; + } + + /* Check if it is the redundant port or not */ + if (pPktDev->redundant == 1) + { + msgQId = port->redport->msgQId[idxf]; + } + else + { + msgQId = port->msgQId[idxf]; + } + + if (length > 0) + { + /* Post the frame to the reqceive Q for the EtherCAT stack */ + STATUS status; + status = msgQSend(msgQId, (char *)&pMblk, sizeof(M_BLK_ID), + NO_WAIT, MSG_PRI_NORMAL); + if (status == OK) + { + NIC_WVEVENT(ECAT_RECV_OK, (char *)&length, sizeof(length)); + ret = TRUE; + } + else if ((status == ERROR) && (errno == S_objLib_OBJ_UNAVAILABLE)) + { + /* Try to empty the MSGQ since we for some strange reason + * already have a frame in the MsqQ, + * is it due to timeout when receiving? + * We want the latest received frame in the buffer + */ + port->pktDev.overrun_count++; + NIC_LOGMSG("mux_rx_callback: idx %d MsgQ overrun\n", idxf, + 2, 3, 4, 5, 6); + M_BLK_ID trash_can; + if (msgQReceive(msgQId, (char *)&trash_can, + sizeof(M_BLK_ID), NO_WAIT) == OK) + { + /* Free resources */ + netMblkClChainFree(trash_can); + } + + status = msgQSend(msgQId, (char *)&pMblk, sizeof(M_BLK_ID), + NO_WAIT, MSG_PRI_NORMAL); + if (status == OK) + { + NIC_WVEVENT(ECAT_RECV_RETRY_OK, (char *)&length, sizeof(length)); + ret = TRUE; + } + } + else + { + NIC_WVEVENT(ECAT_RECV_FAILED, (char *)&length, sizeof(length)); + } + } + } + + return ret; +} + +/** Non blocking or blocking read, if we use timeout we pass minimum 1 tick. + * @param[in] port = port context struct + * @param[in] pMblk = mBlk for received frame + * @param[in] timeout = timeout in us + * @return >0 if frame is available and read + */ +static int ecx_recvpkt(ecx_portt *port, int idx, int stacknumber, M_BLK_ID * pMblk, int timeout) +{ + int bytesrx = 0; + MSG_Q_ID msgQId; + int tick_timeout = max((timeout / 1000), 1); + + if (stacknumber == 1) + { + msgQId = port->redport->msgQId[idx]; + } + else + { + msgQId = port->msgQId[idx]; + } + + if (timeout == 0) + { + bytesrx = msgQReceive(msgQId, (void *)pMblk, + sizeof(M_BLK_ID), NO_WAIT); + } + else + { + bytesrx = msgQReceive(msgQId, (void *)pMblk, + sizeof(M_BLK_ID), tick_timeout); + } + + if (bytesrx > 0) + { + bytesrx = (*pMblk)->mBlkHdr.mLen; + NIC_WVEVENT(ECAT_STACK_RECV, (char *)&bytesrx, sizeof(bytesrx)); + } + + + return (bytesrx > 0); +} + +/** Non blocking receive frame function. Uses RX buffer and index to combine + * read frame with transmitted frame. Frames are received by separate receiver + * task tNet0 (default), tNet0 fetch what frame index and store a reference to the + * received frame in matching MsgQ. The stack user tasks fetch the frame + * reference and copies the frame the the RX buffer, when done it free + * the frame buffer alloctaed by the Mux. + * + * @param[in] port = port context struct + * @param[in] idx = requested index of frame + * @param[in] stacknumber = 0=primary 1=secondary stack + * @param[in] timeout = timeout in us + * @return Workcounter if a frame is found with corresponding index, otherwise + * EC_NOFRAME or EC_OTHERFRAME. + */ +int ecx_inframe(ecx_portt *port, int idx, int stacknumber, int timeout) +{ + uint16 l; + int rval; + ec_etherheadert *ehp; + ec_comt *ecp; + ec_stackT *stack; + ec_bufT *rxbuf; + ec_bufT *tempinbuf; + M_BLK_ID pMblk; + + if (!stacknumber) + { + stack = &(port->stack); + } + else + { + stack = &(port->redport->stack); + } + rval = EC_NOFRAME; + rxbuf = &(*stack->rxbuf)[idx]; + /* (non-) blocking call to retrieve frame from Muxlayer */ + if (ecx_recvpkt(port, idx, stacknumber, &pMblk, timeout)) + { + rval = EC_OTHERFRAME; + + /* Get pointer to the frame */ + tempinbuf = (ec_bufT *)pMblk->mBlkHdr.mData; + /* Get pointer to the Ethernet header */ + ehp = (ec_etherheadert*)tempinbuf; + /* Get pointer to the EtherCAT frame as ethernet payload */ + ecp = (ec_comt*)&(*tempinbuf)[ETH_HEADERSIZE]; + l = etohs(ecp->elength) & 0x0fff; + /* yes, put it in the buffer array (strip ethernet header) */ + netMblkOffsetToBufCopy(pMblk, ETH_HEADERSIZE,(void *) rxbuf, + (*stack->txbuflength)[idx] - ETH_HEADERSIZE, NULL); + + /* return WKC */ + rval = ((*rxbuf)[l] + ((uint16)((*rxbuf)[l + 1]) << 8)); + /* mark as completed */ + (*stack->rxbufstat)[idx] = EC_BUF_COMPLETE; + /* store MAC source word 1 for redundant routing info */ + (*stack->rxsa)[idx] = ntohs(ehp->sa1); + netMblkClChainFree(pMblk); + port->pktDev.rx_count++; + } + + /* WKC if matching frame found */ + return rval; +} + +/** Blocking redundant receive frame function. If redundant mode is not active then + * it skips the secondary stack and redundancy functions. In redundant mode it waits + * for both (primary and secondary) frames to come in. The result goes in an decision + * tree that decides, depending on the route of the packet and its possible missing arrival, + * how to reroute the original packet to get the data in an other try. + * + * @param[in] port = port context struct + * @param[in] idx = requested index of frame + * @param[in] timer = absolute timeout time + * @param[in] timeout = timeout in us + * @return Workcounter if a frame is found with corresponding index, otherwise + * EC_NOFRAME. + */ +static int ecx_waitinframe_red(ecx_portt *port, int idx, osal_timert *timer, int timeout) +{ + osal_timert timer2; + int wkc = EC_NOFRAME; + int wkc2 = EC_NOFRAME; + int primrx, secrx; + + /* if not in redundant mode then always assume secondary is OK */ + if (port->redstate == ECT_RED_NONE) + { + wkc2 = 0; + } + do + { + /* only read frame if not already in */ + if (wkc <= EC_NOFRAME) + { + wkc = ecx_inframe(port, idx, 0, timeout); + } + /* only try secondary if in redundant mode */ + if (port->redstate != ECT_RED_NONE) + { + /* only read frame if not already in */ + if (wkc2 <= EC_NOFRAME) + { + wkc2 = ecx_inframe(port, idx, 1, timeout); + } + } + /* wait for both frames to arrive or timeout */ + } while (((wkc <= EC_NOFRAME) || (wkc2 <= EC_NOFRAME)) && !osal_timer_is_expired(timer)); + /* only do redundant functions when in redundant mode */ + if (port->redstate != ECT_RED_NONE) + { + /* primrx if the reveived MAC source on primary socket */ + primrx = 0; + if (wkc > EC_NOFRAME) primrx = port->rxsa[idx]; + /* secrx if the reveived MAC source on psecondary socket */ + secrx = 0; + if (wkc2 > EC_NOFRAME) secrx = port->redport->rxsa[idx]; + + /* primary socket got secondary frame and secondary socket got primary frame */ + /* normal situation in redundant mode */ + if ( ((primrx == RX_SEC) && (secrx == RX_PRIM)) ) + { + /* copy secondary buffer to primary */ + memcpy(&(port->rxbuf[idx]), &(port->redport->rxbuf[idx]), port->txbuflength[idx] - ETH_HEADERSIZE); + wkc = wkc2; + } + /* primary socket got nothing or primary frame, and secondary socket got secondary frame */ + /* we need to resend TX packet */ + if ( ((primrx == 0) && (secrx == RX_SEC)) || + ((primrx == RX_PRIM) && (secrx == RX_SEC)) ) + { + /* If both primary and secondary have partial connection retransmit the primary received + * frame over the secondary socket. The result from the secondary received frame is a combined + * frame that traversed all slaves in standard order. */ + if ( (primrx == RX_PRIM) && (secrx == RX_SEC) ) + { + /* copy primary rx to tx buffer */ + memcpy(&(port->txbuf[idx][ETH_HEADERSIZE]), &(port->rxbuf[idx]), port->txbuflength[idx] - ETH_HEADERSIZE); + } + osal_timer_start (&timer2, EC_TIMEOUTRET); + /* resend secondary tx */ + ecx_outframe(port, idx, 1); + do + { + /* retrieve frame */ + wkc2 = ecx_inframe(port, idx, 1, timeout); + } while ((wkc2 <= EC_NOFRAME) && !osal_timer_is_expired(&timer2)); + if (wkc2 > EC_NOFRAME) + { + /* copy secondary result to primary rx buffer */ + memcpy(&(port->rxbuf[idx]), &(port->redport->rxbuf[idx]), port->txbuflength[idx] - ETH_HEADERSIZE); + wkc = wkc2; + } + } + } + + /* return WKC or EC_NOFRAME */ + return wkc; +} + +/** Blocking receive frame function. Calls ec_waitinframe_red(). + * @param[in] port = port context struct + * @param[in] idx = requested index of frame + * @param[in] timeout = timeout in us + * @return Workcounter if a frame is found with corresponding index, otherwise + * EC_NOFRAME. + */ +int ecx_waitinframe(ecx_portt *port, int idx, int timeout) +{ + int wkc; + osal_timert timer; + + osal_timer_start (&timer, timeout); + wkc = ecx_waitinframe_red(port, idx, &timer, timeout); + + return wkc; +} + +/** Blocking send and recieve frame function. Used for non processdata frames. + * A datagram is build into a frame and transmitted via this function. It waits + * for an answer and returns the workcounter. The function retries if time is + * left and the result is WKC=0 or no frame received. + * + * The function calls ec_outframe_red() and ec_waitinframe_red(). + * + * @param[in] port = port context struct + * @param[in] idx = index of frame + * @param[in] timeout = timeout in us + * @return Workcounter or EC_NOFRAME + */ +int ecx_srconfirm(ecx_portt *port, int idx, int timeout) +{ + int wkc = EC_NOFRAME; + osal_timert timer1, timer2; + + osal_timer_start (&timer1, timeout); + do + { + /* tx frame on primary and if in redundant mode a dummy on secondary */ + ecx_outframe_red(port, idx); + if (timeout < EC_TIMEOUTRET) + { + osal_timer_start (&timer2, timeout); + } + else + { + /* normally use partial timout for rx */ + osal_timer_start (&timer2, EC_TIMEOUTRET); + } + /* get frame from primary or if in redundant mode possibly from secondary */ + wkc = ecx_waitinframe_red(port, idx, &timer2, timeout); + /* wait for answer with WKC>=0 or otherwise retry until timeout */ + } while ((wkc <= EC_NOFRAME) && !osal_timer_is_expired (&timer1)); + + + return wkc; +} + +#ifdef EC_VER1 +int ec_setupnic(const char *ifname, int secondary) +{ + return ecx_setupnic(&ecx_port, ifname, secondary); +} + +int ec_closenic(void) +{ + return ecx_closenic(&ecx_port); +} + +int ec_getindex(void) +{ + return ecx_getindex(&ecx_port); +} + +void ec_setbufstat(int idx, int bufstat) +{ + ecx_setbufstat(&ecx_port, idx, bufstat); +} + +int ec_outframe(int idx, int stacknumber) +{ + return ecx_outframe(&ecx_port, idx, stacknumber); +} + +int ec_outframe_red(int idx) +{ + return ecx_outframe_red(&ecx_port, idx); +} + +int ec_inframe(int idx, int stacknumber) +{ + return ecx_inframe(&ecx_port, idx, stacknumber); +} + +int ec_waitinframe(int idx, int timeout) +{ + return ecx_waitinframe(&ecx_port, idx, timeout); +} + +int ec_srconfirm(int idx, int timeout) +{ + return ecx_srconfirm(&ecx_port, idx, timeout); +} +#endif diff --git a/oshw/vxworks/nicdrv.h b/oshw/vxworks/nicdrv.h new file mode 100644 index 0000000..c153c00 --- /dev/null +++ b/oshw/vxworks/nicdrv.h @@ -0,0 +1,166 @@ +/* + * Simple Open EtherCAT Master Library + * + * File : nicdrv.h + * Version : 1.3.1 + * Date : 10-01-2017 + * Copyright (C) 2005-2017 Speciaal Machinefabriek Ketels v.o.f. + * Copyright (C) 2005-2017 Arthur Ketels + * Copyright (C) 2008-2009 TU/e Technische Universiteit Eindhoven + * Copyright (C) 2014-2017 rt-labs AB , Sweden + * + * SOEM is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 as published by the Free + * Software Foundation. + * + * SOEM is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * As a special exception, if other files instantiate templates or use macros + * or inline functions from this file, or you compile this file and link it + * with other works to produce a work based on this file, this file does not + * by itself cause the resulting work to be covered by the GNU General Public + * License. However the source code for this file must still be made available + * in accordance with section (3) of the GNU General Public License. + * + * This exception does not invalidate any other reasons why a work based on + * this file might be covered by the GNU General Public License. + * + * The EtherCAT Technology, the trade name and logo “EtherCAT” are the intellectual + * property of, and protected by Beckhoff Automation GmbH. You can use SOEM for + * the sole purpose of creating, using and/or selling or otherwise distributing + * an EtherCAT network master provided that an EtherCAT Master License is obtained + * from Beckhoff Automation GmbH. + * + * In case you did not receive a copy of the EtherCAT Master License along with + * SOEM write to Beckhoff Automation GmbH, Eiserstraße 5, D-33415 Verl, Germany + * (www.beckhoff.com). + */ + +/** \file + * \brief + * Headerfile for nicdrv.c + */ + +#ifndef _nicdrvh_ +#define _nicdrvh_ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include +#include + +/** structure to connect EtherCAT stack and VxWorks device */ +typedef struct ETHERCAT_PKT_DEV +{ + struct ecx_port *port; + void *pCookie; + END_OBJ *endObj; + UINT32 redundant; + UINT32 tx_count; + UINT32 rx_count; + UINT32 overrun_count; +}ETHERCAT_PKT_DEV; + +/** pointer structure to Tx and Rx stacks */ +typedef struct +{ + /** tx buffer */ + ec_bufT (*txbuf)[EC_MAXBUF]; + /** tx buffer lengths */ + int (*txbuflength)[EC_MAXBUF]; + /** rx buffers */ + ec_bufT (*rxbuf)[EC_MAXBUF]; + /** rx buffer status fields */ + int (*rxbufstat)[EC_MAXBUF]; + /** received MAC source address (middle word) */ + int (*rxsa)[EC_MAXBUF]; +} ec_stackT; + +/** pointer structure to buffers for redundant port */ +typedef struct ecx_redport +{ + /** Stack reference */ + ec_stackT stack; + /** Packet device instance */ + ETHERCAT_PKT_DEV pktDev; + /** rx buffers */ + ec_bufT rxbuf[EC_MAXBUF]; + /** rx buffer status */ + int rxbufstat[EC_MAXBUF]; + /** rx MAC source address */ + int rxsa[EC_MAXBUF]; + /** MSG Q for receive callbacks to post into */ + MSG_Q_ID msgQId[EC_MAXBUF]; +} ecx_redportt; + +/** pointer structure to buffers, vars and mutexes for port instantiation */ +typedef struct ecx_port +{ + /** Stack reference */ + ec_stackT stack; + /** Packet device instance */ + ETHERCAT_PKT_DEV pktDev; + /** rx buffers */ + ec_bufT rxbuf[EC_MAXBUF]; + /** rx buffer status */ + int rxbufstat[EC_MAXBUF]; + /** rx MAC source address */ + int rxsa[EC_MAXBUF]; + /** transmit buffers */ + ec_bufT txbuf[EC_MAXBUF]; + /** transmit buffer lenghts */ + int txbuflength[EC_MAXBUF]; + /** temporary tx buffer */ + ec_bufT txbuf2; + /** temporary tx buffer length */ + int txbuflength2; + /** last used frame index */ + int lastidx; + /** current redundancy state */ + int redstate; + /** pointer to redundancy port and buffers */ + ecx_redportt *redport; + /** Semaphore to protect single resources */ + SEM_ID sem_get_index; + /** MSG Q for receive callbacks to post into */ + MSG_Q_ID msgQId[EC_MAXBUF]; +} ecx_portt; + +extern const uint16 priMAC[3]; +extern const uint16 secMAC[3]; + +#ifdef EC_VER1 +extern ecx_portt ecx_port; +extern ecx_redportt ecx_redport; + +int ec_setupnic(const char * ifname, int secondary); +int ec_closenic(void); +void ec_setbufstat(int idx, int bufstat); +int ec_getindex(void); +int ec_outframe(int idx, int sock); +int ec_outframe_red(int idx); +int ec_waitinframe(int idx, int timeout); +int ec_srconfirm(int idx,int timeout); +#endif + +void ec_setupheader(void *p); +int ecx_setupnic(ecx_portt *port, const char * ifname, int secondary); +int ecx_closenic(ecx_portt *port); +void ecx_setbufstat(ecx_portt *port, int idx, int bufstat); +int ecx_getindex(ecx_portt *port); +int ecx_outframe(ecx_portt *port, int idx, int sock); +int ecx_outframe_red(ecx_portt *port, int idx); +int ecx_waitinframe(ecx_portt *port, int idx, int timeout); +int ecx_srconfirm(ecx_portt *port, int idx,int timeout); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/oshw/vxworks/oshw.c b/oshw/vxworks/oshw.c new file mode 100644 index 0000000..81fe351 --- /dev/null +++ b/oshw/vxworks/oshw.c @@ -0,0 +1,96 @@ +/* + * Simple Open EtherCAT Master Library + * + * File : oshw.c + * Version : 1.3.1 + * Date : 10-01-2017 + * Copyright (C) 2005-2017 Speciaal Machinefabriek Ketels v.o.f. + * Copyright (C) 2005-2017 Arthur Ketels + * Copyright (C) 2008-2009 TU/e Technische Universiteit Eindhoven + * Copyright (C) 2012-2017 rt-labs AB , Sweden + * + * SOEM is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 as published by the Free + * Software Foundation. + * + * SOEM is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * As a special exception, if other files instantiate templates or use macros + * or inline functions from this file, or you compile this file and link it + * with other works to produce a work based on this file, this file does not + * by itself cause the resulting work to be covered by the GNU General Public + * License. However the source code for this file must still be made available + * in accordance with section (3) of the GNU General Public License. + * + * This exception does not invalidate any other reasons why a work based on + * this file might be covered by the GNU General Public License. + * + * The EtherCAT Technology, the trade name and logo “EtherCAT” are the intellectual + * property of, and protected by Beckhoff Automation GmbH. You can use SOEM for + * the sole purpose of creating, using and/or selling or otherwise distributing + * an EtherCAT network master provided that an EtherCAT Master License is obtained + * from Beckhoff Automation GmbH. + * + * In case you did not receive a copy of the EtherCAT Master License along with + * SOEM write to Beckhoff Automation GmbH, Eiserstraße 5, D-33415 Verl, Germany + * (www.beckhoff.com). + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#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); +} diff --git a/oshw/vxworks/oshw.h b/oshw/vxworks/oshw.h new file mode 100644 index 0000000..79d2497 --- /dev/null +++ b/oshw/vxworks/oshw.h @@ -0,0 +1,59 @@ +/* + * Simple Open EtherCAT Master Library + * + * File : oshw.h + * Version : 1.3.1 + * Date : 10-01-2017 + * Copyright (C) 2005-2017 Speciaal Machinefabriek Ketels v.o.f. + * Copyright (C) 2005-2017 Arthur Ketels + * Copyright (C) 2008-2009 TU/e Technische Universiteit Eindhoven + * Copyright (C) 2012-2017 rt-labs AB , Sweden + * + * SOEM is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 as published by the Free + * Software Foundation. + * + * SOEM is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * As a special exception, if other files instantiate templates or use macros + * or inline functions from this file, or you compile this file and link it + * with other works to produce a work based on this file, this file does not + * by itself cause the resulting work to be covered by the GNU General Public + * License. However the source code for this file must still be made available + * in accordance with section (3) of the GNU General Public License. + * + * This exception does not invalidate any other reasons why a work based on + * this file might be covered by the GNU General Public License. + * + * The EtherCAT Technology, the trade name and logo “EtherCAT” are the intellectual + * property of, and protected by Beckhoff Automation GmbH. You can use SOEM for + * the sole purpose of creating, using and/or selling or otherwise distributing + * an EtherCAT network master provided that an EtherCAT Master License is obtained + * from Beckhoff Automation GmbH. + * + * In case you did not receive a copy of the EtherCAT Master License along with + * SOEM write to Beckhoff Automation GmbH, Eiserstraße 5, D-33415 Verl, Germany + * (www.beckhoff.com). + */ + +/** \file + * \brief + * Headerfile for oshw.c + */ + +#ifndef _oshw_ +#define _oshw_ + +#include "ethercattype.h" +#include "nicdrv.h" +#include "ethercatmain.h" + +uint16 oshw_htons(uint16 hostshort); +uint16 oshw_ntohs(uint16 networkshort); +ec_adaptert * oshw_find_adapters(void); +void oshw_free_adapters(ec_adaptert * adapter); + +#endif