--- a/master/ethernet.c Mon Apr 24 10:47:03 2006 +0000
+++ b/master/ethernet.c Mon May 29 09:08:56 2006 +0000
@@ -8,7 +8,8 @@
*
* The IgH EtherCAT Master is free software; you can redistribute it
* and/or modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; version 2 of the License.
+ * as published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
*
* The IgH EtherCAT Master is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -19,6 +20,15 @@
* 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 right to use EtherCAT Technology is granted and comes free of
+ * charge under condition of compatibility of product made by
+ * Licensee. People intending to distribute/sell products based on the
+ * code, have to sign an agreement to guarantee that products using
+ * software based on IgH EtherCAT master stay compatible with the actual
+ * EtherCAT specification (which are released themselves as an open
+ * standard) as the (only) precondition to have the right to use EtherCAT
+ * Technology, IP and trade marks.
+ *
*****************************************************************************/
/**
@@ -28,6 +38,8 @@
/*****************************************************************************/
+#include <linux/etherdevice.h>
+
#include "../include/ecrt.h"
#include "globals.h"
#include "master.h"
@@ -35,26 +47,210 @@
#include "mailbox.h"
#include "ethernet.h"
+#define EOE_DEBUG_LEVEL 0
+
+/*****************************************************************************/
+
+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 *);
+
/*****************************************************************************/
/**
EoE constructor.
-*/
-
-void ec_eoe_init(ec_eoe_t *eoe, ec_slave_t *slave)
-{
- eoe->slave = slave;
- eoe->rx_state = EC_EOE_IDLE;
+ Initializes the EoE handler, creates a net_device and registeres it.
+*/
+
+int ec_eoe_init(ec_eoe_t *eoe /**< EoE handler */)
+{
+ ec_eoe_t **priv;
+ int result, i;
+
+ eoe->slave = NULL;
+ 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_queued_frames = 0;
+ eoe->tx_queue_lock = SPIN_LOCK_UNLOCKED;
+ eoe->tx_frame_number = 0xFF;
+ memset(&eoe->stats, 0, sizeof(struct net_device_stats));
+
+ if (!(eoe->dev =
+ alloc_netdev(sizeof(ec_eoe_t *), "eoe%d", ether_setup))) {
+ EC_ERR("Unable to allocate net_device for EoE handler!\n");
+ goto out_return;
+ }
+
+ // initialize net_device
+ 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;
+
+ 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->sii_rx_mailbox_size - ETH_HLEN - 10;
+#endif
+
+ // connect the net_device to the kernel
+ if ((result = register_netdev(eoe->dev))) {
+ EC_ERR("Unable to register net_device: error %i\n", result);
+ 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 -1;
}
/*****************************************************************************/
/**
EoE destructor.
-*/
-
-void ec_eoe_clear(ec_eoe_t *eoe)
-{
+ Unregisteres the net_device and frees allocated memory.
+*/
+
+void ec_eoe_clear(ec_eoe_t *eoe /**< EoE handler */)
+{
+ if (eoe->dev) {
+ unregister_netdev(eoe->dev);
+ free_netdev(eoe->dev);
+ }
+
+ // 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);
+}
+
+/*****************************************************************************/
+
+/**
+ Empties the transmit queue.
+*/
+
+void ec_eoe_flush(ec_eoe_t *eoe /**< EoE handler */)
+{
+ ec_eoe_frame_t *frame, *next;
+
+ spin_lock_bh(&eoe->tx_queue_lock);
+
+ 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;
+
+ spin_unlock_bh(&eoe->tx_queue_lock);
+}
+
+/*****************************************************************************/
+
+/**
+ Sends a frame or the next fragment.
+*/
+
+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 > 1
+ unsigned int i;
+#endif
+
+ remaining_size = eoe->tx_frame->skb->len - eoe->tx_offset;
+
+ if (remaining_size <= eoe->slave->sii_tx_mailbox_size - 10) {
+ current_size = remaining_size;
+ last_fragment = 1;
+ }
+ else {
+ current_size = ((eoe->slave->sii_tx_mailbox_size - 10) / 32) * 32;
+ last_fragment = 0;
+ }
+
+ if (eoe->tx_fragment_number) {
+ complete_offset = eoe->tx_offset / 32;
+ }
+ else {
+ complete_offset = remaining_size / 32 + 1;
+ }
+
+#if EOE_DEBUG_LEVEL > 0
+ EC_DBG("EoE TX sending %sfragment %i with %i octets (%i)."
+ " %i frames queued.\n", last_fragment ? "last " : "",
+ eoe->tx_fragment_number, current_size, complete_offset,
+ eoe->tx_queued_frames);
+#endif
+
+#if EOE_DEBUG_LEVEL > 1
+ EC_DBG("");
+ for (i = 0; i < current_size; i++) {
+ printk("%02X ", frame->skb->data[eoe->tx_offset + i]);
+ if ((i + 1) % 16 == 0) {
+ printk("\n");
+ EC_DBG("");
+ }
+ }
+ printk("\n");
+#endif
+
+ if (!(data = ec_slave_mbox_prepare_send(eoe->slave, 0x02,
+ current_size + 4)))
+ return -1;
+
+ 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);
+ ec_master_queue_command(eoe->slave->master, &eoe->slave->mbox_command);
+
+ eoe->tx_offset += current_size;
+ eoe->tx_fragment_number++;
+ return 0;
}
/*****************************************************************************/
@@ -63,70 +259,137 @@
Runs the EoE state machine.
*/
-void ec_eoe_run(ec_eoe_t *eoe)
-{
- uint8_t *data;
- ec_master_t *master;
- size_t rec_size;
-#if 0
- unsigned int i;
- uint8_t fragment_number;
- uint8_t complete_size;
- uint8_t frame_number;
- uint8_t last_fragment;
-#endif
-
- master = eoe->slave->master;
-
- if (eoe->rx_state == EC_EOE_IDLE) {
- ec_slave_mbox_prepare_check(eoe->slave);
- ec_master_queue_command(master, &eoe->slave->mbox_command);
- eoe->rx_state = EC_EOE_CHECKING;
- return;
- }
-
- if (eoe->rx_state == EC_EOE_CHECKING) {
- if (eoe->slave->mbox_command.state != EC_CMD_RECEIVED) {
- master->stats.eoe_errors++;
- eoe->rx_state = EC_EOE_IDLE;
- return;
- }
- if (!ec_slave_mbox_check(eoe->slave)) {
- eoe->rx_state = EC_EOE_IDLE;
- return;
- }
- ec_slave_mbox_prepare_fetch(eoe->slave);
- ec_master_queue_command(master, &eoe->slave->mbox_command);
- eoe->rx_state = EC_EOE_FETCHING;
- return;
- }
-
- if (eoe->rx_state == EC_EOE_FETCHING) {
- if (eoe->slave->mbox_command.state != EC_CMD_RECEIVED) {
- master->stats.eoe_errors++;
- eoe->rx_state = EC_EOE_IDLE;
- return;
- }
- if (!(data = ec_slave_mbox_fetch(eoe->slave, 0x02, &rec_size))) {
- master->stats.eoe_errors++;
- eoe->rx_state = EC_EOE_IDLE;
- return;
- }
-
-#if 0
+void ec_eoe_run(ec_eoe_t *eoe /**< EoE handler */)
+{
+ if (!eoe->opened) return;
+
+ // call state function
+ eoe->state(eoe);
+}
+
+/*****************************************************************************/
+
+/**
+ Returns the state of the device.
+ \return 1 if the device is "up", 0 if it is "down"
+*/
+
+unsigned int ec_eoe_active(const ec_eoe_t *eoe /**< EoE handler */)
+{
+ return eoe->slave && eoe->opened;
+}
+
+/*****************************************************************************/
+
+/**
+ Prints EoE handler information.
+*/
+
+void ec_eoe_print(const ec_eoe_t *eoe /**< EoE handler */)
+{
+ EC_INFO(" EoE handler %s\n", eoe->dev->name);
+ EC_INFO(" State: %s\n", eoe->opened ? "opened" : "closed");
+ if (eoe->slave)
+ EC_INFO(" Coupled to slave %i.\n", eoe->slave->ring_position);
+ else
+ EC_INFO(" Not coupled.\n");
+}
+
+/******************************************************************************
+ * STATE PROCESSING FUNCTIONS
+ *****************************************************************************/
+
+/**
+ State: RX_START.
+ Starts a new receiving sequence by queueing a command that checks the
+ slave's mailbox for a new EoE command.
+*/
+
+void ec_eoe_state_rx_start(ec_eoe_t *eoe /**< EoE handler */)
+{
+ if (!eoe->slave->online || !eoe->slave->master->device->link_state)
+ return;
+
+ ec_slave_mbox_prepare_check(eoe->slave);
+ ec_master_queue_command(eoe->slave->master, &eoe->slave->mbox_command);
+ eoe->state = ec_eoe_state_rx_check;
+}
+
+/*****************************************************************************/
+
+/**
+ State: RX_CHECK.
+ Processes the checking command sent in RX_START and issues a receive
+ command, if new data is available.
+*/
+
+void ec_eoe_state_rx_check(ec_eoe_t *eoe /**< EoE handler */)
+{
+ if (eoe->slave->mbox_command.state != EC_CMD_RECEIVED) {
+ eoe->stats.rx_errors++;
+ eoe->state = ec_eoe_state_tx_start;
+ return;
+ }
+
+ if (!ec_slave_mbox_check(eoe->slave)) {
+ eoe->state = ec_eoe_state_tx_start;
+ return;
+ }
+
+ ec_slave_mbox_prepare_fetch(eoe->slave);
+ ec_master_queue_command(eoe->slave->master, &eoe->slave->mbox_command);
+ eoe->state = ec_eoe_state_rx_fetch;
+}
+
+/*****************************************************************************/
+
+/**
+ State: RX_FETCH.
+ Checks if the requested data of RX_CHECK was received and processes the
+ EoE command.
+*/
+
+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;
+ uint8_t frame_number, fragment_offset, fragment_number;
+ off_t offset;
+
+ if (eoe->slave->mbox_command.state != EC_CMD_RECEIVED) {
+ eoe->stats.rx_errors++;
+ eoe->state = ec_eoe_state_tx_start;
+ return;
+ }
+
+ if (!(data = ec_slave_mbox_fetch(eoe->slave, 0x02, &rec_size))) {
+ eoe->stats.rx_errors++;
+ eoe->state = ec_eoe_state_tx_start;
+ return;
+ }
+
+ frame_type = EC_READ_U16(data) & 0x000F;
+
+ if (frame_type == 0x00) { // EoE Fragment Request
+ last_fragment = (EC_READ_U16(data) >> 8) & 0x0001;
+ time_appended = (EC_READ_U16(data) >> 9) & 0x0001;
fragment_number = EC_READ_U16(data + 2) & 0x003F;
- complete_size = (EC_READ_U16(data + 2) >> 6) & 0x003F;
- frame_number = (EC_READ_U16(data + 2) >> 12) & 0x0003;
- last_fragment = (EC_READ_U16(data + 2) >> 15) & 0x0001;
-
- EC_DBG("EOE %s received, fragment: %i, complete size: %i (0x%02X),"
- " frame %i%s\n",
- fragment_number ? "fragment" : "initiate", fragment_number,
- (complete_size - 31) / 32, complete_size, frame_number,
- last_fragment ? ", last fragment" : "");
+ fragment_offset = (EC_READ_U16(data + 2) >> 6) & 0x003F;
+ frame_number = (EC_READ_U16(data + 2) >> 12) & 0x000F;
+
+#if EOE_DEBUG_LEVEL > 0
+ EC_DBG("EoE RX fragment %i, offset %i, frame %i%s%s,"
+ " %i octets\n", fragment_number, fragment_offset,
+ frame_number,
+ last_fragment ? ", last fragment" : "",
+ time_appended ? ", + timestamp" : "",
+ time_appended ? rec_size - 8 : rec_size - 4);
+#endif
+
+#if EOE_DEBUG_LEVEL > 1
EC_DBG("");
- for (i = 0; i < rec_size - 2; i++) {
- printk("%02X ", data[i + 2]);
+ for (i = 0; i < rec_size - 4; i++) {
+ printk("%02X ", data[i + 4]);
if ((i + 1) % 16 == 0) {
printk("\n");
EC_DBG("");
@@ -135,21 +398,303 @@
printk("\n");
#endif
- eoe->rx_state = EC_EOE_IDLE;
- return;
- }
-}
-
-/*****************************************************************************/
-
-/**
- Prints EoE object information.
-*/
-
-void ec_eoe_print(const ec_eoe_t *eoe)
-{
- EC_INFO(" EoE slave %i\n", eoe->slave->ring_position);
- EC_INFO(" RX State %i\n", eoe->rx_state);
-}
-
-/*****************************************************************************/
+ data_size = time_appended ? rec_size - 8 : rec_size - 4;
+
+ if (!fragment_number) {
+ if (eoe->rx_skb) {
+ EC_WARN("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_WARN("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) {
+ eoe->stats.rx_errors++;
+ eoe->state = ec_eoe_state_tx_start;
+ dev_kfree_skb(eoe->rx_skb);
+ eoe->rx_skb = NULL;
+ 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;
+
+#if EOE_DEBUG_LEVEL > 0
+ EC_DBG("EoE RX frame completed with %u octets.\n",
+ 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_WARN("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 > 0
+ EC_DBG("EoE RX expecting fragment %i\n",
+ eoe->rx_expected_fragment);
+#endif
+ eoe->state = ec_eoe_state_rx_start;
+ }
+ }
+ else {
+#if EOE_DEBUG_LEVEL > 0
+ EC_DBG("other frame received.\n");
+#endif
+ eoe->stats.rx_dropped++;
+ eoe->state = ec_eoe_state_tx_start;
+ }
+}
+
+/*****************************************************************************/
+
+/**
+ State: TX START.
+ Starts a new transmit sequence. If no data is available, a new receive
+ sequence is started instead.
+*/
+
+void ec_eoe_state_tx_start(ec_eoe_t *eoe /**< EoE handler */)
+{
+#if EOE_DEBUG_LEVEL > 0
+ unsigned int wakeup;
+#endif
+
+ if (!eoe->slave->online || !eoe->slave->master->device->link_state)
+ return;
+
+ spin_lock_bh(&eoe->tx_queue_lock);
+
+ if (!eoe->tx_queued_frames || list_empty(&eoe->tx_queue)) {
+ spin_unlock_bh(&eoe->tx_queue_lock);
+ // 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 == EC_EOE_TX_QUEUE_SIZE / 2) {
+ netif_wake_queue(eoe->dev);
+ eoe->tx_queue_active = 1;
+#if EOE_DEBUG_LEVEL > 0
+ wakeup = 1;
+#endif
+ }
+
+ eoe->tx_queued_frames--;
+ spin_unlock_bh(&eoe->tx_queue_lock);
+
+ 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;
+ return;
+ }
+
+#if EOE_DEBUG_LEVEL > 0
+ if (wakeup) EC_DBG("waking up TX queue...\n");
+#endif
+
+ eoe->state = ec_eoe_state_tx_sent;
+}
+
+/*****************************************************************************/
+
+/**
+ State: TX SENT.
+ Checks is the previous transmit command succeded and sends the next
+ fragment, if necessary.
+*/
+
+void ec_eoe_state_tx_sent(ec_eoe_t *eoe /**< EoE handler */)
+{
+ if (eoe->slave->mbox_command.state != EC_CMD_RECEIVED) {
+ eoe->stats.tx_errors++;
+ eoe->state = ec_eoe_state_rx_start;
+ return;
+ }
+
+ if (eoe->slave->mbox_command.working_counter != 1) {
+ eoe->stats.tx_errors++;
+ 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;
+ 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++;
+ eoe->state = ec_eoe_state_rx_start;
+ }
+ }
+}
+
+/******************************************************************************
+ * NET_DEVICE functions
+ *****************************************************************************/
+
+/**
+ Opens the virtual network device.
+*/
+
+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;
+ netif_start_queue(dev);
+ eoe->tx_queue_active = 1;
+ EC_INFO("%s opened.\n", dev->name);
+ if (!eoe->slave)
+ EC_WARN("device %s is not coupled to any EoE slave!\n", dev->name);
+ else {
+ eoe->slave->requested_state = EC_SLAVE_STATE_OP;
+ eoe->slave->state_error = 0;
+ }
+ return 0;
+}
+
+/*****************************************************************************/
+
+/**
+ Stops the virtual network device.
+*/
+
+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->tx_queue_active = 0;
+ eoe->opened = 0;
+ ec_eoe_flush(eoe);
+ EC_INFO("%s stopped.\n", dev->name);
+ if (!eoe->slave)
+ EC_WARN("device %s is not coupled to any EoE slave!\n", dev->name);
+ else {
+ eoe->slave->requested_state = EC_SLAVE_STATE_INIT;
+ eoe->slave->state_error = 0;
+ }
+ return 0;
+}
+
+/*****************************************************************************/
+
+/**
+ Transmits data via the virtual network device.
+*/
+
+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->sii_tx_mailbox_size - 10) {
+ EC_WARN("EoE TX frame (%i 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_WARN("EoE TX: low on mem. frame dropped.\n");
+ return 1;
+ }
+
+ frame->skb = skb;
+
+ spin_lock_bh(&eoe->tx_queue_lock);
+ list_add_tail(&frame->queue, &eoe->tx_queue);
+ eoe->tx_queued_frames++;
+ if (eoe->tx_queued_frames == EC_EOE_TX_QUEUE_SIZE) {
+ netif_stop_queue(dev);
+ eoe->tx_queue_active = 0;
+ }
+ spin_unlock_bh(&eoe->tx_queue_lock);
+
+#if EOE_DEBUG_LEVEL > 0
+ EC_DBG("EoE TX queued frame with %i octets (%i frames queued).\n",
+ skb->len, eoe->tx_queued_frames);
+ if (!eoe->tx_queue_active)
+ EC_WARN("EoE TX queue is now full.\n");
+#endif
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+/**
+ Gets statistics about the virtual network device.
+*/
+
+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;
+}
+
+/*****************************************************************************/