nicdrv.c

Go to the documentation of this file.
00001 /*
00002  * Simple Open EtherCAT Master Library 
00003  *
00004  * File    : nicdrv.c
00005  * Version : 1.2.5
00006  * Date    : 09-04-2011
00007  * Copyright (C) 2005-2011 Speciaal Machinefabriek Ketels v.o.f.
00008  * Copyright (C) 2005-2011 Arthur Ketels
00009  * Copyright (C) 2008-2009 TU/e Technische Universiteit Eindhoven 
00010  *
00011  * SOEM is free software; you can redistribute it and/or modify it under
00012  * the terms of the GNU General Public License version 2 as published by the Free
00013  * Software Foundation.
00014  *
00015  * SOEM is distributed in the hope that it will be useful, but WITHOUT ANY
00016  * WARRANTY; without even the implied warranty of MERCHANTABILITY or
00017  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
00018  * for more details.
00019  *
00020  * As a special exception, if other files instantiate templates or use macros
00021  * or inline functions from this file, or you compile this file and link it
00022  * with other works to produce a work based on this file, this file does not
00023  * by itself cause the resulting work to be covered by the GNU General Public
00024  * License. However the source code for this file must still be made available
00025  * in accordance with section (3) of the GNU General Public License.
00026  *
00027  * This exception does not invalidate any other reasons why a work based on
00028  * this file might be covered by the GNU General Public License.
00029  *
00030  * The EtherCAT Technology, the trade name and logo “EtherCAT” are the intellectual
00031  * property of, and protected by Beckhoff Automation GmbH. You can use SOEM for
00032  * the sole purpose of creating, using and/or selling or otherwise distributing
00033  * an EtherCAT network master provided that an EtherCAT Master License is obtained
00034  * from Beckhoff Automation GmbH.
00035  *
00036  * In case you did not receive a copy of the EtherCAT Master License along with
00037  * SOEM write to Beckhoff Automation GmbH, Eiserstraße 5, D-33415 Verl, Germany
00038  * (www.beckhoff.com).
00039  */
00040 
00041 /** \file
00042  * \brief
00043  * EtherCAT RAW socket driver.
00044  *
00045  * Low level interface functions to send and receive EtherCAT packets.
00046  * EtherCAT has the property that packets are only send by the master,
00047  * and the send packets allways return in the receive buffer.
00048  * There can be multiple packets "on the wire" before they return.
00049  * To combine the received packets with the original send packets a buffer
00050  * system is installed. The identifier is put in the index item of the
00051  * EtherCAT header. The index is stored and compared when a frame is recieved.
00052  * If there is a match the packet can be combined with the transmit packet
00053  * and returned to the higher level function.
00054  *
00055  * The socket layer can exhibit a reversal in the packet order (rare).
00056  * If the Tx order is A-B-C the return order could be A-C-B. The indexed buffer
00057  * will reorder the packets automatically.
00058  *
00059  * The "redundant" option will configure two sockets and two NIC interfaces.
00060  * Slaves are connected to both interfaces, one on the IN port and one on the
00061  * OUT port. Packets are send via both interfaces. Any one of the connections
00062  * (also an interconnect) can be removed and the slaves are still serviced with
00063  * packets. The software layer will detect the possible failure modes and
00064  * compensate. If needed the packets from interface A are resend through interface B.
00065  * This layer if fully transparent for the higher layers.
00066  */
00067 
00068 #include <sys/types.h>
00069 #include <sys/ioctl.h>
00070 #include <net/if.h> 
00071 #include <sys/socket.h> 
00072 #include <unistd.h>
00073 #include <sys/time.h> 
00074 #include <arpa/inet.h>
00075 #include <stdio.h>
00076 #include <fcntl.h>
00077 #include <string.h>
00078 #include <netpacket/packet.h>
00079 #include <pthread.h>
00080 #include <stdlib.h>
00081 
00082 #include "ethercattype.h"
00083 #include "nicdrv.h"
00084 
00085 /** Redundancy modes */
00086 enum
00087 {
00088   /** No redundancy, single NIC mode */
00089   ECT_RED_NONE,
00090   /** Double redundant NIC connecetion */
00091   ECT_RED_DOUBLE
00092 };
00093 
00094 /** pointer structure to Tx and Rx stacks */
00095 typedef struct
00096 {
00097   /** socket connection used */
00098   int     *sock;
00099   /** tx buffer */
00100   ec_bufT   (*txbuf)[EC_MAXBUF];
00101   /** tx buffer lengths */
00102   int     (*txbuflength)[EC_MAXBUF];
00103   /** temporary receive buffer */
00104   ec_bufT   *tempbuf;
00105   /** rx buffers */
00106   ec_bufT   (*rxbuf)[EC_MAXBUF];
00107   /** rx buffer status fields */
00108   int     (*rxbufstat)[EC_MAXBUF];
00109   /** received MAC source address (middle word) */
00110   int     (*rxsa)[EC_MAXBUF];
00111 } ec_stackT;  
00112 
00113 /** primary rx buffers */
00114 ec_bufT ec_rxbuf[EC_MAXBUF];
00115 /** primary rx buffer status */
00116 int ec_rxbufstat[EC_MAXBUF];
00117 /** primary rx MAC source address */
00118 static int ec_rxsa[EC_MAXBUF];
00119 /** primary temporary rx buffer */
00120 static ec_bufT ec_tempinbuf;
00121 /** primary temporary rx buffer status */
00122 static int ec_tempinbufs;
00123 
00124 /** secondary rx buffers */
00125 static ec_bufT ec_rxbuf2[EC_MAXBUF];
00126 /** secondary rx buffer status */
00127 static int ec_rxbufstat2[EC_MAXBUF];
00128 /** secondary rx MAC source address */
00129 static int ec_rxsa2[EC_MAXBUF];
00130 /** secondary temporary rx buffer */
00131 static ec_bufT ec_tempinbuf2;
00132 
00133 /** transmit buffers */
00134 ec_bufT ec_txbuf[EC_MAXBUF];
00135 /** transmit buffer lenghts */
00136 int ec_txbuflength[EC_MAXBUF];
00137 /** temporary tx buffer */
00138 ec_bufT ec_txbuf2;
00139 /** temporary tx buffer length */
00140 int ec_txbuflength2;
00141 
00142 /** primary socket handle */
00143 int sockhandle = -1;
00144 /** secondary socket handle */
00145 int sockhandle2 = -1;
00146 
00147 /** primary and secondary tx/rx stack pointers */
00148 static ec_stackT ec_stack[2]= 
00149     {{&sockhandle, &ec_txbuf, &ec_txbuflength, &ec_tempinbuf, &ec_rxbuf, &ec_rxbufstat, &ec_rxsa},
00150      {&sockhandle2, &ec_txbuf, &ec_txbuflength, &ec_tempinbuf2, &ec_rxbuf2, &ec_rxbufstat2, &ec_rxsa2}};
00151 
00152 /** last used frame index */
00153 static uint8 ec_lastidx;
00154 /** global rx packet counter, counts only EtherCAT packets */
00155 int ec_incnt;
00156 /** global error packet counter, ie non EtherCAT packets */
00157 int ec_errcnt;
00158 /** current redundancy state */
00159 int ec_redstate;
00160 
00161 /** global helper var to count time used in tx socket */
00162 int hlp_txtime;
00163 /** global helper var ri count time used in rx socket */
00164 int hlp_rxtime;
00165 /** Primary source MAC address used for EtherCAT.
00166  * This address is not the MAC address used from the NIC.
00167  * EtherCAT does not care about MAC addressing, but it is used here to
00168  * differentiate the route the packet traverses through the EtherCAT
00169  * segment. This is needed to find out the packet flow in redundant
00170  * configurations. */
00171 const uint16 priMAC[3] = { 0x0101, 0x0101, 0x0101 };
00172 /** Secondary source MAC address used for EtherCAT. */
00173 const uint16 secMAC[3] = { 0x0404, 0x0404, 0x0404 };
00174 
00175 /** second MAC word is used for identification */
00176 #define RX_PRIM priMAC[1]
00177 /** second MAC word is used for identification */
00178 #define RX_SEC secMAC[1]
00179 
00180 pthread_mutex_t ec_getindex_mutex = PTHREAD_MUTEX_INITIALIZER;
00181 pthread_mutex_t ec_tx_mutex = PTHREAD_MUTEX_INITIALIZER;
00182 pthread_mutex_t ec_rx_mutex = PTHREAD_MUTEX_INITIALIZER;
00183 
00184 /** Basic setup to connect NIC to socket.
00185  * @param[in] ifname      = Name of NIC device, f.e. "eth0"
00186  * @param[in] secondary   = if >0 then use secondary stack instead of primary
00187  * @return >0 if succeeded
00188  */
00189 int ec_setupnic(const char * ifname, int secondary) 
00190 {
00191   int i;
00192   int r, rval, ifindex, fl;
00193   struct timeval timeout;
00194   struct ifreq ifr;
00195   struct sockaddr_ll sll;
00196   int *psock;
00197 
00198   rval = 0;
00199   if (secondary)
00200   {
00201     /* when using secondary socket it is automatically a redundant setup */
00202     psock = &sockhandle2;
00203     ec_redstate = ECT_RED_DOUBLE;
00204   }
00205   else
00206   {
00207     psock = &sockhandle;
00208     ec_redstate = ECT_RED_NONE;
00209   } 
00210   /* we use RAW packet socket, with packet type ETH_P_ECAT */
00211   *psock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ECAT));
00212   timeout.tv_sec =  0;
00213   timeout.tv_usec = 1;
00214    
00215   r = setsockopt(*psock, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
00216   r = setsockopt(*psock, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout));
00217   i = 1;
00218   r = setsockopt(*psock, SOL_SOCKET, SO_DONTROUTE, &i, sizeof(i));
00219   /* connect socket to NIC by name */
00220   strcpy(ifr.ifr_name, ifname);
00221   r = ioctl(*psock, SIOCGIFINDEX, &ifr);
00222   ifindex = ifr.ifr_ifindex;
00223   strcpy(ifr.ifr_name, ifname);
00224   ifr.ifr_flags = 0;
00225   /* reset flags of NIC interface */
00226   r = ioctl(*psock, SIOCGIFFLAGS, &ifr);
00227   /* set flags of NIC interface, here promiscuous and broadcast */
00228   ifr.ifr_flags = ifr.ifr_flags || IFF_PROMISC || IFF_BROADCAST;
00229   r = ioctl(*psock, SIOCGIFFLAGS, &ifr);
00230   /* bind socket to protocol, in this case RAW EtherCAT */
00231   sll.sll_family = AF_PACKET;
00232   sll.sll_ifindex = ifindex;
00233   sll.sll_protocol = htons(ETH_P_ECAT);
00234   r = bind(*psock, (struct sockaddr *)&sll, sizeof(sll));
00235   /* get flags */
00236   fl = fcntl(*psock, F_GETFL, 0);
00237   /* set nodelay option, so make socket non-blocking */
00238 //  r = fcntl(*psock, F_SETFL, fl | O_NDELAY);
00239   /* setup ethernet headers in tx buffers so we don't have to repeat it */
00240   for (i = 0; i < EC_MAXBUF; i++) 
00241   {
00242     ec_setupheader(&ec_txbuf[i]);
00243     ec_rxbufstat[i] = EC_BUF_EMPTY;
00244   }
00245   ec_setupheader(&ec_txbuf2);
00246   ec_errcnt = ec_incnt = 0;
00247   if (r == 0) rval = 1;
00248   
00249   return rval;
00250 }
00251 
00252 /** Close sockets used
00253  * @return 0
00254  */
00255 int ec_closenic(void) 
00256 {
00257   if (sockhandle >= 0) close(sockhandle);
00258   if (sockhandle2 >= 0) close(sockhandle2);
00259   
00260   return 0;
00261 }
00262 
00263 /** Fill buffer with ethernet header structure.
00264  * Destination MAC is allways broadcast.
00265  * Ethertype is allways ETH_P_ECAT.
00266  * @param[out] p = buffer
00267  */
00268 void ec_setupheader(void *p) 
00269 {
00270   ec_etherheadert *bp;
00271   bp = p;
00272   bp->da0 = htons(0xffff);
00273   bp->da1 = htons(0xffff);
00274   bp->da2 = htons(0xffff);
00275   bp->sa0 = htons(priMAC[0]);
00276   bp->sa1 = htons(priMAC[1]);
00277   bp->sa2 = htons(priMAC[2]);
00278   bp->etype = htons(ETH_P_ECAT);
00279 }
00280 
00281 /** Get new frame identifier index and allocate corresponding rx buffer.
00282  * @return new index.
00283  */
00284 uint8 ec_getindex(void)
00285 {
00286   uint8 idx;
00287   int cnt;
00288 
00289   pthread_mutex_lock( &ec_getindex_mutex );
00290 
00291   idx = ec_lastidx + 1;
00292   /* index can't be larger than buffer array */
00293   if (idx >= EC_MAXBUF) 
00294   {
00295     idx = 0;
00296   }
00297   cnt = 0;
00298   /* try to find unused index */
00299   while ((ec_rxbufstat[idx] != EC_BUF_EMPTY) && (cnt < EC_MAXBUF))
00300   {
00301     idx++;
00302     cnt++;
00303     if (idx >= EC_MAXBUF) 
00304     {
00305       idx = 0;
00306     }
00307   }
00308   ec_rxbufstat[idx] = EC_BUF_ALLOC;
00309   ec_rxbufstat2[idx] = EC_BUF_ALLOC;
00310   ec_lastidx = idx;
00311 
00312   pthread_mutex_unlock( &ec_getindex_mutex );
00313   
00314   return idx;
00315 }
00316 
00317 /** Set rx buffer status.
00318  * @param[in] idx   = index in buffer array
00319  * @param[in] bufstat   = status to set
00320  */
00321 void ec_setbufstat(uint8 idx, int bufstat)
00322 {
00323     ec_rxbufstat[idx] = bufstat;
00324   ec_rxbufstat2[idx] = bufstat;
00325 }
00326 
00327 /** Transmit buffer over socket (non blocking).
00328  * @param[in] idx     = index in tx buffer array
00329  * @param[in] stacknumber   = 0=Primary 1=Secondary stack
00330  * @return socket send result
00331  */
00332 int ec_outframe(uint8 idx, int stacknumber)
00333 {
00334   int lp, rval;
00335   ec_stackT *stack;
00336 
00337   stack = &ec_stack[stacknumber];
00338   lp = (*stack->txbuflength)[idx];
00339   rval = send(*stack->sock, (*stack->txbuf)[idx], lp, 0);
00340   (*stack->rxbufstat)[idx] = EC_BUF_TX;
00341 
00342   return rval;
00343 }
00344 
00345 /** Transmit buffer over socket (non blocking).
00346  * @param[in] idx     = index in tx buffer array
00347  * @return socket send result
00348  */
00349 int ec_outframe_red(uint8 idx)
00350 {
00351   ec_comt *datagramP;
00352   ec_etherheadert *ehp;
00353   int rval;
00354 
00355   ehp = (ec_etherheadert *)&ec_txbuf[idx];
00356   /* rewrite MAC source address 1 to primary */
00357   ehp->sa1 = htons(priMAC[1]);
00358   /* transmit over primary socket*/
00359   rval = ec_outframe(idx, 0);
00360   if (ec_redstate != ECT_RED_NONE)
00361   { 
00362     pthread_mutex_lock( &ec_tx_mutex );
00363     ehp = (ec_etherheadert *)&ec_txbuf2;
00364     /* use dummy frame for secondary socket transmit (BRD) */
00365     datagramP = (ec_comt*)&ec_txbuf2[ETH_HEADERSIZE];
00366     /* write index to frame */
00367     datagramP->index = idx;
00368     /* rewrite MAC source address 1 to secondary */
00369     ehp->sa1 = htons(secMAC[1]);
00370     /* transmit over secondary socket */
00371     send(sockhandle2, &ec_txbuf2, ec_txbuflength2 , 0);
00372     pthread_mutex_unlock( &ec_tx_mutex );
00373     ec_rxbufstat2[idx] = EC_BUF_TX;
00374   } 
00375   
00376   return rval;
00377 }
00378 
00379 /** Non blocking read of socket. Put frame in temporary buffer.
00380  * @param[in] stacknumber = 0=primary 1=secondary stack
00381  * @return >0 if frame is available and read
00382  */
00383 static int ec_recvpkt(int stacknumber)
00384 {
00385   int lp, bytesrx;
00386   ec_stackT *stack;
00387 
00388   stack = &ec_stack[stacknumber];
00389   lp = sizeof(ec_tempinbuf);
00390   bytesrx = recv(*stack->sock, (*stack->tempbuf), lp, 0);
00391   ec_tempinbufs = bytesrx;
00392   
00393   return (bytesrx > 0);
00394 }
00395 
00396 /** Non blocking receive frame function. Uses RX buffer and index to combine
00397  * read frame with transmitted frame. To compensate for received frames that
00398  * are out-of-order all frames are stored in their respective indexed buffer.
00399  * If a frame was placed in the buffer previously, the function retreives it
00400  * from that buffer index without calling ec_recvpkt. If the requested index
00401  * is not already in the buffer it calls ec_recvpkt to fetch it. There are
00402  * three options now, 1 no frame read, so exit. 2 frame read but other
00403  * than requested index, store in buffer and exit. 3 frame read with matching
00404  * index, store in buffer, set completed flag in buffer status and exit.
00405  * 
00406  * @param[in] idx     = requested index of frame
00407  * @param[in] stacknumber  = 0=primary 1=secondary stack
00408  * @return Workcounter if a frame is found with corresponding index, otherwise
00409  * EC_NOFRAME or EC_OTHERFRAME.
00410  */
00411 int ec_inframe(uint8 idx, int stacknumber)
00412 {
00413   uint16  l;
00414   int   rval;
00415   uint8   idxf;
00416   ec_etherheadert *ehp;
00417   ec_comt *ecp;
00418   ec_stackT *stack;
00419   ec_bufT *rxbuf;
00420 
00421   stack = &ec_stack[stacknumber];
00422   rval = EC_NOFRAME;
00423   rxbuf = &(*stack->rxbuf)[idx];
00424   /* check if requested index is already in buffer ? */
00425   if ((idx < EC_MAXBUF) && (  (*stack->rxbufstat)[idx] == EC_BUF_RCVD)) 
00426   {
00427     l = (*rxbuf)[0] + ((uint16)((*rxbuf)[1] & 0x0f) << 8);
00428     /* return WKC */
00429     rval = ((*rxbuf)[l] + ((uint16)(*rxbuf)[l + 1] << 8));
00430     /* mark as completed */
00431     (*stack->rxbufstat)[idx] = EC_BUF_COMPLETE;
00432   }
00433   else 
00434   {
00435     pthread_mutex_lock( &ec_rx_mutex );
00436     /* non blocking call to retrieve frame from socket */
00437     if (ec_recvpkt( stacknumber)) 
00438     {
00439       rval = EC_OTHERFRAME;
00440       ehp =(ec_etherheadert*)(stack->tempbuf);
00441       /* check if it is an EtherCAT frame */
00442       if (ehp->etype == htons(ETH_P_ECAT)) 
00443       {
00444         ec_incnt++;
00445         ecp =(ec_comt*)(&(*stack->tempbuf)[ETH_HEADERSIZE]); 
00446         l = etohs(ecp->elength) & 0x0fff;
00447         idxf = ecp->index;
00448         /* found index equals reqested index ? */
00449         if (idxf == idx) 
00450         {
00451           /* yes, put it in the buffer array (strip ethernet header) */
00452           memcpy(rxbuf, &(*stack->tempbuf)[ETH_HEADERSIZE], (*stack->txbuflength)[idx] - ETH_HEADERSIZE);
00453           /* return WKC */
00454           rval = ((*rxbuf)[l] + ((uint16)((*rxbuf)[l + 1]) << 8));
00455           /* mark as completed */
00456           (*stack->rxbufstat)[idx] = EC_BUF_COMPLETE;
00457           /* store MAC source word 1 for redundant routing info */
00458           (*stack->rxsa)[idx] = ntohs(ehp->sa1);
00459         }
00460         else 
00461         {
00462           int size = (*stack->txbuflength)[idxf] - ETH_HEADERSIZE;
00463           /* check if index exist? */
00464           if (idxf < EC_MAXBUF && size > 0) 
00465           {
00466             rxbuf = &(*stack->rxbuf)[idxf];
00467             /* put it in the buffer array (strip ethernet header) */
00468             memcpy(rxbuf, &(*stack->tempbuf)[ETH_HEADERSIZE], (*stack->txbuflength)[idxf] - ETH_HEADERSIZE);
00469             /* mark as received */
00470             (*stack->rxbufstat)[idxf] = EC_BUF_RCVD;
00471             (*stack->rxsa)[idxf] = ntohs(ehp->sa1);
00472           }
00473           else 
00474           {
00475             /* strange things happend */
00476             ec_errcnt++;
00477             printf("There is a other instance of a EtherCAT master running!\n");
00478             exit(0);
00479           }
00480         }
00481       }
00482     }
00483     pthread_mutex_unlock( &ec_rx_mutex );
00484     
00485   }
00486   
00487   /* WKC if mathing frame found */
00488   return rval;
00489 }
00490 
00491 /** Blocking redundant receive frame function. If redundant mode is not active then
00492  * it skips the secondary stack and redundancy functions. In redundant mode it waits
00493  * for both (primary and secondary) frames to come in. The result goes in an decision
00494  * tree that decides, depending on the route of the packet and its possible missing arrival,
00495  * how to reroute the original packet to get the data in an other try. 
00496  *
00497  * @param[in] idx = requested index of frame
00498  * @param[in] tvs = timeout
00499  * @return Workcounter if a frame is found with corresponding index, otherwise
00500  * EC_NOFRAME.
00501  */
00502 static int ec_waitinframe_red(uint8 idx, struct timeval tvs)
00503 {
00504   struct timeval tv1,tv2;
00505   int wkc  = EC_NOFRAME;
00506   int wkc2 = EC_NOFRAME;
00507   int primrx, secrx;
00508   
00509   /* if not in redundat mode then always assume secondary is OK */
00510   if (ec_redstate == ECT_RED_NONE)
00511     wkc2 = 0;
00512   do 
00513   {
00514     /* only read frame if not already in */
00515     if (wkc <= EC_NOFRAME)
00516       wkc  = ec_inframe(idx, 0);
00517     /* only try secondary if in redundant mode */
00518     if (ec_redstate != ECT_RED_NONE)
00519     { 
00520       /* only read frame if not already in */
00521       if (wkc2 <= EC_NOFRAME)
00522         wkc2 = ec_inframe(idx, 1);
00523     } 
00524     gettimeofday(&tv1, 0);
00525   /* wait for both frames to arrive or timeout */ 
00526   } while (((wkc <= EC_NOFRAME) || (wkc2 <= EC_NOFRAME)) && (timercmp(&tv1, &tvs, <)));
00527   /* only do redundant functions when in redundant mode */
00528   if (ec_redstate != ECT_RED_NONE)
00529   {
00530     /* primrx if the reveived MAC source on primary socket */
00531     primrx = 0;
00532     if (wkc > EC_NOFRAME) primrx = ec_rxsa[idx];
00533     /* secrx if the reveived MAC source on psecondary socket */
00534     secrx = 0;
00535     if (wkc2 > EC_NOFRAME) secrx = ec_rxsa2[idx];
00536     
00537     /* primary socket got secondary frame and secondary socket got primary frame */
00538     /* normal situation in redundant mode */
00539     if ( ((primrx == RX_SEC) && (secrx == RX_PRIM)) )
00540     {
00541       /* copy secondary buffer to primary */
00542       memcpy(&ec_rxbuf[idx], &ec_rxbuf2[idx], ec_txbuflength[idx] - ETH_HEADERSIZE);
00543       wkc = wkc2;
00544     } 
00545     /* primary socket got nothing or primary frame, and secondary socket got secondary frame */
00546     /* we need to resend TX packet */ 
00547     if ( ((primrx == 0) && (secrx == RX_SEC)) ||
00548        ((primrx == RX_PRIM) && (secrx == RX_SEC)) )
00549     {
00550       /* If both primary and secondary have partial connection retransmit the primary received
00551        * frame over the secondary socket. The result from the secondary received frame is a combined
00552        * frame that traversed all slaves in standard order. */
00553       if ( (primrx == RX_PRIM) && (secrx == RX_SEC) )
00554       { 
00555         /* copy primary rx to tx buffer */
00556         memcpy(&ec_txbuf[idx][ETH_HEADERSIZE], &ec_rxbuf[idx], ec_txbuflength[idx] - ETH_HEADERSIZE);
00557       }
00558       gettimeofday(&tv1, 0);        
00559       tv2.tv_sec = 0;
00560       tv2.tv_usec = EC_TIMEOUTRET;
00561       timeradd(&tv1, &tv2, &tvs);
00562       /* resend secondary tx */
00563       ec_outframe(idx,1);
00564       do 
00565       {
00566         /* retrieve frame */
00567         wkc2 = ec_inframe(idx, 1);
00568         gettimeofday(&tv1, 0);
00569       } while ((wkc2 <= EC_NOFRAME) && (timercmp(&tv1, &tvs, <)));
00570       if (wkc2 > EC_NOFRAME)
00571       { 
00572         /* copy secondary result to primary rx buffer */
00573         memcpy(&ec_rxbuf[idx], &ec_rxbuf2[idx], ec_txbuflength[idx] - ETH_HEADERSIZE);
00574         wkc = wkc2;
00575       } 
00576     }   
00577   }
00578   
00579   /* return WKC or EC_NOFRAME */
00580   return wkc;
00581 } 
00582 
00583 /** Blocking receive frame function. Calls ec_waitinframe_red().
00584  * @param[in] idx = requested index of frame
00585  * @param[in] timeout = timeout in us
00586  * @return Workcounter if a frame is found with corresponding index, otherwise
00587  * EC_NOFRAME.
00588  */
00589 int ec_waitinframe(uint8 idx, int timeout)
00590 {
00591   int wkc;
00592   struct timeval tv1, tv2, tve;
00593   
00594   gettimeofday(&tv1, 0);
00595   tv2.tv_sec = 0;
00596   tv2.tv_usec = timeout;
00597   timeradd(&tv1, &tv2, &tve);
00598   wkc = ec_waitinframe_red(idx, tve);
00599   /* if nothing received, clear buffer index status so it can be used again */
00600   if (wkc <= EC_NOFRAME) 
00601   {
00602     ec_setbufstat(idx, EC_BUF_EMPTY);
00603   }
00604   
00605   return wkc;
00606 }
00607 
00608 /** Blocking send and recieve frame function. Used for non processdata frames.
00609  * A datagram is build into a frame and transmitted via this function. It waits
00610  * for an answer and returns the workcounter. The function retries if time is
00611  * left and the result is WKC=0 or no frame received.
00612  *
00613  * The function calls ec_outframe_red() and ec_waitinframe_red().
00614  *
00615  * @param[in] idx   = index of frame
00616  * @param[in] timeout  = timeout in us
00617  * @return Workcounter or EC_NOFRAME
00618  */
00619 int ec_srconfirm(uint8 idx, int timeout)
00620 {
00621   int wkc = EC_NOFRAME;
00622   struct timeval tv1, tv2, tv3, tve, tvs, tvh;
00623 
00624   gettimeofday(&tv1, 0);
00625   tv2.tv_sec = 0;
00626   tv2.tv_usec = timeout;
00627   timeradd(&tv1, &tv2, &tve);
00628   do 
00629   {
00630     /* tx frame on primary and if in redundant mode a dummy on secondary */
00631     ec_outframe_red(idx);
00632     gettimeofday(&tv2, 0);
00633     timersub(&tv2, &tv1, &tvh);
00634     hlp_txtime += (int)tvh.tv_usec;
00635     tv1.tv_sec = 0;
00636     if (timeout < EC_TIMEOUTRET) 
00637     {
00638       tv1.tv_usec = timeout; 
00639     }
00640     else 
00641     {
00642       /* normally use partial timout for rx */
00643       tv1.tv_usec = EC_TIMEOUTRET;
00644     }
00645     timeradd(&tv2, &tv1, &tvs);
00646     /* get frame from primary or if in redundant mode possibly from secondary */
00647     wkc = ec_waitinframe_red(idx, tvs);
00648     gettimeofday(&tv3, 0);
00649     timersub(&tv3, &tv2, &tvh);
00650     hlp_rxtime += (int)tvh.tv_usec;   
00651   /* wait for answer with WKC>0 or otherwise retry until timeout */ 
00652   } while ((wkc <= EC_NOFRAME) && (timercmp(&tv3, &tve, <)));
00653   /* if nothing received, clear buffer index status so it can be used again */
00654   if (wkc <= EC_NOFRAME) 
00655   {
00656     ec_setbufstat(idx, EC_BUF_EMPTY);
00657   }
00658   
00659   return wkc;
00660 }
Generated by  doxygen 1.6.3