master/ethernet.c
author Patrick Bruenn <p.bruenn@beckhoff.com>
Tue, 12 Apr 2016 11:17:36 +0200
branchstable-1.5
changeset 2654 b3f6b3e5ef29
parent 2631 827abf3c50de
permissions -rw-r--r--
devices/ccat: revert "limit rx processing to one frame per poll"

revert "limit rx processing to one frame per poll", which caused etherlab
frame timeouts in setups with more than one frame per cycle.
/******************************************************************************
 *
 *  $Id$
 *
 *  Copyright (C) 2006-2008  Florian Pose, Ingenieurgemeinschaft IgH
 *
 *  This file is part of the IgH EtherCAT Master.
 *
 *  The IgH EtherCAT Master 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.
 *
 *  The IgH EtherCAT Master 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.
 *
 *  You should have received a copy of the GNU General Public License along
 *  with the IgH EtherCAT Master; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 *  ---
 *
 *  The license mentioned above concerns the source code only. Using the
 *  EtherCAT technology and brand is only permitted in compliance with the
 *  industrial property and similar rights of Beckhoff Automation GmbH.
 *
 *****************************************************************************/

/**
   \file
   Ethernet over EtherCAT (EoE).
*/

/*****************************************************************************/

#include <linux/version.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>

#include "globals.h"
#include "master.h"
#include "slave.h"
#include "mailbox.h"
#include "ethernet.h"

/*****************************************************************************/

/** Defines the debug level of EoE processing.
 *
 * 0 = No debug messages.
 * 1 = Output warnings.
 * 2 = Output actions.
 * 3 = Output actions and frame data.
 */
#define EOE_DEBUG_LEVEL 1

/** Size of the EoE tx queue.
 */
#define EC_EOE_TX_QUEUE_SIZE 100

/** Number of tries.
 */
#define EC_EOE_TRIES 100

/*****************************************************************************/

void ec_eoe_flush(ec_eoe_t *);

// state functions
void ec_eoe_state_rx_start(ec_eoe_t *);
void ec_eoe_state_rx_check(ec_eoe_t *);
void ec_eoe_state_rx_fetch(ec_eoe_t *);
void ec_eoe_state_tx_start(ec_eoe_t *);
void ec_eoe_state_tx_sent(ec_eoe_t *);

// net_device functions
int ec_eoedev_open(struct net_device *);
int ec_eoedev_stop(struct net_device *);
int ec_eoedev_tx(struct sk_buff *, struct net_device *);
struct net_device_stats *ec_eoedev_stats(struct net_device *);

/*****************************************************************************/

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29)
/** Device operations for EoE interfaces.
 */
static const struct net_device_ops ec_eoedev_ops = {
    .ndo_open = ec_eoedev_open,
    .ndo_stop = ec_eoedev_stop,
    .ndo_start_xmit = ec_eoedev_tx,
    .ndo_get_stats = ec_eoedev_stats,
};
#endif

/*****************************************************************************/

/** EoE constructor.
 *
 * Initializes the EoE handler, creates a net_device and registers it.
 *
 * \return Zero on success, otherwise a negative error code.
 */
int ec_eoe_init(
        ec_eoe_t *eoe, /**< EoE handler */
        ec_slave_t *slave /**< EtherCAT slave */
        )
{
    ec_eoe_t **priv;
    int i, ret = 0;
    char name[EC_DATAGRAM_NAME_SIZE];

    eoe->slave = slave;

    ec_datagram_init(&eoe->datagram);
    eoe->queue_datagram = 0;
    eoe->state = ec_eoe_state_rx_start;
    eoe->opened = 0;
    eoe->rx_skb = NULL;
    eoe->rx_expected_fragment = 0;
    INIT_LIST_HEAD(&eoe->tx_queue);
    eoe->tx_frame = NULL;
    eoe->tx_queue_active = 0;
    eoe->tx_queue_size = EC_EOE_TX_QUEUE_SIZE;
    eoe->tx_queued_frames = 0;

    sema_init(&eoe->tx_queue_sem, 1);
    eoe->tx_frame_number = 0xFF;
    memset(&eoe->stats, 0, sizeof(struct net_device_stats));

    eoe->rx_counter = 0;
    eoe->tx_counter = 0;
    eoe->rx_rate = 0;
    eoe->tx_rate = 0;
    eoe->rate_jiffies = 0;
    eoe->rx_idle = 1;
    eoe->tx_idle = 1;

    /* device name eoe<MASTER>[as]<SLAVE>, because networking scripts don't
     * like hyphens etc. in interface names. */
    if (slave->effective_alias) {
        snprintf(name, EC_DATAGRAM_NAME_SIZE,
                "eoe%ua%u", slave->master->index, slave->effective_alias);
    } else {
        snprintf(name, EC_DATAGRAM_NAME_SIZE,
                "eoe%us%u", slave->master->index, slave->ring_position);
    }

    snprintf(eoe->datagram.name, EC_DATAGRAM_NAME_SIZE, name);

#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 17, 0)
    eoe->dev = alloc_netdev(sizeof(ec_eoe_t *), name, NET_NAME_UNKNOWN,
            ether_setup);
#else
    eoe->dev = alloc_netdev(sizeof(ec_eoe_t *), name, ether_setup);
#endif
    if (!eoe->dev) {
        EC_SLAVE_ERR(slave, "Unable to allocate net_device %s"
                " for EoE handler!\n", name);
        ret = -ENODEV;
        goto out_return;
    }

    // initialize net_device
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29)
    eoe->dev->netdev_ops = &ec_eoedev_ops;
#else
    eoe->dev->open = ec_eoedev_open;
    eoe->dev->stop = ec_eoedev_stop;
    eoe->dev->hard_start_xmit = ec_eoedev_tx;
    eoe->dev->get_stats = ec_eoedev_stats;
#endif

    for (i = 0; i < ETH_ALEN; i++)
        eoe->dev->dev_addr[i] = i | (i << 4);

    // initialize private data
    priv = netdev_priv(eoe->dev);
    *priv = eoe;

    // Usually setting the MTU appropriately makes the upper layers
    // do the frame fragmenting. In some cases this doesn't work
    // so the MTU is left on the Ethernet standard value and fragmenting
    // is done "manually".
#if 0
    eoe->dev->mtu = slave->configured_rx_mailbox_size - ETH_HLEN - 10;
#endif

    // connect the net_device to the kernel
    ret = register_netdev(eoe->dev);
    if (ret) {
        EC_SLAVE_ERR(slave, "Unable to register net_device:"
                " error %i\n", ret);
        goto out_free;
    }

    // make the last address octet unique
    eoe->dev->dev_addr[ETH_ALEN - 1] = (uint8_t) eoe->dev->ifindex;
    return 0;

 out_free:
    free_netdev(eoe->dev);
    eoe->dev = NULL;
 out_return:
    return ret;
}

/*****************************************************************************/

/** EoE destructor.
 *
 * Unregisteres the net_device and frees allocated memory.
 */
void ec_eoe_clear(ec_eoe_t *eoe /**< EoE handler */)
{
    unregister_netdev(eoe->dev); // possibly calls close callback

    // empty transmit queue
    ec_eoe_flush(eoe);

    if (eoe->tx_frame) {
        dev_kfree_skb(eoe->tx_frame->skb);
        kfree(eoe->tx_frame);
    }

    if (eoe->rx_skb)
        dev_kfree_skb(eoe->rx_skb);

    free_netdev(eoe->dev);

    ec_datagram_clear(&eoe->datagram);
}

/*****************************************************************************/

/** Empties the transmit queue.
 */
void ec_eoe_flush(ec_eoe_t *eoe /**< EoE handler */)
{
    ec_eoe_frame_t *frame, *next;

    down(&eoe->tx_queue_sem);

    list_for_each_entry_safe(frame, next, &eoe->tx_queue, queue) {
        list_del(&frame->queue);
        dev_kfree_skb(frame->skb);
        kfree(frame);
    }
    eoe->tx_queued_frames = 0;

    up(&eoe->tx_queue_sem);
}

/*****************************************************************************/

/** Sends a frame or the next fragment.
 *
 * \return Zero on success, otherwise a negative error code.
 */
int ec_eoe_send(ec_eoe_t *eoe /**< EoE handler */)
{
    size_t remaining_size, current_size, complete_offset;
    unsigned int last_fragment;
    uint8_t *data;
#if EOE_DEBUG_LEVEL >= 3
    unsigned int i;
#endif

    remaining_size = eoe->tx_frame->skb->len - eoe->tx_offset;

    if (remaining_size <= eoe->slave->configured_tx_mailbox_size - 10) {
        current_size = remaining_size;
        last_fragment = 1;
    } else {
        current_size = ((eoe->slave->configured_tx_mailbox_size - 10) / 32) * 32;
        last_fragment = 0;
    }

    if (eoe->tx_fragment_number) {
        complete_offset = eoe->tx_offset / 32;
    }
    else {
        // complete size in 32 bit blocks, rounded up.
        complete_offset = remaining_size / 32 + 1;
    }

#if EOE_DEBUG_LEVEL >= 2
    EC_SLAVE_DBG(slave, 0, "EoE %s TX sending fragment %u%s"
            " with %u octets (%u). %u frames queued.\n",
            eoe->dev->name, eoe->tx_fragment_number,
            last_fragment ? "" : "+", current_size, complete_offset,
            eoe->tx_queued_frames);
#endif

#if EOE_DEBUG_LEVEL >= 3
    EC_SLAVE_DBG(master, 0, "");
    for (i = 0; i < current_size; i++) {
        printk("%02X ", eoe->tx_frame->skb->data[eoe->tx_offset + i]);
        if ((i + 1) % 16 == 0) {
            printk("\n");
            EC_SLAVE_DBG(master, 0, "");
        }
    }
    printk("\n");
#endif

    data = ec_slave_mbox_prepare_send(eoe->slave, &eoe->datagram,
            0x02, current_size + 4);
    if (IS_ERR(data))
        return PTR_ERR(data);

    EC_WRITE_U8 (data,     0x00); // eoe fragment req.
    EC_WRITE_U8 (data + 1, last_fragment);
    EC_WRITE_U16(data + 2, ((eoe->tx_fragment_number & 0x3F) |
                            (complete_offset & 0x3F) << 6 |
                            (eoe->tx_frame_number & 0x0F) << 12));

    memcpy(data + 4, eoe->tx_frame->skb->data + eoe->tx_offset, current_size);
    eoe->queue_datagram = 1;

    eoe->tx_offset += current_size;
    eoe->tx_fragment_number++;
    return 0;
}

/*****************************************************************************/

/** Runs the EoE state machine.
 */
void ec_eoe_run(ec_eoe_t *eoe /**< EoE handler */)
{
    if (!eoe->opened)
        return;

    // if the datagram was not sent, or is not yet received, skip this cycle
    if (eoe->queue_datagram || eoe->datagram.state == EC_DATAGRAM_SENT)
        return;

    // call state function
    eoe->state(eoe);

    // update statistics
    if (jiffies - eoe->rate_jiffies > HZ) {
        eoe->rx_rate = eoe->rx_counter;
        eoe->tx_rate = eoe->tx_counter;
        eoe->rx_counter = 0;
        eoe->tx_counter = 0;
        eoe->rate_jiffies = jiffies;
    }

    ec_datagram_output_stats(&eoe->datagram);
}

/*****************************************************************************/

/** Queues the datagram, if necessary.
 */
void ec_eoe_queue(ec_eoe_t *eoe /**< EoE handler */)
{
   if (eoe->queue_datagram) {
       ec_master_queue_datagram_ext(eoe->slave->master, &eoe->datagram);
       eoe->queue_datagram = 0;
   }
}

/*****************************************************************************/

/** Returns the state of the device.
 *
 * \return 1 if the device is "up", 0 if it is "down"
 */
int ec_eoe_is_open(const ec_eoe_t *eoe /**< EoE handler */)
{
    return eoe->opened;
}

/*****************************************************************************/

/** Returns the idle state.
 *
 * \retval 1 The device is idle.
 * \retval 0 The device is busy.
 */
int ec_eoe_is_idle(const ec_eoe_t *eoe /**< EoE handler */)
{
    return eoe->rx_idle && eoe->tx_idle;
}

/******************************************************************************
 *  STATE PROCESSING FUNCTIONS
 *****************************************************************************/

/** State: RX_START.
 *
 * Starts a new receiving sequence by queueing a datagram that checks the
 * slave's mailbox for a new EoE datagram.
 *
 * \todo Use both devices.
 */
void ec_eoe_state_rx_start(ec_eoe_t *eoe /**< EoE handler */)
{
    if (eoe->slave->error_flag ||
            !eoe->slave->master->devices[EC_DEVICE_MAIN].link_state) {
        eoe->rx_idle = 1;
        eoe->tx_idle = 1;
        return;
    }

    ec_slave_mbox_prepare_check(eoe->slave, &eoe->datagram);
    eoe->queue_datagram = 1;
    eoe->state = ec_eoe_state_rx_check;
}

/*****************************************************************************/

/** State: RX_CHECK.
 *
 * Processes the checking datagram sent in RX_START and issues a receive
 * datagram, if new data is available.
 */
void ec_eoe_state_rx_check(ec_eoe_t *eoe /**< EoE handler */)
{
    if (eoe->datagram.state != EC_DATAGRAM_RECEIVED) {
        eoe->stats.rx_errors++;
#if EOE_DEBUG_LEVEL >= 1
        EC_SLAVE_WARN(eoe->slave, "Failed to receive mbox"
                " check datagram for %s.\n", eoe->dev->name);
#endif
        eoe->state = ec_eoe_state_tx_start;
        return;
    }

    if (!ec_slave_mbox_check(&eoe->datagram)) {
        eoe->rx_idle = 1;
        eoe->state = ec_eoe_state_tx_start;
        return;
    }

    eoe->rx_idle = 0;
    ec_slave_mbox_prepare_fetch(eoe->slave, &eoe->datagram);
    eoe->queue_datagram = 1;
    eoe->state = ec_eoe_state_rx_fetch;
}

/*****************************************************************************/

/** State: RX_FETCH.
 *
 * Checks if the requested data of RX_CHECK was received and processes the EoE
 * datagram.
 */
void ec_eoe_state_rx_fetch(ec_eoe_t *eoe /**< EoE handler */)
{
    size_t rec_size, data_size;
    uint8_t *data, frame_type, last_fragment, time_appended, mbox_prot;
    uint8_t fragment_offset, fragment_number;
#if EOE_DEBUG_LEVEL >= 2
    uint8_t frame_number;
#endif
    off_t offset;
#if EOE_DEBUG_LEVEL >= 3
    unsigned int i;
#endif

    if (eoe->datagram.state != EC_DATAGRAM_RECEIVED) {
        eoe->stats.rx_errors++;
#if EOE_DEBUG_LEVEL >= 1
        EC_SLAVE_WARN(eoe->slave, "Failed to receive mbox"
                " fetch datagram for %s.\n", eoe->dev->name);
#endif
        eoe->state = ec_eoe_state_tx_start;
        return;
    }

    data = ec_slave_mbox_fetch(eoe->slave, &eoe->datagram,
            &mbox_prot, &rec_size);
    if (IS_ERR(data)) {
        eoe->stats.rx_errors++;
#if EOE_DEBUG_LEVEL >= 1
        EC_SLAVE_WARN(eoe->slave, "Invalid mailbox response for %s.\n",
                eoe->dev->name);
#endif
        eoe->state = ec_eoe_state_tx_start;
        return;
    }

    if (mbox_prot != 0x02) { // EoE FIXME mailbox handler necessary
        eoe->stats.rx_errors++;
#if EOE_DEBUG_LEVEL >= 1
        EC_SLAVE_WARN(eoe->slave, "Other mailbox protocol response for %s.\n",
                eoe->dev->name);
#endif
        eoe->state = ec_eoe_state_tx_start;
        return;
    }

    frame_type = EC_READ_U16(data) & 0x000F;

    if (frame_type != 0x00) {
#if EOE_DEBUG_LEVEL >= 1
        EC_SLAVE_WARN(eoe->slave, "%s: Other frame received."
                " Dropping.\n", eoe->dev->name);
#endif
        eoe->stats.rx_dropped++;
        eoe->state = ec_eoe_state_tx_start;
        return;
    }

    // EoE Fragment Request received

    last_fragment = (EC_READ_U16(data) >> 8) & 0x0001;
    time_appended = (EC_READ_U16(data) >> 9) & 0x0001;
    fragment_number = EC_READ_U16(data + 2) & 0x003F;
    fragment_offset = (EC_READ_U16(data + 2) >> 6) & 0x003F;
#if EOE_DEBUG_LEVEL >= 2
    frame_number = (EC_READ_U16(data + 2) >> 12) & 0x000F;
#endif

#if EOE_DEBUG_LEVEL >= 2
    EC_SLAVE_DBG(eoe->slave, 0, "EoE %s RX fragment %u%s, offset %u,"
            " frame %u%s, %u octets\n", eoe->dev->name, fragment_number,
           last_fragment ? "" : "+", fragment_offset, frame_number,
           time_appended ? ", + timestamp" : "",
           time_appended ? rec_size - 8 : rec_size - 4);
#endif

#if EOE_DEBUG_LEVEL >= 3
    EC_SLAVE_DBG(eoe->slave, 0, "");
    for (i = 0; i < rec_size - 4; i++) {
        printk("%02X ", data[i + 4]);
        if ((i + 1) % 16 == 0) {
            printk("\n");
            EC_SLAVE_DBG(eoe->slave, 0, "");
        }
    }
    printk("\n");
#endif

    data_size = time_appended ? rec_size - 8 : rec_size - 4;

    if (!fragment_number) {
        if (eoe->rx_skb) {
            EC_SLAVE_WARN(eoe->slave, "EoE RX freeing old socket buffer.\n");
            dev_kfree_skb(eoe->rx_skb);
        }

        // new socket buffer
        if (!(eoe->rx_skb = dev_alloc_skb(fragment_offset * 32))) {
            if (printk_ratelimit())
                EC_SLAVE_WARN(eoe->slave, "EoE RX low on mem,"
                        " frame dropped.\n");
            eoe->stats.rx_dropped++;
            eoe->state = ec_eoe_state_tx_start;
            return;
        }

        eoe->rx_skb_offset = 0;
        eoe->rx_skb_size = fragment_offset * 32;
        eoe->rx_expected_fragment = 0;
    }
    else {
        if (!eoe->rx_skb) {
            eoe->stats.rx_dropped++;
            eoe->state = ec_eoe_state_tx_start;
            return;
        }

        offset = fragment_offset * 32;
        if (offset != eoe->rx_skb_offset ||
            offset + data_size > eoe->rx_skb_size ||
            fragment_number != eoe->rx_expected_fragment) {
            dev_kfree_skb(eoe->rx_skb);
            eoe->rx_skb = NULL;
            eoe->stats.rx_errors++;
#if EOE_DEBUG_LEVEL >= 1
            EC_SLAVE_WARN(eoe->slave, "Fragmenting error at %s.\n",
                    eoe->dev->name);
#endif
            eoe->state = ec_eoe_state_tx_start;
            return;
        }
    }

    // copy fragment into socket buffer
    memcpy(skb_put(eoe->rx_skb, data_size), data + 4, data_size);
    eoe->rx_skb_offset += data_size;

    if (last_fragment) {
        // update statistics
        eoe->stats.rx_packets++;
        eoe->stats.rx_bytes += eoe->rx_skb->len;
        eoe->rx_counter += eoe->rx_skb->len;

#if EOE_DEBUG_LEVEL >= 2
        EC_SLAVE_DBG(eoe->slave, 0, "EoE %s RX frame completed"
                " with %u octets.\n", eoe->dev->name, eoe->rx_skb->len);
#endif

        // pass socket buffer to network stack
        eoe->rx_skb->dev = eoe->dev;
        eoe->rx_skb->protocol = eth_type_trans(eoe->rx_skb, eoe->dev);
        eoe->rx_skb->ip_summed = CHECKSUM_UNNECESSARY;
        if (netif_rx(eoe->rx_skb)) {
            EC_SLAVE_WARN(eoe->slave, "EoE RX netif_rx failed.\n");
        }
        eoe->rx_skb = NULL;

        eoe->state = ec_eoe_state_tx_start;
    }
    else {
        eoe->rx_expected_fragment++;
#if EOE_DEBUG_LEVEL >= 2
        EC_SLAVE_DBG(eoe->slave, 0, "EoE %s RX expecting fragment %u\n",
               eoe->dev->name, eoe->rx_expected_fragment);
#endif
        eoe->state = ec_eoe_state_rx_start;
    }
}

/*****************************************************************************/

/** State: TX START.
 *
 * Starts a new transmit sequence. If no data is available, a new receive
 * sequence is started instead.
 *
 * \todo Use both devices.
 */
void ec_eoe_state_tx_start(ec_eoe_t *eoe /**< EoE handler */)
{
#if EOE_DEBUG_LEVEL >= 2
    unsigned int wakeup = 0;
#endif

    if (eoe->slave->error_flag ||
            !eoe->slave->master->devices[EC_DEVICE_MAIN].link_state) {
        eoe->rx_idle = 1;
        eoe->tx_idle = 1;
        return;
    }

    down(&eoe->tx_queue_sem);

    if (!eoe->tx_queued_frames || list_empty(&eoe->tx_queue)) {
        up(&eoe->tx_queue_sem);
        eoe->tx_idle = 1;
        // no data available.
        // start a new receive immediately.
        ec_eoe_state_rx_start(eoe);
        return;
    }

    // take the first frame out of the queue
    eoe->tx_frame = list_entry(eoe->tx_queue.next, ec_eoe_frame_t, queue);
    list_del(&eoe->tx_frame->queue);
    if (!eoe->tx_queue_active &&
        eoe->tx_queued_frames == eoe->tx_queue_size / 2) {
        netif_wake_queue(eoe->dev);
        eoe->tx_queue_active = 1;
#if EOE_DEBUG_LEVEL >= 2
        wakeup = 1;
#endif
    }

    eoe->tx_queued_frames--;
    up(&eoe->tx_queue_sem);

    eoe->tx_idle = 0;

    eoe->tx_frame_number++;
    eoe->tx_frame_number %= 16;
    eoe->tx_fragment_number = 0;
    eoe->tx_offset = 0;

    if (ec_eoe_send(eoe)) {
        dev_kfree_skb(eoe->tx_frame->skb);
        kfree(eoe->tx_frame);
        eoe->tx_frame = NULL;
        eoe->stats.tx_errors++;
        eoe->state = ec_eoe_state_rx_start;
#if EOE_DEBUG_LEVEL >= 1
        EC_SLAVE_WARN(eoe->slave, "Send error at %s.\n", eoe->dev->name);
#endif
        return;
    }

#if EOE_DEBUG_LEVEL >= 2
    if (wakeup)
        EC_SLAVE_DBG(eoe->slave, 0, "EoE %s waking up TX queue...\n",
                eoe->dev->name);
#endif

    eoe->tries = EC_EOE_TRIES;
    eoe->state = ec_eoe_state_tx_sent;
}

/*****************************************************************************/

/** State: TX SENT.
 *
 * Checks is the previous transmit datagram succeded and sends the next
 * fragment, if necessary.
 */
void ec_eoe_state_tx_sent(ec_eoe_t *eoe /**< EoE handler */)
{
    if (eoe->datagram.state != EC_DATAGRAM_RECEIVED) {
        if (eoe->tries) {
            eoe->tries--; // try again
            eoe->queue_datagram = 1;
        } else {
            eoe->stats.tx_errors++;
#if EOE_DEBUG_LEVEL >= 1
            EC_SLAVE_WARN(eoe->slave, "Failed to receive send"
                    " datagram for %s after %u tries.\n",
                    eoe->dev->name, EC_EOE_TRIES);
#endif
            eoe->state = ec_eoe_state_rx_start;
        }
        return;
    }

    if (eoe->datagram.working_counter != 1) {
        if (eoe->tries) {
            eoe->tries--; // try again
            eoe->queue_datagram = 1;
        } else {
            eoe->stats.tx_errors++;
#if EOE_DEBUG_LEVEL >= 1
            EC_SLAVE_WARN(eoe->slave, "No sending response"
                    " for %s after %u tries.\n",
                    eoe->dev->name, EC_EOE_TRIES);
#endif
            eoe->state = ec_eoe_state_rx_start;
        }
        return;
    }

    // frame completely sent
    if (eoe->tx_offset >= eoe->tx_frame->skb->len) {
        eoe->stats.tx_packets++;
        eoe->stats.tx_bytes += eoe->tx_frame->skb->len;
        eoe->tx_counter += eoe->tx_frame->skb->len;
        dev_kfree_skb(eoe->tx_frame->skb);
        kfree(eoe->tx_frame);
        eoe->tx_frame = NULL;
        eoe->state = ec_eoe_state_rx_start;
    }
    else { // send next fragment
        if (ec_eoe_send(eoe)) {
            dev_kfree_skb(eoe->tx_frame->skb);
            kfree(eoe->tx_frame);
            eoe->tx_frame = NULL;
            eoe->stats.tx_errors++;
#if EOE_DEBUG_LEVEL >= 1
            EC_SLAVE_WARN(eoe->slave, "Send error at %s.\n", eoe->dev->name);
#endif
            eoe->state = ec_eoe_state_rx_start;
        }
    }
}

/******************************************************************************
 *  NET_DEVICE functions
 *****************************************************************************/

/** Opens the virtual network device.
 *
 * \return Always zero (success).
 */
int ec_eoedev_open(struct net_device *dev /**< EoE net_device */)
{
    ec_eoe_t *eoe = *((ec_eoe_t **) netdev_priv(dev));
    ec_eoe_flush(eoe);
    eoe->opened = 1;
    eoe->rx_idle = 0;
    eoe->tx_idle = 0;
    netif_start_queue(dev);
    eoe->tx_queue_active = 1;
#if EOE_DEBUG_LEVEL >= 2
    EC_SLAVE_DBG(eoe->slave, 0, "%s opened.\n", dev->name);
#endif
    ec_slave_request_state(eoe->slave, EC_SLAVE_STATE_OP);
    return 0;
}

/*****************************************************************************/

/** Stops the virtual network device.
 *
 * \return Always zero (success).
 */
int ec_eoedev_stop(struct net_device *dev /**< EoE net_device */)
{
    ec_eoe_t *eoe = *((ec_eoe_t **) netdev_priv(dev));
    netif_stop_queue(dev);
    eoe->rx_idle = 1;
    eoe->tx_idle = 1;
    eoe->tx_queue_active = 0;
    eoe->opened = 0;
    ec_eoe_flush(eoe);
#if EOE_DEBUG_LEVEL >= 2
    EC_SLAVE_DBG(eoe->slave, 0, "%s stopped.\n", dev->name);
#endif
    ec_slave_request_state(eoe->slave, EC_SLAVE_STATE_PREOP);
    return 0;
}

/*****************************************************************************/

/** Transmits data via the virtual network device.
 *
 * \return Zero on success, non-zero on failure.
 */
int ec_eoedev_tx(struct sk_buff *skb, /**< transmit socket buffer */
                 struct net_device *dev /**< EoE net_device */
                )
{
    ec_eoe_t *eoe = *((ec_eoe_t **) netdev_priv(dev));
    ec_eoe_frame_t *frame;

#if 0
    if (skb->len > eoe->slave->configured_tx_mailbox_size - 10) {
        EC_SLAVE_WARN(eoe->slave, "EoE TX frame (%u octets)"
                " exceeds MTU. dropping.\n", skb->len);
        dev_kfree_skb(skb);
        eoe->stats.tx_dropped++;
        return 0;
    }
#endif

    if (!(frame =
          (ec_eoe_frame_t *) kmalloc(sizeof(ec_eoe_frame_t), GFP_ATOMIC))) {
        if (printk_ratelimit())
            EC_SLAVE_WARN(eoe->slave, "EoE TX: low on mem. frame dropped.\n");
        return 1;
    }

    frame->skb = skb;

    down(&eoe->tx_queue_sem);
    list_add_tail(&frame->queue, &eoe->tx_queue);
    eoe->tx_queued_frames++;
    if (eoe->tx_queued_frames == eoe->tx_queue_size) {
        netif_stop_queue(dev);
        eoe->tx_queue_active = 0;
    }
    up(&eoe->tx_queue_sem);

#if EOE_DEBUG_LEVEL >= 2
    EC_SLAVE_DBG(eoe->slave, 0, "EoE %s TX queued frame"
            " with %u octets (%u frames queued).\n",
            eoe->dev->name, skb->len, eoe->tx_queued_frames);
    if (!eoe->tx_queue_active)
        EC_SLAVE_WARN(eoe->slave, "EoE TX queue is now full.\n");
#endif

    return 0;
}

/*****************************************************************************/

/** Gets statistics about the virtual network device.
 *
 * \return Statistics.
 */
struct net_device_stats *ec_eoedev_stats(
        struct net_device *dev /**< EoE net_device */
        )
{
    ec_eoe_t *eoe = *((ec_eoe_t **) netdev_priv(dev));
    return &eoe->stats;
}

/*****************************************************************************/