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 
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 <youbot_driver/soem/ethercattype.h>
00083 #include <youbot_driver/soem/nicdrv.h>
00084 
00086 enum
00087 {
00089   ECT_RED_NONE,
00091   ECT_RED_DOUBLE
00092 };
00093 
00095 typedef struct
00096 {
00098   int *sock;
00100   ec_bufT (*txbuf)[EC_MAXBUF];
00102   int (*txbuflength)[EC_MAXBUF];
00104   ec_bufT *tempbuf;
00106   ec_bufT (*rxbuf)[EC_MAXBUF];
00108   int (*rxbufstat)[EC_MAXBUF];
00110   int (*rxsa)[EC_MAXBUF];
00111 } ec_stackT;
00112 
00114 ec_bufT ec_rxbuf[EC_MAXBUF];
00116 int ec_rxbufstat[EC_MAXBUF];
00118 static int ec_rxsa[EC_MAXBUF];
00120 static ec_bufT ec_tempinbuf;
00122 static int ec_tempinbufs;
00123 
00125 static ec_bufT ec_rxbuf2[EC_MAXBUF];
00127 static int ec_rxbufstat2[EC_MAXBUF];
00129 static int ec_rxsa2[EC_MAXBUF];
00131 static ec_bufT ec_tempinbuf2;
00132 
00134 ec_bufT ec_txbuf[EC_MAXBUF];
00136 int ec_txbuflength[EC_MAXBUF];
00138 ec_bufT ec_txbuf2;
00140 int ec_txbuflength2;
00141 
00143 int sockhandle = -1;
00145 int sockhandle2 = -1;
00146 
00148 static ec_stackT ec_stack[2] = { {&sockhandle, &ec_txbuf, &ec_txbuflength, &ec_tempinbuf, &ec_rxbuf, &ec_rxbufstat,
00149                                   &ec_rxsa},
00150                                 {&sockhandle2, &ec_txbuf, &ec_txbuflength, &ec_tempinbuf2, &ec_rxbuf2, &ec_rxbufstat2,
00151                                  &ec_rxsa2}};
00152 
00154 static uint8 ec_lastidx;
00156 int ec_incnt;
00158 int ec_errcnt;
00160 int ec_redstate;
00161 
00163 int hlp_txtime;
00165 int hlp_rxtime;
00172 const uint16 priMAC[3] = {0x0101, 0x0101, 0x0101};
00174 const uint16 secMAC[3] = {0x0404, 0x0404, 0x0404};
00175 
00177 #define RX_PRIM priMAC[1]
00178 
00179 #define RX_SEC secMAC[1]
00180 
00181 pthread_mutex_t ec_getindex_mutex = PTHREAD_MUTEX_INITIALIZER;
00182 pthread_mutex_t ec_tx_mutex = PTHREAD_MUTEX_INITIALIZER;
00183 pthread_mutex_t ec_rx_mutex = PTHREAD_MUTEX_INITIALIZER;
00184 
00190 int ec_setupnic(const char * ifname, int secondary)
00191 {
00192   int i;
00193   int r, rval, ifindex; //, fl;
00194   struct timeval timeout;
00195   struct ifreq ifr;
00196   struct sockaddr_ll sll;
00197   int *psock;
00198 
00199   rval = 0;
00200   if (secondary)
00201   {
00202     /* when using secondary socket it is automatically a redundant setup */
00203     psock = &sockhandle2;
00204     ec_redstate = ECT_RED_DOUBLE;
00205   }
00206   else
00207   {
00208     psock = &sockhandle;
00209     ec_redstate = ECT_RED_NONE;
00210   }
00211   /* we use RAW packet socket, with packet type ETH_P_ECAT */
00212   *psock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ECAT));
00213   timeout.tv_sec = 0;
00214   timeout.tv_usec = 1;
00215 
00216   r = setsockopt(*psock, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
00217   r = setsockopt(*psock, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout));
00218   i = 1;
00219   r = setsockopt(*psock, SOL_SOCKET, SO_DONTROUTE, &i, sizeof(i));
00220   /* connect socket to NIC by name */
00221   strcpy(ifr.ifr_name, ifname);
00222   r = ioctl(*psock, SIOCGIFINDEX, &ifr);
00223   ifindex = ifr.ifr_ifindex;
00224   strcpy(ifr.ifr_name, ifname);
00225   ifr.ifr_flags = 0;
00226   /* reset flags of NIC interface */
00227   r = ioctl(*psock, SIOCGIFFLAGS, &ifr);
00228   /* set flags of NIC interface, here promiscuous and broadcast */
00229   ifr.ifr_flags = ifr.ifr_flags || IFF_PROMISC || IFF_BROADCAST;
00230   r = ioctl(*psock, SIOCGIFFLAGS, &ifr);
00231   /* bind socket to protocol, in this case RAW EtherCAT */
00232   sll.sll_family = AF_PACKET;
00233   sll.sll_ifindex = ifindex;
00234   sll.sll_protocol = htons(ETH_P_ECAT);
00235   r = bind(*psock, (struct sockaddr *)&sll, sizeof(sll));
00236   /* get flags */
00237   /*fl =*/fcntl(*psock, F_GETFL, 0);
00238   /* set nodelay option, so make socket non-blocking */
00239 //      r = fcntl(*psock, F_SETFL, fl | O_NDELAY);
00240   /* setup ethernet headers in tx buffers so we don't have to repeat it */
00241   for (i = 0; i < EC_MAXBUF; i++)
00242   {
00243     ec_setupheader(&ec_txbuf[i]);
00244     ec_rxbufstat[i] = EC_BUF_EMPTY;
00245   }
00246   ec_setupheader(&ec_txbuf2);
00247   ec_errcnt = ec_incnt = 0;
00248   if (r == 0)
00249     rval = 1;
00250 
00251   return rval;
00252 }
00253 
00257 int ec_closenic(void)
00258 {
00259   if (sockhandle >= 0)
00260     close(sockhandle);
00261   if (sockhandle2 >= 0)
00262     close(sockhandle2);
00263 
00264   return 0;
00265 }
00266 
00272 void ec_setupheader(void *p)
00273 {
00274   ec_etherheadert *bp;
00275   bp = p;
00276   bp->da0 = htons(0xffff);
00277   bp->da1 = htons(0xffff);
00278   bp->da2 = htons(0xffff);
00279   bp->sa0 = htons(priMAC[0]);
00280   bp->sa1 = htons(priMAC[1]);
00281   bp->sa2 = htons(priMAC[2]);
00282   bp->etype = htons(ETH_P_ECAT);
00283 }
00284 
00288 uint8 ec_getindex(void)
00289 {
00290   uint8 idx;
00291   int cnt;
00292 
00293   pthread_mutex_lock(&ec_getindex_mutex);
00294 
00295   idx = ec_lastidx + 1;
00296   /* index can't be larger than buffer array */
00297   if (idx >= EC_MAXBUF)
00298   {
00299     idx = 0;
00300   }
00301   cnt = 0;
00302   /* try to find unused index */
00303   while ((ec_rxbufstat[idx] != EC_BUF_EMPTY) && (cnt < EC_MAXBUF))
00304   {
00305     idx++;
00306     cnt++;
00307     if (idx >= EC_MAXBUF)
00308     {
00309       idx = 0;
00310     }
00311   }
00312   ec_rxbufstat[idx] = EC_BUF_ALLOC;
00313   ec_rxbufstat2[idx] = EC_BUF_ALLOC;
00314   ec_lastidx = idx;
00315 
00316   pthread_mutex_unlock(&ec_getindex_mutex);
00317 
00318   return idx;
00319 }
00320 
00325 void ec_setbufstat(uint8 idx, int bufstat)
00326 {
00327   ec_rxbufstat[idx] = bufstat;
00328   ec_rxbufstat2[idx] = bufstat;
00329 }
00330 
00336 int ec_outframe(uint8 idx, int stacknumber)
00337 {
00338   int lp, rval;
00339   ec_stackT *stack;
00340 
00341   stack = &ec_stack[stacknumber];
00342   lp = (*stack->txbuflength)[idx];
00343   rval = send(*stack->sock, (*stack->txbuf)[idx], lp, 0);
00344   (*stack->rxbufstat)[idx] = EC_BUF_TX;
00345 
00346   return rval;
00347 }
00348 
00353 int ec_outframe_red(uint8 idx)
00354 {
00355   ec_comt *datagramP;
00356   ec_etherheadert *ehp;
00357   int rval;
00358 
00359   ehp = (ec_etherheadert *)&ec_txbuf[idx];
00360   /* rewrite MAC source address 1 to primary */
00361   ehp->sa1 = htons(priMAC[1]);
00362   /* transmit over primary socket*/
00363   rval = ec_outframe(idx, 0);
00364   if (ec_redstate != ECT_RED_NONE)
00365   {
00366     pthread_mutex_lock(&ec_tx_mutex);
00367     ehp = (ec_etherheadert *)&ec_txbuf2;
00368     /* use dummy frame for secondary socket transmit (BRD) */
00369     datagramP = (ec_comt*)&ec_txbuf2[ETH_HEADERSIZE];
00370     /* write index to frame */
00371     datagramP->index = idx;
00372     /* rewrite MAC source address 1 to secondary */
00373     ehp->sa1 = htons(secMAC[1]);
00374     /* transmit over secondary socket */
00375     send(sockhandle2, &ec_txbuf2, ec_txbuflength2, 0);
00376     pthread_mutex_unlock(&ec_tx_mutex);
00377     ec_rxbufstat2[idx] = EC_BUF_TX;
00378   }
00379 
00380   return rval;
00381 }
00382 
00387 static int ec_recvpkt(int stacknumber)
00388 {
00389   int lp, bytesrx;
00390   ec_stackT *stack;
00391 
00392   stack = &ec_stack[stacknumber];
00393   lp = sizeof(ec_tempinbuf);
00394   bytesrx = recv(*stack->sock, (*stack->tempbuf), lp, 0);
00395   ec_tempinbufs = bytesrx;
00396 
00397   return (bytesrx > 0);
00398 }
00399 
00415 int ec_inframe(uint8 idx, int stacknumber)
00416 {
00417   uint16 l;
00418   int rval;
00419   uint8 idxf;
00420   ec_etherheadert *ehp;
00421   ec_comt *ecp;
00422   ec_stackT *stack;
00423   ec_bufT *rxbuf;
00424 
00425   stack = &ec_stack[stacknumber];
00426   rval = EC_NOFRAME;
00427   rxbuf = &(*stack->rxbuf)[idx];
00428   /* check if requested index is already in buffer ? */
00429   if ((idx < EC_MAXBUF) && ((*stack->rxbufstat)[idx] == EC_BUF_RCVD))
00430   {
00431     l = (*rxbuf)[0] + ((uint16)((*rxbuf)[1] & 0x0f) << 8);
00432     /* return WKC */
00433     rval = ((*rxbuf)[l] + ((uint16)(*rxbuf)[l + 1] << 8));
00434     /* mark as completed */
00435     (*stack->rxbufstat)[idx] = EC_BUF_COMPLETE;
00436   }
00437   else
00438   {
00439     pthread_mutex_lock(&ec_rx_mutex);
00440     /* non blocking call to retrieve frame from socket */
00441     if (ec_recvpkt(stacknumber))
00442     {
00443       rval = EC_OTHERFRAME;
00444       ehp = (ec_etherheadert*)(stack->tempbuf);
00445       /* check if it is an EtherCAT frame */
00446       if (ehp->etype == htons(ETH_P_ECAT))
00447       {
00448         ec_incnt++;
00449         ecp = (ec_comt*)(&(*stack->tempbuf)[ETH_HEADERSIZE]);
00450         l = etohs(ecp->elength) & 0x0fff;
00451         idxf = ecp->index;
00452         /* found index equals reqested index ? */
00453         if (idxf == idx)
00454         {
00455           /* yes, put it in the buffer array (strip ethernet header) */
00456           memcpy(rxbuf, &(*stack->tempbuf)[ETH_HEADERSIZE], (*stack->txbuflength)[idx] - ETH_HEADERSIZE);
00457           /* return WKC */
00458           rval = ((*rxbuf)[l] + ((uint16)((*rxbuf)[l + 1]) << 8));
00459           /* mark as completed */
00460           (*stack->rxbufstat)[idx] = EC_BUF_COMPLETE;
00461           /* store MAC source word 1 for redundant routing info */
00462           (*stack->rxsa)[idx] = ntohs(ehp->sa1);
00463         }
00464         else
00465         {
00466           int size = (*stack->txbuflength)[idxf] - ETH_HEADERSIZE;
00467           /* check if index exist? */
00468           if (idxf < EC_MAXBUF && size > 0)
00469           {
00470             rxbuf = &(*stack->rxbuf)[idxf];
00471             /* put it in the buffer array (strip ethernet header) */
00472             memcpy(rxbuf, &(*stack->tempbuf)[ETH_HEADERSIZE], (*stack->txbuflength)[idxf] - ETH_HEADERSIZE);
00473             /* mark as received */
00474             (*stack->rxbufstat)[idxf] = EC_BUF_RCVD;
00475             (*stack->rxsa)[idxf] = ntohs(ehp->sa1);
00476           }
00477           else
00478           {
00479             /* strange things happend */
00480             ec_errcnt++;
00481             printf("There is a other instance of a EtherCAT master running!\n");
00482             exit(0);
00483           }
00484         }
00485       }
00486     }
00487     pthread_mutex_unlock(&ec_rx_mutex);
00488 
00489   }
00490 
00491   /* WKC if mathing frame found */
00492   return rval;
00493 }
00494 
00506 static int ec_waitinframe_red(uint8 idx, struct timeval tvs)
00507 {
00508   struct timeval tv1, tv2;
00509   int wkc = EC_NOFRAME;
00510   int wkc2 = EC_NOFRAME;
00511   int primrx, secrx;
00512 
00513   /* if not in redundat mode then always assume secondary is OK */
00514   if (ec_redstate == ECT_RED_NONE)
00515     wkc2 = 0;
00516   do
00517   {
00518     /* only read frame if not already in */
00519     if (wkc <= EC_NOFRAME)
00520       wkc = ec_inframe(idx, 0);
00521     /* only try secondary if in redundant mode */
00522     if (ec_redstate != ECT_RED_NONE)
00523     {
00524       /* only read frame if not already in */
00525       if (wkc2 <= EC_NOFRAME)
00526         wkc2 = ec_inframe(idx, 1);
00527     }
00528     gettimeofday(&tv1, 0);
00529     /* wait for both frames to arrive or timeout */
00530   } while (((wkc <= EC_NOFRAME) || (wkc2 <= EC_NOFRAME)) && (timercmp(&tv1, &tvs, <)));
00531   /* only do redundant functions when in redundant mode */
00532   if (ec_redstate != ECT_RED_NONE)
00533   {
00534     /* primrx if the reveived MAC source on primary socket */
00535     primrx = 0;
00536     if (wkc > EC_NOFRAME)
00537       primrx = ec_rxsa[idx];
00538     /* secrx if the reveived MAC source on psecondary socket */
00539     secrx = 0;
00540     if (wkc2 > EC_NOFRAME)
00541       secrx = ec_rxsa2[idx];
00542 
00543     /* primary socket got secondary frame and secondary socket got primary frame */
00544     /* normal situation in redundant mode */
00545     if (((primrx == RX_SEC) && (secrx == RX_PRIM)) )
00546     {
00547       /* copy secondary buffer to primary */
00548       memcpy(&ec_rxbuf[idx], &ec_rxbuf2[idx], ec_txbuflength[idx] - ETH_HEADERSIZE);
00549       wkc = wkc2;
00550     }
00551     /* primary socket got nothing or primary frame, and secondary socket got secondary frame */
00552     /* we need to resend TX packet */
00553     if (((primrx == 0) && (secrx == RX_SEC)) ||
00554     ((primrx == RX_PRIM) && (secrx == RX_SEC)) )
00555     {
00556       /* If both primary and secondary have partial connection retransmit the primary received
00557        * frame over the secondary socket. The result from the secondary received frame is a combined
00558        * frame that traversed all slaves in standard order. */
00559       if ( (primrx == RX_PRIM) && (secrx == RX_SEC) )
00560       {
00561         /* copy primary rx to tx buffer */
00562         memcpy(&ec_txbuf[idx][ETH_HEADERSIZE], &ec_rxbuf[idx], ec_txbuflength[idx] - ETH_HEADERSIZE);
00563       }
00564       gettimeofday(&tv1, 0);
00565       tv2.tv_sec = 0;
00566       tv2.tv_usec = EC_TIMEOUTRET;
00567       timeradd(&tv1, &tv2, &tvs);
00568       /* resend secondary tx */
00569       ec_outframe(idx,1);
00570       do
00571       {
00572         /* retrieve frame */
00573         wkc2 = ec_inframe(idx, 1);
00574         gettimeofday(&tv1, 0);
00575       }while ((wkc2 <= EC_NOFRAME) && (timercmp(&tv1, &tvs, <)));
00576       if (wkc2 > EC_NOFRAME)
00577       {
00578         /* copy secondary result to primary rx buffer */
00579         memcpy(&ec_rxbuf[idx], &ec_rxbuf2[idx], ec_txbuflength[idx] - ETH_HEADERSIZE);
00580         wkc = wkc2;
00581       }
00582     }
00583   }
00584 
00585     /* return WKC or EC_NOFRAME */
00586   return wkc;
00587 }
00588 
00595 int ec_waitinframe(uint8 idx, int timeout)
00596 {
00597   int wkc;
00598   struct timeval tv1, tv2, tve;
00599 
00600   gettimeofday(&tv1, 0);
00601   tv2.tv_sec = 0;
00602   tv2.tv_usec = timeout;
00603   timeradd(&tv1, &tv2, &tve);
00604   wkc = ec_waitinframe_red(idx, tve);
00605   /* if nothing received, clear buffer index status so it can be used again */
00606   if (wkc <= EC_NOFRAME)
00607   {
00608     ec_setbufstat(idx, EC_BUF_EMPTY);
00609   }
00610 
00611   return wkc;
00612 }
00613 
00625 int ec_srconfirm(uint8 idx, int timeout)
00626 {
00627   int wkc = EC_NOFRAME;
00628   struct timeval tv1, tv2, tv3, tve, tvs, tvh;
00629 
00630   gettimeofday(&tv1, 0);
00631   tv2.tv_sec = 0;
00632   tv2.tv_usec = timeout;
00633   timeradd(&tv1, &tv2, &tve);
00634   do
00635   {
00636     /* tx frame on primary and if in redundant mode a dummy on secondary */
00637     ec_outframe_red(idx);
00638     gettimeofday(&tv2, 0);
00639     timersub(&tv2, &tv1, &tvh);
00640     hlp_txtime += (int)tvh.tv_usec;
00641     tv1.tv_sec = 0;
00642     if (timeout < EC_TIMEOUTRET)
00643     {
00644       tv1.tv_usec = timeout;
00645     }
00646     else
00647     {
00648       /* normally use partial timout for rx */
00649       tv1.tv_usec = EC_TIMEOUTRET;
00650     }
00651     timeradd(&tv2, &tv1, &tvs);
00652     /* get frame from primary or if in redundant mode possibly from secondary */
00653     wkc = ec_waitinframe_red(idx, tvs);
00654     gettimeofday(&tv3, 0);
00655     timersub(&tv3, &tv2, &tvh);
00656     hlp_rxtime += (int)tvh.tv_usec;
00657     /* wait for answer with WKC>0 or otherwise retry until timeout */
00658   } while ((wkc <= EC_NOFRAME) && (timercmp(&tv3, &tve, <)));
00659   /* if nothing received, clear buffer index status so it can be used again */
00660   if (wkc <= EC_NOFRAME)
00661   {
00662     ec_setbufstat(idx, EC_BUF_EMPTY);
00663   }
00664 
00665   return wkc;
00666 }


youbot_driver
Author(s): Jan Paulus
autogenerated on Mon Oct 6 2014 09:08:01