master/device.c
branchstable-1.3
changeset 1744 7bc131b92039
parent 1739 5fcbd29151d2
child 1746 72e7507b3f1b
--- a/master/device.c	Fri Aug 10 15:08:44 2007 +0000
+++ b/master/device.c	Fri Aug 10 15:27:08 2007 +0000
@@ -42,11 +42,22 @@
 #include <linux/skbuff.h>
 #include <linux/if_ether.h>
 #include <linux/netdevice.h>
-#include <linux/delay.h>
 
 #include "device.h"
 #include "master.h"
 
+#ifdef EC_DEBUG_RING
+#define timersub(a, b, result) \
+    do { \
+        (result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \
+        (result)->tv_usec = (a)->tv_usec - (b)->tv_usec; \
+        if ((result)->tv_usec < 0) { \
+            --(result)->tv_sec; \
+            (result)->tv_usec += 1000000; \
+        } \
+    } while (0)
+#endif
+
 /*****************************************************************************/
 
 /**
@@ -55,73 +66,132 @@
 */
 
 int ec_device_init(ec_device_t *device, /**< EtherCAT device */
-                   ec_master_t *master, /**< master owning the device */
-                   struct net_device *net_dev, /**< net_device structure */
-                   ec_pollfunc_t poll, /**< pointer to device's poll function */
-                   struct module *module /**< the device's module */
-                   )
-{
+        ec_master_t *master /**< master owning the device */
+        )
+{
+    unsigned int i;
     struct ethhdr *eth;
+#ifdef EC_DEBUG_IF
+    char ifname[10];
+    char mb = 'x';
+#endif
+#ifdef EC_DEBUG_RING
+    device->debug_frame_index = 0;
+    device->debug_frame_count = 0;
+#endif
 
     device->master = master;
+    device->tx_ring_index = 0;
+
+#ifdef EC_DEBUG_IF
+    if (device == &master->main_device)
+        mb = 'm';
+    else if (device == &master->backup_device)
+        mb = 'b';
+
+    sprintf(ifname, "ecdbg%c%u", mb, master->index);
+
+    if (ec_debug_init(&device->dbg, ifname)) {
+        EC_ERR("Failed to init debug device!\n");
+        goto out_return;
+    }
+#endif
+
+    for (i = 0; i < EC_TX_RING_SIZE; i++)
+        device->tx_skb[i] = NULL;
+
+    for (i = 0; i < EC_TX_RING_SIZE; i++) {
+        if (!(device->tx_skb[i] = dev_alloc_skb(ETH_FRAME_LEN))) {
+            EC_ERR("Error allocating device socket buffer!\n");
+            goto out_tx_ring;
+        }
+
+        // add Ethernet-II-header
+        skb_reserve(device->tx_skb[i], ETH_HLEN);
+        eth = (struct ethhdr *) skb_push(device->tx_skb[i], ETH_HLEN);
+        eth->h_proto = htons(0x88A4);
+        memset(eth->h_dest, 0xFF, ETH_ALEN);
+    }
+
+    ec_device_detach(device); // resets remaining fields
+    return 0;
+
+out_tx_ring:
+    for (i = 0; i < EC_TX_RING_SIZE; i++)
+        if (device->tx_skb[i])
+            dev_kfree_skb(device->tx_skb[i]);
+#ifdef EC_DEBUG_IF
+    ec_debug_clear(&device->dbg);
+out_return:
+#endif
+    return -1;
+}
+
+/*****************************************************************************/
+
+/**
+   EtherCAT device destuctor.
+*/
+
+void ec_device_clear(ec_device_t *device /**< EtherCAT device */)
+{
+    unsigned int i;
+
+    if (device->open) ec_device_close(device);
+    for (i = 0; i < EC_TX_RING_SIZE; i++)
+        dev_kfree_skb(device->tx_skb[i]);
+#ifdef EC_DEBUG_IF
+    ec_debug_clear(&device->dbg);
+#endif
+}
+
+/*****************************************************************************/
+
+/**
+   Associate with net_device.
+*/
+
+void ec_device_attach(ec_device_t *device, /**< EtherCAT device */
+        struct net_device *net_dev, /**< net_device structure */
+        ec_pollfunc_t poll, /**< pointer to device's poll function */
+        struct module *module /**< the device's module */
+        )
+{
+    unsigned int i;
+    struct ethhdr *eth;
+
+    ec_device_detach(device); // resets fields
+
     device->dev = net_dev;
     device->poll = poll;
     device->module = module;
 
+    for (i = 0; i < EC_TX_RING_SIZE; i++) {
+        device->tx_skb[i]->dev = net_dev;
+        eth = (struct ethhdr *) (device->tx_skb[i]->data + ETH_HLEN);
+        memcpy(eth->h_source, net_dev->dev_addr, ETH_ALEN);
+    }
+}
+
+/*****************************************************************************/
+
+/**
+   Disconnect from net_device.
+*/
+
+void ec_device_detach(ec_device_t *device /**< EtherCAT device */)
+{
+    unsigned int i;
+
+    device->dev = NULL;
+    device->poll = NULL;
+    device->module = NULL;
     device->open = 0;
     device->link_state = 0; // down
-
     device->tx_count = 0;
     device->rx_count = 0;
-
-#ifdef EC_DBG_IF
-    if (ec_debug_init(&device->dbg)) {
-        EC_ERR("Failed to init debug device!\n");
-        goto out_return;
-    }
-#endif
-
-    if (!(device->tx_skb = dev_alloc_skb(ETH_FRAME_LEN))) {
-        EC_ERR("Error allocating device socket buffer!\n");
-#ifdef EC_DBG_IF
-        goto out_debug;
-#else
-        goto out_return;
-#endif
-    }
-
-    device->tx_skb->dev = net_dev;
-
-    // add Ethernet-II-header
-    skb_reserve(device->tx_skb, ETH_HLEN);
-    eth = (struct ethhdr *) skb_push(device->tx_skb, ETH_HLEN);
-    eth->h_proto = htons(0x88A4);
-    memcpy(eth->h_source, net_dev->dev_addr, net_dev->addr_len);
-    memset(eth->h_dest, 0xFF, net_dev->addr_len);
-
-    return 0;
-
-#ifdef EC_DBG_IF
- out_debug:
-    ec_debug_clear(&device->dbg);
-#endif
- out_return:
-    return -1;
-}
-
-/*****************************************************************************/
-
-/**
-   EtherCAT device destuctor.
-*/
-
-void ec_device_clear(ec_device_t *device /**< EtherCAT device */)
-{
-    if (device->open) ec_device_close(device);
-    if (device->tx_skb) dev_kfree_skb(device->tx_skb);
-#ifdef EC_DBG_IF
-    ec_debug_clear(&device->dbg);
-#endif
+    for (i = 0; i < EC_TX_RING_SIZE; i++)
+        device->tx_skb[i]->dev = NULL;
 }
 
 /*****************************************************************************/
@@ -133,8 +203,6 @@
 
 int ec_device_open(ec_device_t *device /**< EtherCAT device */)
 {
-    unsigned int i;
-
     if (!device->dev) {
         EC_ERR("No net_device to open!\n");
         return -1;
@@ -145,9 +213,6 @@
         return 0;
     }
 
-    // device could have received frames before
-    for (i = 0; i < 4; i++) ec_device_poll(device);
-
     device->link_state = 0;
     device->tx_count = 0;
     device->rx_count = 0;
@@ -190,7 +255,12 @@
 
 uint8_t *ec_device_tx_data(ec_device_t *device /**< EtherCAT device */)
 {
-    return device->tx_skb->data + ETH_HLEN;
+    /* cycle through socket buffers, because otherwise there is a race
+     * condition, if multiple frames are sent and the DMA is not scheduled in
+     * between. */
+    device->tx_ring_index++;
+    device->tx_ring_index %= EC_TX_RING_SIZE;
+    return device->tx_skb[device->tx_ring_index]->data + ETH_HLEN;
 }
 
 /*****************************************************************************/
@@ -205,28 +275,107 @@
                     size_t size /**< number of bytes to send */
                     )
 {
+    struct sk_buff *skb = device->tx_skb[device->tx_ring_index];
+
     if (unlikely(!device->link_state)) // Link down
         return;
 
     // set the right length for the data
-    device->tx_skb->len = ETH_HLEN + size;
+    skb->len = ETH_HLEN + size;
 
     if (unlikely(device->master->debug_level > 1)) {
         EC_DBG("sending frame:\n");
-        ec_print_data(device->tx_skb->data + ETH_HLEN, size);
-    }
-
-#ifdef EC_DBG_IF
-    ec_debug_send(&device->dbg, device->tx_skb->data, ETH_HLEN + size);
+        ec_print_data(skb->data + ETH_HLEN, size);
+    }
+
+#ifdef EC_DEBUG_IF
+    ec_debug_send(&device->dbg, skb->data, ETH_HLEN + size);
+#endif
+#ifdef EC_DEBUG_RING
+    ec_device_debug_ring_append(
+            device, TX, skb->data + ETH_HLEN, size);
 #endif
 
     // start sending
-    device->dev->hard_start_xmit(device->tx_skb, device->dev);
+    device->dev->hard_start_xmit(skb, device->dev);
     device->tx_count++;
 }
 
 /*****************************************************************************/
 
+#ifdef EC_DEBUG_RING
+/**
+ * Appends frame data to the debug ring.
+ */
+
+void ec_device_debug_ring_append(
+        ec_device_t *device, /**< EtherCAT device */
+        ec_debug_frame_dir_t dir, /**< direction */
+        const void *data, /**< frame data */
+        size_t size /**< data size */
+        )
+{
+    ec_debug_frame_t *df = &device->debug_frames[device->debug_frame_index];
+
+    df->dir = dir;
+    if (dir == TX)
+        do_gettimeofday(&df->t);
+    else
+        df->t = device->timeval_poll;
+    memcpy(df->data, data, size);
+    df->data_size = size;
+
+    device->debug_frame_index++;
+    device->debug_frame_index %= EC_DEBUG_RING_SIZE;
+    if (unlikely(device->debug_frame_count < EC_DEBUG_RING_SIZE))
+        device->debug_frame_count++;
+}
+
+/*****************************************************************************/
+
+/**
+ * Outputs the debug ring.
+ */
+
+void ec_device_debug_ring_print(
+        const ec_device_t *device /**< EtherCAT device */
+        )
+{
+    int i;
+    unsigned int ring_index;
+    const ec_debug_frame_t *df;
+    struct timeval t0, diff;
+
+    // calculate index of the newest frame in the ring to get its time
+    ring_index = (device->debug_frame_index + EC_DEBUG_RING_SIZE - 1)
+        % EC_DEBUG_RING_SIZE;
+    t0 = device->debug_frames[ring_index].t;
+
+    EC_DBG("Debug ring %i:\n", ring_index);
+
+    // calculate index of the oldest frame in the ring
+    ring_index = (device->debug_frame_index + EC_DEBUG_RING_SIZE
+            - device->debug_frame_count) % EC_DEBUG_RING_SIZE;
+
+    for (i = 0; i < device->debug_frame_count; i++) {
+        df = &device->debug_frames[ring_index];
+        timersub(&t0, &df->t, &diff);
+
+        EC_DBG("Frame %i, dt=%u.%06u s, %s:\n",
+                i + 1 - device->debug_frame_count,
+                (unsigned int) diff.tv_sec,
+                (unsigned int) diff.tv_usec,
+                (df->dir == TX) ? "TX" : "RX");
+        ec_print_data(df->data, df->data_size);
+
+        ring_index++;
+        ring_index %= EC_DEBUG_RING_SIZE;
+    }
+}
+#endif
+
+/*****************************************************************************/
+
 /**
    Calls the poll function of the assigned net_device.
    The master itself works without using interrupts. Therefore the processing
@@ -238,6 +387,9 @@
 {
     device->cycles_poll = get_cycles();
     device->jiffies_poll = jiffies;
+#ifdef EC_DEBUG_RING
+    do_gettimeofday(&device->timeval_poll);
+#endif
     device->poll(device->dev);
 }
 
@@ -257,21 +409,23 @@
                    size_t size /**< number of bytes received */
                    )
 {
+    const void *ec_data = data + ETH_HLEN;
+    size_t ec_size = size - ETH_HLEN;
     device->rx_count++;
 
     if (unlikely(device->master->debug_level > 1)) {
         EC_DBG("Received frame:\n");
-        ec_print_data_diff(device->tx_skb->data + ETH_HLEN,
-                           data + ETH_HLEN, size - ETH_HLEN);
-    }
-
-#ifdef EC_DBG_IF
+        ec_print_data(ec_data, ec_size);
+    }
+
+#ifdef EC_DEBUG_IF
     ec_debug_send(&device->dbg, data, size);
 #endif
-
-    ec_master_receive_datagrams(device->master,
-                                data + ETH_HLEN,
-                                size - ETH_HLEN);
+#ifdef EC_DEBUG_RING
+    ec_device_debug_ring_append(device, RX, ec_data, ec_size);
+#endif
+
+    ec_master_receive_datagrams(device->master, ec_data, ec_size);
 }
 
 /*****************************************************************************/
@@ -283,9 +437,9 @@
    \ingroup DeviceInterface
 */
 
-void ecdev_link_state(ec_device_t *device, /**< EtherCAT device */
-                      uint8_t state /**< new link state */
-                      )
+void ecdev_set_link(ec_device_t *device, /**< EtherCAT device */
+        uint8_t state /**< new link state */
+        )
 {
     if (unlikely(!device)) {
         EC_WARN("ecdev_link_state: no device!\n");
@@ -300,10 +454,28 @@
 
 /*****************************************************************************/
 
+/**
+   Reads the link state.
+   \ingroup DeviceInterface
+*/
+
+uint8_t ecdev_get_link(ec_device_t *device /**< EtherCAT device */)
+{
+    if (unlikely(!device)) {
+        EC_WARN("ecdev_link_state: no device!\n");
+        return 0;
+    }
+
+    return device->link_state;
+}
+
+/*****************************************************************************/
+
 /** \cond */
 
 EXPORT_SYMBOL(ecdev_receive);
-EXPORT_SYMBOL(ecdev_link_state);
+EXPORT_SYMBOL(ecdev_get_link);
+EXPORT_SYMBOL(ecdev_set_link);
 
 /** \endcond */