Main Page | Modules | Data Structures | Directories | File List | Data Fields | Globals | Related Pages

ethernet.c

Go to the documentation of this file.
00001 /******************************************************************************
00002  *
00003  *  $Id: ethernet.c 682 2006-11-07 12:13:30Z 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, mbox_prot;
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                                      &mbox_prot, &rec_size))) {
00380         eoe->stats.rx_errors++;
00381         eoe->state = ec_eoe_state_tx_start;
00382         return;
00383     }
00384 
00385     if (mbox_prot != 0x02) { // EoE
00386         eoe->stats.rx_errors++;
00387         eoe->state = ec_eoe_state_tx_start;
00388         return;
00389     }
00390 
00391     frame_type = EC_READ_U16(data) & 0x000F;
00392 
00393     if (frame_type != 0x00) {
00394 #if EOE_DEBUG_LEVEL > 0
00395         EC_DBG("other frame received.\n");
00396 #endif
00397         eoe->stats.rx_dropped++;
00398         eoe->state = ec_eoe_state_tx_start;
00399         return;
00400     }
00401 
00402     // EoE Fragment Request received
00403 
00404     last_fragment = (EC_READ_U16(data) >> 8) & 0x0001;
00405     time_appended = (EC_READ_U16(data) >> 9) & 0x0001;
00406     fragment_number = EC_READ_U16(data + 2) & 0x003F;
00407     fragment_offset = (EC_READ_U16(data + 2) >> 6) & 0x003F;
00408     frame_number = (EC_READ_U16(data + 2) >> 12) & 0x000F;
00409 
00410 #if EOE_DEBUG_LEVEL > 0
00411     EC_DBG("EoE RX fragment %i, offset %i, frame %i%s%s,"
00412            " %i octets\n", fragment_number, fragment_offset,
00413            frame_number,
00414            last_fragment ? ", last fragment" : "",
00415            time_appended ? ", + timestamp" : "",
00416            time_appended ? rec_size - 8 : rec_size - 4);
00417 #endif
00418 
00419 #if EOE_DEBUG_LEVEL > 1
00420     EC_DBG("");
00421     for (i = 0; i < rec_size - 4; i++) {
00422         printk("%02X ", data[i + 4]);
00423         if ((i + 1) % 16 == 0) {
00424             printk("\n");
00425             EC_DBG("");
00426         }
00427     }
00428     printk("\n");
00429 #endif
00430 
00431     data_size = time_appended ? rec_size - 8 : rec_size - 4;
00432 
00433     if (!fragment_number) {
00434         if (eoe->rx_skb) {
00435             EC_WARN("EoE RX freeing old socket buffer...\n");
00436             dev_kfree_skb(eoe->rx_skb);
00437         }
00438 
00439         // new socket buffer
00440         if (!(eoe->rx_skb = dev_alloc_skb(fragment_offset * 32))) {
00441             if (printk_ratelimit())
00442                 EC_WARN("EoE RX low on mem. frame dropped.\n");
00443             eoe->stats.rx_dropped++;
00444             eoe->state = ec_eoe_state_tx_start;
00445             return;
00446         }
00447 
00448         eoe->rx_skb_offset = 0;
00449         eoe->rx_skb_size = fragment_offset * 32;
00450         eoe->rx_expected_fragment = 0;
00451     }
00452     else {
00453         if (!eoe->rx_skb) {
00454             eoe->stats.rx_dropped++;
00455             eoe->state = ec_eoe_state_tx_start;
00456             return;
00457         }
00458 
00459         offset = fragment_offset * 32;
00460         if (offset != eoe->rx_skb_offset ||
00461             offset + data_size > eoe->rx_skb_size ||
00462             fragment_number != eoe->rx_expected_fragment) {
00463             dev_kfree_skb(eoe->rx_skb);
00464             eoe->rx_skb = NULL;
00465             eoe->stats.rx_errors++;
00466             eoe->state = ec_eoe_state_tx_start;
00467             return;
00468         }
00469     }
00470 
00471     // copy fragment into socket buffer
00472     memcpy(skb_put(eoe->rx_skb, data_size), data + 4, data_size);
00473     eoe->rx_skb_offset += data_size;
00474 
00475     if (last_fragment) {
00476         // update statistics
00477         eoe->stats.rx_packets++;
00478         eoe->stats.rx_bytes += eoe->rx_skb->len;
00479         eoe->rx_counter += eoe->rx_skb->len;
00480 
00481 #if EOE_DEBUG_LEVEL > 0
00482         EC_DBG("EoE RX frame completed with %u octets.\n",
00483                eoe->rx_skb->len);
00484 #endif
00485 
00486         // pass socket buffer to network stack
00487         eoe->rx_skb->dev = eoe->dev;
00488         eoe->rx_skb->protocol = eth_type_trans(eoe->rx_skb, eoe->dev);
00489         eoe->rx_skb->ip_summed = CHECKSUM_UNNECESSARY;
00490         if (netif_rx(eoe->rx_skb)) {
00491             EC_WARN("EoE RX netif_rx failed.\n");
00492         }
00493         eoe->rx_skb = NULL;
00494 
00495         eoe->state = ec_eoe_state_tx_start;
00496     }
00497     else {
00498         eoe->rx_expected_fragment++;
00499 #if EOE_DEBUG_LEVEL > 0
00500         EC_DBG("EoE RX expecting fragment %i\n",
00501                eoe->rx_expected_fragment);
00502 #endif
00503         eoe->state = ec_eoe_state_rx_start;
00504     }
00505 }
00506 
00507 /*****************************************************************************/
00508 
00515 void ec_eoe_state_tx_start(ec_eoe_t *eoe )
00516 {
00517 #if EOE_DEBUG_LEVEL > 0
00518     unsigned int wakeup = 0;
00519 #endif
00520 
00521     if (!eoe->slave->online || !eoe->slave->master->device->link_state)
00522         return;
00523 
00524     spin_lock_bh(&eoe->tx_queue_lock);
00525 
00526     if (!eoe->tx_queued_frames || list_empty(&eoe->tx_queue)) {
00527         spin_unlock_bh(&eoe->tx_queue_lock);
00528         // no data available.
00529         // start a new receive immediately.
00530         ec_eoe_state_rx_start(eoe);
00531         return;
00532     }
00533 
00534     // take the first frame out of the queue
00535     eoe->tx_frame = list_entry(eoe->tx_queue.next, ec_eoe_frame_t, queue);
00536     list_del(&eoe->tx_frame->queue);
00537     if (!eoe->tx_queue_active &&
00538         eoe->tx_queued_frames == EC_EOE_TX_QUEUE_SIZE / 2) {
00539         netif_wake_queue(eoe->dev);
00540         eoe->tx_queue_active = 1;
00541 #if EOE_DEBUG_LEVEL > 0
00542         wakeup = 1;
00543 #endif
00544     }
00545 
00546     eoe->tx_queued_frames--;
00547     spin_unlock_bh(&eoe->tx_queue_lock);
00548 
00549     eoe->tx_frame_number++;
00550     eoe->tx_frame_number %= 16;
00551     eoe->tx_fragment_number = 0;
00552     eoe->tx_offset = 0;
00553 
00554     if (ec_eoe_send(eoe)) {
00555         dev_kfree_skb(eoe->tx_frame->skb);
00556         kfree(eoe->tx_frame);
00557         eoe->tx_frame = NULL;
00558         eoe->stats.tx_errors++;
00559         eoe->state = ec_eoe_state_rx_start;
00560         return;
00561     }
00562 
00563 #if EOE_DEBUG_LEVEL > 0
00564     if (wakeup) EC_DBG("waking up TX queue...\n");
00565 #endif
00566 
00567     eoe->state = ec_eoe_state_tx_sent;
00568 }
00569 
00570 /*****************************************************************************/
00571 
00578 void ec_eoe_state_tx_sent(ec_eoe_t *eoe )
00579 {
00580     if (eoe->datagram.state != EC_DATAGRAM_RECEIVED) {
00581         eoe->stats.tx_errors++;
00582         eoe->state = ec_eoe_state_rx_start;
00583         return;
00584     }
00585 
00586     if (eoe->datagram.working_counter != 1) {
00587         eoe->stats.tx_errors++;
00588         eoe->state = ec_eoe_state_rx_start;
00589         return;
00590     }
00591 
00592     // frame completely sent
00593     if (eoe->tx_offset >= eoe->tx_frame->skb->len) {
00594         eoe->stats.tx_packets++;
00595         eoe->stats.tx_bytes += eoe->tx_frame->skb->len;
00596         eoe->tx_counter += eoe->tx_frame->skb->len;
00597         dev_kfree_skb(eoe->tx_frame->skb);
00598         kfree(eoe->tx_frame);
00599         eoe->tx_frame = NULL;
00600         eoe->state = ec_eoe_state_rx_start;
00601     }
00602     else { // send next fragment
00603         if (ec_eoe_send(eoe)) {
00604             dev_kfree_skb(eoe->tx_frame->skb);
00605             kfree(eoe->tx_frame);
00606             eoe->tx_frame = NULL;
00607             eoe->stats.tx_errors++;
00608             eoe->state = ec_eoe_state_rx_start;
00609         }
00610     }
00611 }
00612 
00613 /******************************************************************************
00614  *  NET_DEVICE functions
00615  *****************************************************************************/
00616 
00621 int ec_eoedev_open(struct net_device *dev )
00622 {
00623     ec_eoe_t *eoe = *((ec_eoe_t **) netdev_priv(dev));
00624     ec_eoe_flush(eoe);
00625     eoe->opened = 1;
00626     netif_start_queue(dev);
00627     eoe->tx_queue_active = 1;
00628     EC_INFO("%s opened.\n", dev->name);
00629     if (!eoe->slave)
00630         EC_WARN("Device %s is not coupled to any EoE slave!\n", dev->name);
00631     else {
00632         ec_slave_request_state(eoe->slave, EC_SLAVE_STATE_OP);
00633     }
00634     return 0;
00635 }
00636 
00637 /*****************************************************************************/
00638 
00643 int ec_eoedev_stop(struct net_device *dev )
00644 {
00645     ec_eoe_t *eoe = *((ec_eoe_t **) netdev_priv(dev));
00646     netif_stop_queue(dev);
00647     eoe->tx_queue_active = 0;
00648     eoe->opened = 0;
00649     ec_eoe_flush(eoe);
00650     EC_INFO("%s stopped.\n", dev->name);
00651     if (!eoe->slave)
00652         EC_WARN("Device %s is not coupled to any EoE slave!\n", dev->name);
00653     else {
00654         ec_slave_request_state(eoe->slave, EC_SLAVE_STATE_PREOP);
00655     }
00656     return 0;
00657 }
00658 
00659 /*****************************************************************************/
00660 
00665 int ec_eoedev_tx(struct sk_buff *skb, 
00666                  struct net_device *dev 
00667                 )
00668 {
00669     ec_eoe_t *eoe = *((ec_eoe_t **) netdev_priv(dev));
00670     ec_eoe_frame_t *frame;
00671 
00672 #if 0
00673     if (skb->len > eoe->slave->sii_tx_mailbox_size - 10) {
00674         EC_WARN("EoE TX frame (%i octets) exceeds MTU. dropping.\n", skb->len);
00675         dev_kfree_skb(skb);
00676         eoe->stats.tx_dropped++;
00677         return 0;
00678     }
00679 #endif
00680 
00681     if (!(frame =
00682           (ec_eoe_frame_t *) kmalloc(sizeof(ec_eoe_frame_t), GFP_ATOMIC))) {
00683         if (printk_ratelimit())
00684             EC_WARN("EoE TX: low on mem. frame dropped.\n");
00685         return 1;
00686     }
00687 
00688     frame->skb = skb;
00689 
00690     spin_lock_bh(&eoe->tx_queue_lock);
00691     list_add_tail(&frame->queue, &eoe->tx_queue);
00692     eoe->tx_queued_frames++;
00693     if (eoe->tx_queued_frames == EC_EOE_TX_QUEUE_SIZE) {
00694         netif_stop_queue(dev);
00695         eoe->tx_queue_active = 0;
00696     }
00697     spin_unlock_bh(&eoe->tx_queue_lock);
00698 
00699 #if EOE_DEBUG_LEVEL > 0
00700     EC_DBG("EoE TX queued frame with %i octets (%i frames queued).\n",
00701            skb->len, eoe->tx_queued_frames);
00702     if (!eoe->tx_queue_active)
00703         EC_WARN("EoE TX queue is now full.\n");
00704 #endif
00705 
00706     return 0;
00707 }
00708 
00709 /*****************************************************************************/
00710 
00715 struct net_device_stats *ec_eoedev_stats(struct net_device *dev)
00717 {
00718     ec_eoe_t *eoe = *((ec_eoe_t **) netdev_priv(dev));
00719     return &eoe->stats;
00720 }
00721 
00722 /*****************************************************************************/

Generated on Tue Nov 7 15:03:35 2006 for IgH EtherCAT master by  doxygen 1.4.4