ethernet.c

Go to the documentation of this file.
00001 /******************************************************************************
00002  *
00003  *  $Id: ethernet.c 533 2006-09-01 12:35:41Z fp $
00004  *
00005  *  Copyright (C) 2006  Florian Pose, Ingenieurgemeinschaft IgH
00006  *
00007  *  This file is part of the IgH EtherCAT Master.
00008  *
00009  *  The IgH EtherCAT Master is free software; you can redistribute it
00010  *  and/or modify it under the terms of the GNU General Public License
00011  *  as published by the Free Software Foundation; either version 2 of the
00012  *  License, or (at your option) any later version.
00013  *
00014  *  The IgH EtherCAT Master is distributed in the hope that it will be
00015  *  useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
00016  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00017  *  GNU General Public License for more details.
00018  *
00019  *  You should have received a copy of the GNU General Public License
00020  *  along with the IgH EtherCAT Master; if not, write to the Free Software
00021  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
00022  *
00023  *  The right to use EtherCAT Technology is granted and comes free of
00024  *  charge under condition of compatibility of product made by
00025  *  Licensee. People intending to distribute/sell products based on the
00026  *  code, have to sign an agreement to guarantee that products using
00027  *  software based on IgH EtherCAT master stay compatible with the actual
00028  *  EtherCAT specification (which are released themselves as an open
00029  *  standard) as the (only) precondition to have the right to use EtherCAT
00030  *  Technology, IP and trade marks.
00031  *
00032  *****************************************************************************/
00033 
00039 /*****************************************************************************/
00040 
00041 #include <linux/netdevice.h>
00042 #include <linux/etherdevice.h>
00043 
00044 #include "../include/ecrt.h"
00045 #include "globals.h"
00046 #include "master.h"
00047 #include "slave.h"
00048 #include "mailbox.h"
00049 #include "ethernet.h"
00050 
00059 #define EOE_DEBUG_LEVEL 0
00060 
00061 /*****************************************************************************/
00062 
00063 void ec_eoe_flush(ec_eoe_t *);
00064 
00065 // state functions
00066 void ec_eoe_state_rx_start(ec_eoe_t *);
00067 void ec_eoe_state_rx_check(ec_eoe_t *);
00068 void ec_eoe_state_rx_fetch(ec_eoe_t *);
00069 void ec_eoe_state_tx_start(ec_eoe_t *);
00070 void ec_eoe_state_tx_sent(ec_eoe_t *);
00071 
00072 // net_device functions
00073 int ec_eoedev_open(struct net_device *);
00074 int ec_eoedev_stop(struct net_device *);
00075 int ec_eoedev_tx(struct sk_buff *, struct net_device *);
00076 struct net_device_stats *ec_eoedev_stats(struct net_device *);
00077 
00078 /*****************************************************************************/
00079 
00085 int ec_eoe_init(ec_eoe_t *eoe )
00086 {
00087     ec_eoe_t **priv;
00088     int result, i;
00089 
00090     eoe->slave = NULL;
00091     ec_datagram_init(&eoe->datagram);
00092     eoe->state = ec_eoe_state_rx_start;
00093     eoe->opened = 0;
00094     eoe->rx_skb = NULL;
00095     eoe->rx_expected_fragment = 0;
00096     INIT_LIST_HEAD(&eoe->tx_queue);
00097     eoe->tx_frame = NULL;
00098     eoe->tx_queue_active = 0;
00099     eoe->tx_queued_frames = 0;
00100     eoe->tx_queue_lock = SPIN_LOCK_UNLOCKED;
00101     eoe->tx_frame_number = 0xFF;
00102     memset(&eoe->stats, 0, sizeof(struct net_device_stats));
00103 
00104     eoe->rx_counter = 0;
00105     eoe->tx_counter = 0;
00106     eoe->rx_rate = 0;
00107     eoe->tx_rate = 0;
00108     eoe->rate_jiffies = 0;
00109 
00110     if (!(eoe->dev =
00111           alloc_netdev(sizeof(ec_eoe_t *), "eoe%d", ether_setup))) {
00112         EC_ERR("Unable to allocate net_device for EoE handler!\n");
00113         goto out_return;
00114     }
00115 
00116     // initialize net_device
00117     eoe->dev->open = ec_eoedev_open;
00118     eoe->dev->stop = ec_eoedev_stop;
00119     eoe->dev->hard_start_xmit = ec_eoedev_tx;
00120     eoe->dev->get_stats = ec_eoedev_stats;
00121 
00122     for (i = 0; i < ETH_ALEN; i++)
00123         eoe->dev->dev_addr[i] = i | (i << 4);
00124 
00125     // initialize private data
00126     priv = netdev_priv(eoe->dev);
00127     *priv = eoe;
00128 
00129     // Usually setting the MTU appropriately makes the upper layers
00130     // do the frame fragmenting. In some cases this doesn't work
00131     // so the MTU is left on the Ethernet standard value and fragmenting
00132     // is done "manually".
00133 #if 0
00134     eoe->dev->mtu = slave->sii_rx_mailbox_size - ETH_HLEN - 10;
00135 #endif
00136 
00137     // connect the net_device to the kernel
00138     if ((result = register_netdev(eoe->dev))) {
00139         EC_ERR("Unable to register net_device: error %i\n", result);
00140         goto out_free;
00141     }
00142 
00143     // make the last address octet unique
00144     eoe->dev->dev_addr[ETH_ALEN - 1] = (uint8_t) eoe->dev->ifindex;
00145 
00146     return 0;
00147 
00148  out_free:
00149     free_netdev(eoe->dev);
00150     eoe->dev = NULL;
00151  out_return:
00152     return -1;
00153 }
00154 
00155 /*****************************************************************************/
00156 
00162 void ec_eoe_clear(ec_eoe_t *eoe )
00163 {
00164     unregister_netdev(eoe->dev);
00165     free_netdev(eoe->dev);
00166 
00167     // empty transmit queue
00168     ec_eoe_flush(eoe);
00169 
00170     if (eoe->tx_frame) {
00171         dev_kfree_skb(eoe->tx_frame->skb);
00172         kfree(eoe->tx_frame);
00173     }
00174 
00175     if (eoe->rx_skb) dev_kfree_skb(eoe->rx_skb);
00176 
00177     ec_datagram_clear(&eoe->datagram);
00178 }
00179 
00180 /*****************************************************************************/
00181 
00186 void ec_eoe_flush(ec_eoe_t *eoe )
00187 {
00188     ec_eoe_frame_t *frame, *next;
00189 
00190     spin_lock_bh(&eoe->tx_queue_lock);
00191 
00192     list_for_each_entry_safe(frame, next, &eoe->tx_queue, queue) {
00193         list_del(&frame->queue);
00194         dev_kfree_skb(frame->skb);
00195         kfree(frame);
00196     }
00197     eoe->tx_queued_frames = 0;
00198 
00199     spin_unlock_bh(&eoe->tx_queue_lock);
00200 }
00201 
00202 /*****************************************************************************/
00203 
00208 int ec_eoe_send(ec_eoe_t *eoe )
00209 {
00210     size_t remaining_size, current_size, complete_offset;
00211     unsigned int last_fragment;
00212     uint8_t *data;
00213 #if EOE_DEBUG_LEVEL > 1
00214     unsigned int i;
00215 #endif
00216 
00217     remaining_size = eoe->tx_frame->skb->len - eoe->tx_offset;
00218 
00219     if (remaining_size <= eoe->slave->sii_tx_mailbox_size - 10) {
00220         current_size = remaining_size;
00221         last_fragment = 1;
00222     }
00223     else {
00224         current_size = ((eoe->slave->sii_tx_mailbox_size - 10) / 32) * 32;
00225         last_fragment = 0;
00226     }
00227 
00228     if (eoe->tx_fragment_number) {
00229         complete_offset = eoe->tx_offset / 32;
00230     }
00231     else {
00232         // complete size in 32 bit blocks, rounded up.
00233         complete_offset = remaining_size / 32 + 1;
00234     }
00235 
00236 #if EOE_DEBUG_LEVEL > 0
00237     EC_DBG("EoE TX sending %sfragment %i with %i octets (%i)."
00238            " %i frames queued.\n", last_fragment ? "last " : "",
00239            eoe->tx_fragment_number, current_size, complete_offset,
00240            eoe->tx_queued_frames);
00241 #endif
00242 
00243 #if EOE_DEBUG_LEVEL > 1
00244     EC_DBG("");
00245     for (i = 0; i < current_size; i++) {
00246         printk("%02X ", eoe->tx_frame->skb->data[eoe->tx_offset + i]);
00247         if ((i + 1) % 16 == 0) {
00248             printk("\n");
00249             EC_DBG("");
00250         }
00251     }
00252     printk("\n");
00253 #endif
00254 
00255     if (!(data = ec_slave_mbox_prepare_send(eoe->slave, &eoe->datagram,
00256                                             0x02, current_size + 4)))
00257         return -1;
00258 
00259     EC_WRITE_U8 (data,     0x00); // eoe fragment req.
00260     EC_WRITE_U8 (data + 1, last_fragment);
00261     EC_WRITE_U16(data + 2, ((eoe->tx_fragment_number & 0x3F) |
00262                             (complete_offset & 0x3F) << 6 |
00263                             (eoe->tx_frame_number & 0x0F) << 12));
00264 
00265     memcpy(data + 4, eoe->tx_frame->skb->data + eoe->tx_offset, current_size);
00266     ec_master_queue_datagram(eoe->slave->master, &eoe->datagram);
00267 
00268     eoe->tx_offset += current_size;
00269     eoe->tx_fragment_number++;
00270     return 0;
00271 }
00272 
00273 /*****************************************************************************/
00274 
00279 void ec_eoe_run(ec_eoe_t *eoe )
00280 {
00281     if (!eoe->opened) return;
00282 
00283     // call state function
00284     eoe->state(eoe);
00285 
00286     // update statistics
00287     if (jiffies - eoe->rate_jiffies > HZ) {
00288         eoe->rx_rate = eoe->rx_counter * 8;
00289         eoe->tx_rate = eoe->tx_counter * 8;
00290         eoe->rx_counter = 0;
00291         eoe->tx_counter = 0;
00292         eoe->rate_jiffies = jiffies;
00293     }
00294 }
00295 
00296 /*****************************************************************************/
00297 
00303 int ec_eoe_active(const ec_eoe_t *eoe )
00304 {
00305     return eoe->slave && eoe->opened;
00306 }
00307 
00308 /******************************************************************************
00309  *  STATE PROCESSING FUNCTIONS
00310  *****************************************************************************/
00311 
00318 void ec_eoe_state_rx_start(ec_eoe_t *eoe )
00319 {
00320     if (!eoe->slave->online || !eoe->slave->master->device->link_state)
00321         return;
00322 
00323     ec_slave_mbox_prepare_check(eoe->slave, &eoe->datagram);
00324     ec_master_queue_datagram(eoe->slave->master, &eoe->datagram);
00325     eoe->state = ec_eoe_state_rx_check;
00326 }
00327 
00328 /*****************************************************************************/
00329 
00336 void ec_eoe_state_rx_check(ec_eoe_t *eoe )
00337 {
00338     if (eoe->datagram.state != EC_DATAGRAM_RECEIVED) {
00339         eoe->stats.rx_errors++;
00340         eoe->state = ec_eoe_state_tx_start;
00341         return;
00342     }
00343 
00344     if (!ec_slave_mbox_check(&eoe->datagram)) {
00345         eoe->state = ec_eoe_state_tx_start;
00346         return;
00347     }
00348 
00349     ec_slave_mbox_prepare_fetch(eoe->slave, &eoe->datagram);
00350     ec_master_queue_datagram(eoe->slave->master, &eoe->datagram);
00351     eoe->state = ec_eoe_state_rx_fetch;
00352 }
00353 
00354 /*****************************************************************************/
00355 
00362 void ec_eoe_state_rx_fetch(ec_eoe_t *eoe )
00363 {
00364     size_t rec_size, data_size;
00365     uint8_t *data, frame_type, last_fragment, time_appended;
00366     uint8_t frame_number, fragment_offset, fragment_number;
00367     off_t offset;
00368 #if EOE_DEBUG_LEVEL > 1
00369     unsigned int i;
00370 #endif
00371 
00372     if (eoe->datagram.state != EC_DATAGRAM_RECEIVED) {
00373         eoe->stats.rx_errors++;
00374         eoe->state = ec_eoe_state_tx_start;
00375         return;
00376     }
00377 
00378     if (!(data = ec_slave_mbox_fetch(eoe->slave, &eoe->datagram,
00379                                      0x02, &rec_size))) {
00380         eoe->stats.rx_errors++;
00381         eoe->state = ec_eoe_state_tx_start;
00382         return;
00383     }
00384 
00385     frame_type = EC_READ_U16(data) & 0x000F;
00386 
00387     if (frame_type != 0x00) {
00388 #if EOE_DEBUG_LEVEL > 0
00389         EC_DBG("other frame received.\n");
00390 #endif
00391         eoe->stats.rx_dropped++;
00392         eoe->state = ec_eoe_state_tx_start;
00393         return;
00394     }
00395 
00396     // EoE Fragment Request received
00397 
00398     last_fragment = (EC_READ_U16(data) >> 8) & 0x0001;
00399     time_appended = (EC_READ_U16(data) >> 9) & 0x0001;
00400     fragment_number = EC_READ_U16(data + 2) & 0x003F;
00401     fragment_offset = (EC_READ_U16(data + 2) >> 6) & 0x003F;
00402     frame_number = (EC_READ_U16(data + 2) >> 12) & 0x000F;
00403 
00404 #if EOE_DEBUG_LEVEL > 0
00405     EC_DBG("EoE RX fragment %i, offset %i, frame %i%s%s,"
00406            " %i octets\n", fragment_number, fragment_offset,
00407            frame_number,
00408            last_fragment ? ", last fragment" : "",
00409            time_appended ? ", + timestamp" : "",
00410            time_appended ? rec_size - 8 : rec_size - 4);
00411 #endif
00412 
00413 #if EOE_DEBUG_LEVEL > 1
00414     EC_DBG("");
00415     for (i = 0; i < rec_size - 4; i++) {
00416         printk("%02X ", data[i + 4]);
00417         if ((i + 1) % 16 == 0) {
00418             printk("\n");
00419             EC_DBG("");
00420         }
00421     }
00422     printk("\n");
00423 #endif
00424 
00425     data_size = time_appended ? rec_size - 8 : rec_size - 4;
00426 
00427     if (!fragment_number) {
00428         if (eoe->rx_skb) {
00429             EC_WARN("EoE RX freeing old socket buffer...\n");
00430             dev_kfree_skb(eoe->rx_skb);
00431         }
00432 
00433         // new socket buffer
00434         if (!(eoe->rx_skb = dev_alloc_skb(fragment_offset * 32))) {
00435             if (printk_ratelimit())
00436                 EC_WARN("EoE RX low on mem. frame dropped.\n");
00437             eoe->stats.rx_dropped++;
00438             eoe->state = ec_eoe_state_tx_start;
00439             return;
00440         }
00441 
00442         eoe->rx_skb_offset = 0;
00443         eoe->rx_skb_size = fragment_offset * 32;
00444         eoe->rx_expected_fragment = 0;
00445     }
00446     else {
00447         if (!eoe->rx_skb) {
00448             eoe->stats.rx_dropped++;
00449             eoe->state = ec_eoe_state_tx_start;
00450             return;
00451         }
00452 
00453         offset = fragment_offset * 32;
00454         if (offset != eoe->rx_skb_offset ||
00455             offset + data_size > eoe->rx_skb_size ||
00456             fragment_number != eoe->rx_expected_fragment) {
00457             dev_kfree_skb(eoe->rx_skb);
00458             eoe->rx_skb = NULL;
00459             eoe->stats.rx_errors++;
00460             eoe->state = ec_eoe_state_tx_start;
00461             return;
00462         }
00463     }
00464 
00465     // copy fragment into socket buffer
00466     memcpy(skb_put(eoe->rx_skb, data_size), data + 4, data_size);
00467     eoe->rx_skb_offset += data_size;
00468 
00469     if (last_fragment) {
00470         // update statistics
00471         eoe->stats.rx_packets++;
00472         eoe->stats.rx_bytes += eoe->rx_skb->len;
00473         eoe->rx_counter += eoe->rx_skb->len;
00474 
00475 #if EOE_DEBUG_LEVEL > 0
00476         EC_DBG("EoE RX frame completed with %u octets.\n",
00477                eoe->rx_skb->len);
00478 #endif
00479 
00480         // pass socket buffer to network stack
00481         eoe->rx_skb->dev = eoe->dev;
00482         eoe->rx_skb->protocol = eth_type_trans(eoe->rx_skb, eoe->dev);
00483         eoe->rx_skb->ip_summed = CHECKSUM_UNNECESSARY;
00484         if (netif_rx(eoe->rx_skb)) {
00485             EC_WARN("EoE RX netif_rx failed.\n");
00486         }
00487         eoe->rx_skb = NULL;
00488 
00489         eoe->state = ec_eoe_state_tx_start;
00490     }
00491     else {
00492         eoe->rx_expected_fragment++;
00493 #if EOE_DEBUG_LEVEL > 0
00494         EC_DBG("EoE RX expecting fragment %i\n",
00495                eoe->rx_expected_fragment);
00496 #endif
00497         eoe->state = ec_eoe_state_rx_start;
00498     }
00499 }
00500 
00501 /*****************************************************************************/
00502 
00509 void ec_eoe_state_tx_start(ec_eoe_t *eoe )
00510 {
00511 #if EOE_DEBUG_LEVEL > 0
00512     unsigned int wakeup = 0;
00513 #endif
00514 
00515     if (!eoe->slave->online || !eoe->slave->master->device->link_state)
00516         return;
00517 
00518     spin_lock_bh(&eoe->tx_queue_lock);
00519 
00520     if (!eoe->tx_queued_frames || list_empty(&eoe->tx_queue)) {
00521         spin_unlock_bh(&eoe->tx_queue_lock);
00522         // no data available.
00523         // start a new receive immediately.
00524         ec_eoe_state_rx_start(eoe);
00525         return;
00526     }
00527 
00528     // take the first frame out of the queue
00529     eoe->tx_frame = list_entry(eoe->tx_queue.next, ec_eoe_frame_t, queue);
00530     list_del(&eoe->tx_frame->queue);
00531     if (!eoe->tx_queue_active &&
00532         eoe->tx_queued_frames == EC_EOE_TX_QUEUE_SIZE / 2) {
00533         netif_wake_queue(eoe->dev);
00534         eoe->tx_queue_active = 1;
00535 #if EOE_DEBUG_LEVEL > 0
00536         wakeup = 1;
00537 #endif
00538     }
00539 
00540     eoe->tx_queued_frames--;
00541     spin_unlock_bh(&eoe->tx_queue_lock);
00542 
00543     eoe->tx_frame_number++;
00544     eoe->tx_frame_number %= 16;
00545     eoe->tx_fragment_number = 0;
00546     eoe->tx_offset = 0;
00547 
00548     if (ec_eoe_send(eoe)) {
00549         dev_kfree_skb(eoe->tx_frame->skb);
00550         kfree(eoe->tx_frame);
00551         eoe->tx_frame = NULL;
00552         eoe->stats.tx_errors++;
00553         eoe->state = ec_eoe_state_rx_start;
00554         return;
00555     }
00556 
00557 #if EOE_DEBUG_LEVEL > 0
00558     if (wakeup) EC_DBG("waking up TX queue...\n");
00559 #endif
00560 
00561     eoe->state = ec_eoe_state_tx_sent;
00562 }
00563 
00564 /*****************************************************************************/
00565 
00572 void ec_eoe_state_tx_sent(ec_eoe_t *eoe )
00573 {
00574     if (eoe->datagram.state != EC_DATAGRAM_RECEIVED) {
00575         eoe->stats.tx_errors++;
00576         eoe->state = ec_eoe_state_rx_start;
00577         return;
00578     }
00579 
00580     if (eoe->datagram.working_counter != 1) {
00581         eoe->stats.tx_errors++;
00582         eoe->state = ec_eoe_state_rx_start;
00583         return;
00584     }
00585 
00586     // frame completely sent
00587     if (eoe->tx_offset >= eoe->tx_frame->skb->len) {
00588         eoe->stats.tx_packets++;
00589         eoe->stats.tx_bytes += eoe->tx_frame->skb->len;
00590         eoe->tx_counter += eoe->tx_frame->skb->len;
00591         dev_kfree_skb(eoe->tx_frame->skb);
00592         kfree(eoe->tx_frame);
00593         eoe->tx_frame = NULL;
00594         eoe->state = ec_eoe_state_rx_start;
00595     }
00596     else { // send next fragment
00597         if (ec_eoe_send(eoe)) {
00598             dev_kfree_skb(eoe->tx_frame->skb);
00599             kfree(eoe->tx_frame);
00600             eoe->tx_frame = NULL;
00601             eoe->stats.tx_errors++;
00602             eoe->state = ec_eoe_state_rx_start;
00603         }
00604     }
00605 }
00606 
00607 /******************************************************************************
00608  *  NET_DEVICE functions
00609  *****************************************************************************/
00610 
00615 int ec_eoedev_open(struct net_device *dev )
00616 {
00617     ec_eoe_t *eoe = *((ec_eoe_t **) netdev_priv(dev));
00618     ec_eoe_flush(eoe);
00619     eoe->opened = 1;
00620     netif_start_queue(dev);
00621     eoe->tx_queue_active = 1;
00622     EC_INFO("%s opened.\n", dev->name);
00623     if (!eoe->slave)
00624         EC_WARN("device %s is not coupled to any EoE slave!\n", dev->name);
00625     else {
00626         eoe->slave->requested_state = EC_SLAVE_STATE_OP;
00627         eoe->slave->error_flag = 0;
00628     }
00629     return 0;
00630 }
00631 
00632 /*****************************************************************************/
00633 
00638 int ec_eoedev_stop(struct net_device *dev )
00639 {
00640     ec_eoe_t *eoe = *((ec_eoe_t **) netdev_priv(dev));
00641     netif_stop_queue(dev);
00642     eoe->tx_queue_active = 0;
00643     eoe->opened = 0;
00644     ec_eoe_flush(eoe);
00645     EC_INFO("%s stopped.\n", dev->name);
00646     if (!eoe->slave)
00647         EC_WARN("device %s is not coupled to any EoE slave!\n", dev->name);
00648     else {
00649         eoe->slave->requested_state = EC_SLAVE_STATE_INIT;
00650         eoe->slave->error_flag = 0;
00651     }
00652     return 0;
00653 }
00654 
00655 /*****************************************************************************/
00656 
00661 int ec_eoedev_tx(struct sk_buff *skb, 
00662                  struct net_device *dev 
00663                 )
00664 {
00665     ec_eoe_t *eoe = *((ec_eoe_t **) netdev_priv(dev));
00666     ec_eoe_frame_t *frame;
00667 
00668 #if 0
00669     if (skb->len > eoe->slave->sii_tx_mailbox_size - 10) {
00670         EC_WARN("EoE TX frame (%i octets) exceeds MTU. dropping.\n", skb->len);
00671         dev_kfree_skb(skb);
00672         eoe->stats.tx_dropped++;
00673         return 0;
00674     }
00675 #endif
00676 
00677     if (!(frame =
00678           (ec_eoe_frame_t *) kmalloc(sizeof(ec_eoe_frame_t), GFP_ATOMIC))) {
00679         if (printk_ratelimit())
00680             EC_WARN("EoE TX: low on mem. frame dropped.\n");
00681         return 1;
00682     }
00683 
00684     frame->skb = skb;
00685 
00686     spin_lock_bh(&eoe->tx_queue_lock);
00687     list_add_tail(&frame->queue, &eoe->tx_queue);
00688     eoe->tx_queued_frames++;
00689     if (eoe->tx_queued_frames == EC_EOE_TX_QUEUE_SIZE) {
00690         netif_stop_queue(dev);
00691         eoe->tx_queue_active = 0;
00692     }
00693     spin_unlock_bh(&eoe->tx_queue_lock);
00694 
00695 #if EOE_DEBUG_LEVEL > 0
00696     EC_DBG("EoE TX queued frame with %i octets (%i frames queued).\n",
00697            skb->len, eoe->tx_queued_frames);
00698     if (!eoe->tx_queue_active)
00699         EC_WARN("EoE TX queue is now full.\n");
00700 #endif
00701 
00702     return 0;
00703 }
00704 
00705 /*****************************************************************************/
00706 
00711 struct net_device_stats *ec_eoedev_stats(struct net_device *dev)
00713 {
00714     ec_eoe_t *eoe = *((ec_eoe_t **) netdev_priv(dev));
00715     return &eoe->stats;
00716 }
00717 
00718 /*****************************************************************************/

Generated on Fri Sep 1 14:56:56 2006 for IgH EtherCAT master by  doxygen 1.4.6