merge
authorFlorian Pose <fp@igh-essen.com>
Fri, 22 Jan 2016 13:09:43 +0100
changeset 2635 42b62867574d
parent 2634 f859d567f94e (current diff)
parent 2630 a380cce7d6f0 (diff)
child 2641 535465bf176d
merge
lib/slave_config.c
master/domain.c
master/domain.h
master/fmmu_config.c
master/fmmu_config.h
master/fsm_slave_config.c
master/globals.h
master/ioctl.c
master/slave_config.c
master/slave_config.h
--- a/Makefile.am	Fri Jan 22 10:11:58 2016 +0100
+++ b/Makefile.am	Fri Jan 22 13:09:43 2016 +0100
@@ -119,7 +119,11 @@
 		configure \
 		configure.in \
 		libtool \
-		stamp-h1
+		stamp-h1 \
+		ethercat.spec \
+		script/init.d/ethercat \
+		m4/*.m4 \
+		`find -path ./documentation -prune -o "(" -name Makefile -o -name Makefile.in -o -name Kbuild -o -name .deps -o -name Module.symvers -o -name .tmp_versions ")" -print`
 
 doc:
 	doxygen Doxyfile
--- a/configure.ac	Fri Jan 22 10:11:58 2016 +0100
+++ b/configure.ac	Fri Jan 22 13:09:43 2016 +0100
@@ -820,6 +820,35 @@
 fi
 
 #------------------------------------------------------------------------------
+# Force the reference clock to OP even if unconfigured
+#------------------------------------------------------------------------------
+
+AC_MSG_CHECKING([whether to force the reference clock to OP])
+
+AC_ARG_ENABLE([refclkop],
+    AS_HELP_STRING([--enable-refclkop],
+                   [Force reference clock to OP (default: no)]),
+    [
+        case "${enableval}" in
+            yes) refclkop=1
+                ;;
+            no) refclkop=0
+                ;;
+            *) AC_MSG_ERROR([Invalid value for --enable-refclkop])
+                ;;
+        esac
+    ],
+    [refclkop=0]
+)
+
+if test "x${refclkop}" = "x1"; then
+    AC_DEFINE([EC_REFCLKOP], [1], [Force refclk to OP])
+    AC_MSG_RESULT([yes])
+else
+    AC_MSG_RESULT([no])
+fi
+
+#------------------------------------------------------------------------------
 # Command-line tool
 #-----------------------------------------------------------------------------
 
--- a/include/ecrt.h	Fri Jan 22 10:11:58 2016 +0100
+++ b/include/ecrt.h	Fri Jan 22 13:09:43 2016 +0100
@@ -815,7 +815,7 @@
         uint16_t slave_position, /**< Slave position. */
         uint16_t index, /**< Index of the SDO. */
         uint8_t subindex, /**< Subindex of the SDO. */
-        uint8_t *data, /**< Data buffer to download. */
+        const uint8_t *data, /**< Data buffer to download. */
         size_t data_size, /**< Size of the data buffer. */
         uint32_t *abort_code /**< Abort code of the SDO download. */
         );
@@ -834,7 +834,7 @@
         ec_master_t *master, /**< EtherCAT master. */
         uint16_t slave_position, /**< Slave position. */
         uint16_t index, /**< Index of the SDO. */
-        uint8_t *data, /**< Data buffer to download. */
+        const uint8_t *data, /**< Data buffer to download. */
         size_t data_size, /**< Size of the data buffer. */
         uint32_t *abort_code /**< Abort code of the SDO download. */
         );
@@ -956,8 +956,10 @@
  *
  * Has to be called cyclically by the application after ecrt_master_activate()
  * has returned.
- */
-void ecrt_master_send(
+ *
+ * Returns the number of bytes sent.
+ */
+size_t ecrt_master_send(
         ec_master_t *master /**< EtherCAT master. */
         );
 
@@ -1145,6 +1147,17 @@
                                      */
         );
 
+/** Configure whether a slave allows overlapping PDOs.
+ *
+ * Overlapping PDOs allows inputs to use the same space as outputs on the frame.
+ * This reduces the frame length.
+ */
+void ecrt_slave_config_overlapping_pdos(
+        ec_slave_config_t *sc, /**< Slave configuration. */
+        uint8_t allow_overlapping_pdos /**< Allow overlapping PDOs */
+        );
+
+
 /** Add a PDO to a sync manager's PDO assignment.
  *
  * This method has to be called in non-realtime context before
--- a/lib/master.c	Fri Jan 22 10:11:58 2016 +0100
+++ b/lib/master.c	Fri Jan 22 13:09:43 2016 +0100
@@ -60,6 +60,11 @@
     ec_domain_t *d, *next_d;
     ec_slave_config_t *c, *next_c;
 
+    if (master->process_data)  {
+        munmap(master->process_data, master->process_data_size);
+        master->process_data = NULL;
+    }
+
     d = master->first_domain;
     while (d) {
         next_d = d->next;
@@ -81,10 +86,6 @@
 
 void ec_master_clear(ec_master_t *master)
 {
-    if (master->process_data)  {
-        munmap(master->process_data, master->process_data_size);
-    }
-
     ec_master_clear_config(master);
 
     if (master->fd != -1) {
@@ -389,7 +390,7 @@
 /****************************************************************************/
 
 int ecrt_master_sdo_download(ec_master_t *master, uint16_t slave_position,
-        uint16_t index, uint8_t subindex, uint8_t *data,
+        uint16_t index, uint8_t subindex, const uint8_t *data,
         size_t data_size, uint32_t *abort_code)
 {
     ec_ioctl_slave_sdo_download_t download;
@@ -418,7 +419,7 @@
 /****************************************************************************/
 
 int ecrt_master_sdo_download_complete(ec_master_t *master,
-        uint16_t slave_position, uint16_t index, uint8_t *data,
+        uint16_t slave_position, uint16_t index, const uint8_t *data,
         size_t data_size, uint32_t *abort_code)
 {
     ec_ioctl_slave_sdo_download_t download;
@@ -606,15 +607,18 @@
 
 /****************************************************************************/
 
-void ecrt_master_send(ec_master_t *master)
-{
-    int ret;
-
-    ret = ioctl(master->fd, EC_IOCTL_SEND, NULL);
+size_t ecrt_master_send(ec_master_t *master)
+{
+    int ret;
+    size_t sent_bytes = 0;
+
+    ret = ioctl(master->fd, EC_IOCTL_SEND, &sent_bytes);
     if (EC_IOCTL_IS_ERROR(ret)) {
         fprintf(stderr, "Failed to send: %s\n",
                 strerror(EC_IOCTL_ERRNO(ret)));
     }
+
+    return sent_bytes;
 }
 
 /****************************************************************************/
@@ -714,11 +718,16 @@
 
     ret = ioctl(master->fd, EC_IOCTL_REF_CLOCK_TIME, time);
     if (EC_IOCTL_IS_ERROR(ret)) {
-        fprintf(stderr, "Failed to get reference clock time: %s\n",
-                strerror(EC_IOCTL_ERRNO(ret)));
-    }
-
-    return ret;
+        ret = EC_IOCTL_ERRNO(ret);
+        if (ret != EIO && ret != ENXIO) {
+            // do not log if no refclk or no refclk time yet
+            fprintf(stderr, "Failed to get reference clock time: %s\n",
+                    strerror(ret));
+        }
+        return -ret;
+    }
+
+    return 0;
 }
 
 /****************************************************************************/
--- a/lib/slave_config.c	Fri Jan 22 10:11:58 2016 +0100
+++ b/lib/slave_config.c	Fri Jan 22 13:09:43 2016 +0100
@@ -120,6 +120,23 @@
 
 /*****************************************************************************/
 
+void ecrt_slave_config_overlapping_pdos(ec_slave_config_t *sc,
+        uint8_t allow_overlapping_pdos)
+{
+    ec_ioctl_config_t data;
+
+    memset(&data, 0, sizeof(ec_ioctl_config_t));
+    data.config_index = sc->index;
+    data.allow_overlapping_pdos = allow_overlapping_pdos;
+
+    if (ioctl(sc->master->fd, EC_IOCTL_SC_OVERLAPPING_IO, &data) == -1) {
+        fprintf(stderr, "Failed to config overlapping PDOs: %s\n",
+                strerror(errno));
+    }
+}
+
+/*****************************************************************************/
+
 int ecrt_slave_config_pdo_assign_add(ec_slave_config_t *sc,
         uint8_t sync_index, uint16_t pdo_index)
 {
--- a/master/device.h	Fri Jan 22 10:11:58 2016 +0100
+++ b/master/device.h	Fri Jan 22 13:09:43 2016 +0100
@@ -48,7 +48,7 @@
  * different memory regions, because otherwise the network device DMA could
  * send the same data twice, if it is called twice.
  */
-#define EC_TX_RING_SIZE 2
+#define EC_TX_RING_SIZE 0x10
 
 #ifdef EC_DEBUG_IF
 #include "debug.h"
--- a/master/domain.c	Fri Jan 22 10:11:58 2016 +0100
+++ b/master/domain.c	Fri Jan 22 13:09:43 2016 +0100
@@ -47,6 +47,11 @@
  */
 #define DEBUG_REDUNDANCY 0
 
+#ifndef list_next_entry
+#define list_next_entry(pos, member) \
+    list_entry((pos)->member.next, typeof(*(pos)), member)
+#endif
+
 /*****************************************************************************/
 
 void ec_domain_clear_data(ec_domain_t *);
@@ -79,6 +84,10 @@
     domain->working_counter_changes = 0;
     domain->redundancy_active = 0;
     domain->notify_jiffies = 0;
+
+    /* Used by ec_domain_add_fmmu_config */
+    memset(domain->offset_used, 0, sizeof(domain->offset_used));
+    domain->sc_in_work = 0;
 }
 
 /*****************************************************************************/
@@ -124,14 +133,45 @@
         ec_fmmu_config_t *fmmu /**< FMMU configuration. */
         )
 {
+    const ec_slave_config_t *sc;
+    uint32_t logical_domain_offset;
+    unsigned fmmu_data_size;
+
     fmmu->domain = domain;
-
-    domain->data_size += fmmu->data_size;
+    sc = fmmu->sc;
+
+    fmmu_data_size = ec_pdo_list_total_size(
+        &sc->sync_configs[fmmu->sync_index].pdos);
+
+    if (sc->allow_overlapping_pdos && (sc == domain->sc_in_work)) {
+        // If we permit overlapped PDOs, and we already have an allocated FMMU
+        // for this slave, allocate the subsequent FMMU offsets by direction
+        logical_domain_offset = domain->offset_used[fmmu->dir];
+    } else {
+        // otherwise, allocate to the furthest extent of any allocated
+        // FMMU on this domain.
+        logical_domain_offset = max(domain->offset_used[EC_DIR_INPUT],
+            domain->offset_used[EC_DIR_OUTPUT]);
+        // rebase the free offsets to the current position
+        domain->offset_used[EC_DIR_INPUT] = logical_domain_offset;
+        domain->offset_used[EC_DIR_OUTPUT] = logical_domain_offset;
+    }
+    domain->sc_in_work = sc;
+
+    // consume the offset space for this FMMU's direction
+    domain->offset_used[fmmu->dir] += fmmu_data_size;
+
+    ec_fmmu_set_domain_offset_size(fmmu, logical_domain_offset, fmmu_data_size);
+
     list_add_tail(&fmmu->list, &domain->fmmu_configs);
 
+    // Determine domain size from furthest extent of FMMU data
+    domain->data_size = max(domain->offset_used[EC_DIR_INPUT],
+            domain->offset_used[EC_DIR_OUTPUT]);
+
     EC_MASTER_DBG(domain->master, 1, "Domain %u:"
-            " Added %u bytes, total %zu.\n",
-            domain->index, fmmu->data_size, domain->data_size);
+            " Added %u bytes at %u.\n",
+            domain->index, fmmu->data_size, logical_domain_offset);
 }
 
 /*****************************************************************************/
@@ -192,7 +232,7 @@
  *
  * \return Non-zero if slave connfig was already counted.
  */
-int shall_count(
+static int shall_count(
         const ec_fmmu_config_t *cur_fmmu, /**< Current FMMU with direction to
                                             search for. */
         const ec_fmmu_config_t *first_fmmu /**< Datagram's first FMMU. */
@@ -213,13 +253,55 @@
 
 /*****************************************************************************/
 
+/** Domain finish helper function.
+ *
+ * Known boundaries for a datagram have been identified. Scans the datagram
+ * FMMU boundaries for WKC counters and then creates the datagram_pair.
+ *
+ * \param domain The parent domain
+ * \param datagram_begin_offset Datagram's logical beginning offset
+ * \param datagram_end_offset Logical end offset (one past last byte)
+ * \param datagram_first_fmmu The begin FMMU in the datagram
+ * \param datagram_end_fmmu The end (one past last) FMMU
+ *
+ * \return Non-zero if error emplacing domain
+ */
+static int emplace_datagram(ec_domain_t *domain,
+        uint32_t datagram_begin_offset,
+        uint32_t datagram_end_offset,
+        const ec_fmmu_config_t *datagram_first_fmmu,
+        const ec_fmmu_config_t *datagram_end_fmmu
+        )
+{
+    unsigned int datagram_used[EC_DIR_COUNT];
+    const ec_fmmu_config_t *curr_fmmu;
+    size_t data_size;
+
+    data_size = datagram_end_offset - datagram_begin_offset;
+
+    memset(datagram_used, 0, sizeof(datagram_used));
+    for (curr_fmmu = datagram_first_fmmu;
+            &curr_fmmu->list != &datagram_end_fmmu->list;
+            curr_fmmu = list_next_entry(curr_fmmu, list)) {
+        if (shall_count(curr_fmmu, datagram_first_fmmu)) {
+            datagram_used[curr_fmmu->dir]++;
+        }
+    }
+
+    return ec_domain_add_datagram_pair(domain,
+            domain->logical_base_address + datagram_begin_offset,
+            data_size,
+            domain->data + datagram_begin_offset,
+            datagram_used);
+}
+
+/*****************************************************************************/
+
 /** Finishes a domain.
  *
  * This allocates the necessary datagrams and writes the correct logical
  * addresses to every configured FMMU.
  *
- * \todo Check for FMMUs that do not fit into any datagram.
- *
  * \retval  0 Success
  * \retval <0 Error code.
  */
@@ -228,12 +310,13 @@
         uint32_t base_address /**< Logical base address. */
         )
 {
-    uint32_t datagram_offset;
-    size_t datagram_size;
-    unsigned int datagram_count;
-    unsigned int datagram_used[EC_DIR_COUNT];
+    uint32_t datagram_offset = 0;
+    unsigned int datagram_count = 0;
     ec_fmmu_config_t *fmmu;
     const ec_fmmu_config_t *datagram_first_fmmu = NULL;
+    const ec_fmmu_config_t *valid_fmmu = NULL;
+    unsigned candidate_start = 0;
+    unsigned valid_start = 0;
     const ec_datagram_pair_t *datagram_pair;
     int ret;
 
@@ -253,56 +336,49 @@
     // - correct the logical base addresses
     // - set up the datagrams to carry the process data
     // - calculate the datagrams' expected working counters
-    datagram_offset = 0;
-    datagram_size = 0;
-    datagram_count = 0;
-    datagram_used[EC_DIR_OUTPUT] = 0;
-    datagram_used[EC_DIR_INPUT] = 0;
-
     if (!list_empty(&domain->fmmu_configs)) {
         datagram_first_fmmu =
             list_entry(domain->fmmu_configs.next, ec_fmmu_config_t, list);
     }
 
     list_for_each_entry(fmmu, &domain->fmmu_configs, list) {
-
-        // Correct logical FMMU address
-        fmmu->logical_start_address += base_address;
-
-        // Increment Input/Output counter to determine datagram types
-        // and calculate expected working counters
-        if (shall_count(fmmu, datagram_first_fmmu)) {
-            datagram_used[fmmu->dir]++;
-        }
-
-        // If the current FMMU's data do not fit in the current datagram,
-        // allocate a new one.
-        if (datagram_size + fmmu->data_size > EC_MAX_DATA_SIZE) {
-            ret = ec_domain_add_datagram_pair(domain,
-                    domain->logical_base_address + datagram_offset,
-                    datagram_size, domain->data + datagram_offset,
-                    datagram_used);
-            if (ret < 0)
-                return ret;
-
-            datagram_offset += datagram_size;
-            datagram_size = 0;
-            datagram_count++;
-            datagram_used[EC_DIR_OUTPUT] = 0;
-            datagram_used[EC_DIR_INPUT] = 0;
-            datagram_first_fmmu = fmmu;
-        }
-
-        datagram_size += fmmu->data_size;
+        if (fmmu->data_size > EC_MAX_DATA_SIZE) {
+            EC_MASTER_ERR(domain->master,
+                "FMMU size %u bytes exceeds maximum data size %u",
+                fmmu->data_size, EC_MAX_DATA_SIZE);
+            return -EINVAL;
+        }
+        if (fmmu->logical_domain_offset >= candidate_start) {
+            // As FMMU offsets increase monotonically, and candidate start
+            // offset has never been contradicted, it can now never be
+            // contradicted, as no future FMMU can cross it.
+            // All FMMUs prior to this point approved for next datagram
+            valid_fmmu = fmmu;
+            valid_start = candidate_start;
+            if (fmmu->logical_domain_offset + fmmu->data_size - datagram_offset
+                    > EC_MAX_DATA_SIZE) {
+                // yet the new candidate exceeds the datagram size, so we
+                // use the last known valid candidate to create the datagram
+                ret = emplace_datagram(domain, datagram_offset, valid_start,
+                    datagram_first_fmmu, valid_fmmu);
+                if (ret < 0)
+                    return ret;
+
+                datagram_offset = valid_start;
+                datagram_count++;
+                datagram_first_fmmu = fmmu;
+            }
+        }
+        if (fmmu->logical_domain_offset + fmmu->data_size > candidate_start) {
+            candidate_start = fmmu->logical_domain_offset + fmmu->data_size;
+        }
     }
 
     /* Allocate last datagram pair, if data are left (this is also the case if
      * the process data fit into a single datagram) */
-    if (datagram_size) {
-        ret = ec_domain_add_datagram_pair(domain,
-                domain->logical_base_address + datagram_offset,
-                datagram_size, domain->data + datagram_offset,
-                datagram_used);
+    if (domain->data_size > datagram_offset) {
+        ret = emplace_datagram(domain, datagram_offset, domain->data_size,
+            datagram_first_fmmu, fmmu);
         if (ret < 0)
             return ret;
         datagram_count++;
@@ -317,9 +393,9 @@
         const ec_datagram_t *datagram =
             &datagram_pair->datagrams[EC_DEVICE_MAIN];
         EC_MASTER_INFO(domain->master, "  Datagram %s: Logical offset 0x%08x,"
-                " %zu byte, type %s.\n", datagram->name,
+                " %zu byte, type %s at %p.\n", datagram->name,
                 EC_READ_U32(datagram->address), datagram->data_size,
-                ec_datagram_type_string(datagram));
+                ec_datagram_type_string(datagram), datagram);
     }
 
     return 0;
@@ -503,19 +579,17 @@
                     continue;
                 }
 
-                if (fmmu->logical_start_address >=
-                        logical_datagram_address + datagram_size) {
+                if (fmmu->logical_domain_offset >= datagram_size) {
                     // fmmu data contained in next datagram pair
                     break;
                 }
 
-                datagram_offset =
-                    fmmu->logical_start_address - logical_datagram_address;
+                datagram_offset = fmmu->logical_domain_offset;
 
 #if DEBUG_REDUNDANCY
                 EC_MASTER_DBG(domain->master, 1,
-                        "input fmmu log=%u size=%u offset=%u\n",
-                        fmmu->logical_start_address, fmmu->data_size,
+                        "input fmmu log_off=%u size=%u offset=%u\n",
+                        fmmu->logical_domain_offset, fmmu->data_size,
                         datagram_offset);
                 if (domain->master->debug_level > 0) {
                     ec_print_data(pair->send_buffer + datagram_offset,
--- a/master/domain.h	Fri Jan 22 10:11:58 2016 +0100
+++ b/master/domain.h	Fri Jan 22 13:09:43 2016 +0100
@@ -72,6 +72,11 @@
                                              since last notification. */
     unsigned int redundancy_active; /**< Non-zero, if redundancy is in use. */
     unsigned long notify_jiffies; /**< Time of last notification. */
+    uint32_t offset_used[EC_DIR_COUNT]; /**< Next available domain offset of
+        PDO, by direction */
+    const ec_slave_config_t *sc_in_work; /**< slave_config which is actively
+        being registered in this domain
+        (i.e. ecrt_slave_config_reg_pdo_entry() ) */
 };
 
 /*****************************************************************************/
--- a/master/ethernet.c	Fri Jan 22 10:11:58 2016 +0100
+++ b/master/ethernet.c	Fri Jan 22 13:09:43 2016 +0100
@@ -79,6 +79,7 @@
 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 *);
+static int ec_eoedev_set_mac(struct net_device *netdev, void *p);
 
 /*****************************************************************************/
 
@@ -90,11 +91,34 @@
     .ndo_stop = ec_eoedev_stop,
     .ndo_start_xmit = ec_eoedev_tx,
     .ndo_get_stats = ec_eoedev_stats,
+    .ndo_set_mac_address = ec_eoedev_set_mac,
 };
 #endif
 
 /*****************************************************************************/
 
+/**
+ * ec_eoedev_set_mac - Change the Ethernet Address of the NIC
+ * @netdev: network interface device structure
+ * @p: pointer to an address structure
+ *
+ * Returns 0 on success, negative on failure
+ **/
+static int
+ec_eoedev_set_mac(struct net_device *netdev, void *p)
+{
+   struct sockaddr *addr = p;
+
+   if (!is_valid_ether_addr(addr->sa_data))
+      return -EADDRNOTAVAIL;
+
+   memcpy(netdev->dev_addr, addr->sa_data, netdev->addr_len);
+
+   return 0;
+}
+
+/*****************************************************************************/
+
 /** EoE constructor.
  *
  * Initializes the EoE handler, creates a net_device and registers it.
@@ -107,9 +131,13 @@
         )
 {
     ec_eoe_t **priv;
-    int i, ret = 0;
+    int ret = 0;
     char name[EC_DATAGRAM_NAME_SIZE];
 
+    struct net_device *dev;
+    unsigned char lo_mac[ETH_ALEN] = {0};
+    unsigned int use_master_mac = 0;
+
     eoe->slave = slave;
 
     ec_datagram_init(&eoe->datagram);
@@ -165,8 +193,65 @@
     eoe->dev->get_stats = ec_eoedev_stats;
 #endif
 
-    for (i = 0; i < ETH_ALEN; i++)
-        eoe->dev->dev_addr[i] = i | (i << 4);
+    // First check if the MAC address assigned to the master is globally
+    // unique
+    if ((slave->master->devices[EC_DEVICE_MAIN].dev->dev_addr[0] & 0x02) !=
+            0x02) {
+        // The master MAC is unique and the NIC part can be used for the EoE
+        // interface MAC
+        use_master_mac = 1;
+    }
+    else {
+        // The master MAC is not unique, so we check for unique MAC in other
+        // interfaces
+        dev = first_net_device(&init_net);
+        while (dev) {
+            // Check if globally unique MAC address
+            if (dev->addr_len == ETH_ALEN) {
+                if (memcmp(dev->dev_addr, lo_mac, ETH_ALEN) != 0) {
+                    if ((dev->dev_addr[0] & 0x02) != 0x02) {
+                        // The first globally unique MAC address has been
+                        // identified
+                        break;
+                    }
+                }
+            }
+            dev = next_net_device(dev);
+        }
+        if (eoe->dev->addr_len == ETH_ALEN) {
+            if (dev) {
+                // A unique MAC were identified in one of the other network
+                // interfaces and the NIC part can be used for the EoE
+                // interface MAC.
+                EC_SLAVE_INFO(slave, "%s MAC address derived from"
+                        " NIC part of %s MAC address",
+                    eoe->dev->name, dev->name);
+                eoe->dev->dev_addr[1] = dev->dev_addr[3];
+                eoe->dev->dev_addr[2] = dev->dev_addr[4];
+                eoe->dev->dev_addr[3] = dev->dev_addr[5];
+            }
+            else {
+                use_master_mac = 1;
+            }
+        }
+    }
+    if (eoe->dev->addr_len == ETH_ALEN) {
+        if (use_master_mac) {
+            EC_SLAVE_INFO(slave, "%s MAC address derived"
+                    " from NIC part of %s MAC address",
+                eoe->dev->name,
+                slave->master->devices[EC_DEVICE_MAIN].dev->name);
+            eoe->dev->dev_addr[1] =
+                slave->master->devices[EC_DEVICE_MAIN].dev->dev_addr[3];
+            eoe->dev->dev_addr[2] =
+                slave->master->devices[EC_DEVICE_MAIN].dev->dev_addr[4];
+            eoe->dev->dev_addr[3] =
+                slave->master->devices[EC_DEVICE_MAIN].dev->dev_addr[5];
+        }
+        eoe->dev->dev_addr[0] = 0x02;
+        eoe->dev->dev_addr[4] = (uint8_t)(slave->ring_position >> 8);
+        eoe->dev->dev_addr[5] = (uint8_t)(slave->ring_position);
+    }
 
     // initialize private data
     priv = netdev_priv(eoe->dev);
@@ -188,8 +273,6 @@
         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:
@@ -266,7 +349,8 @@
         current_size = remaining_size;
         last_fragment = 1;
     } else {
-        current_size = ((eoe->slave->configured_tx_mailbox_size - 10) / 32) * 32;
+        current_size =
+            ((eoe->slave->configured_tx_mailbox_size - 10) / 32) * 32;
         last_fragment = 0;
     }
 
--- a/master/fmmu_config.c	Fri Jan 22 10:11:58 2016 +0100
+++ b/master/fmmu_config.c	Fri Jan 22 13:09:43 2016 +0100
@@ -60,13 +60,23 @@
     fmmu->sync_index = sync_index;
     fmmu->dir = dir;
 
-    fmmu->logical_start_address = domain->data_size;
-    fmmu->data_size = ec_pdo_list_total_size(
-            &sc->sync_configs[sync_index].pdos);
+    fmmu->logical_domain_offset = 0;
+    fmmu->data_size = 0;
 
     ec_domain_add_fmmu_config(domain, fmmu);
 }
 
+void ec_fmmu_set_domain_offset_size(
+        ec_fmmu_config_t *fmmu, /**< EtherCAT FMMU configuration. */
+        uint32_t logical_domain_offset, /**< Logical offset address
+            relative to domain->logical_base_address. */
+        unsigned data_size /**< Covered PDO size. */
+        )
+{
+    fmmu->logical_domain_offset = logical_domain_offset;
+    fmmu->data_size = data_size;
+}
+
 /*****************************************************************************/
 
 /** Initializes an FMMU configuration page.
@@ -79,13 +89,14 @@
         uint8_t *data /**> Configuration page memory. */
         )
 {
-    EC_CONFIG_DBG(fmmu->sc, 1, "FMMU: LogAddr 0x%08X, Size %3u,"
+    EC_CONFIG_DBG(fmmu->sc, 1, "FMMU: LogOff 0x%08X, Size %3u,"
             " PhysAddr 0x%04X, SM%u, Dir %s\n",
-            fmmu->logical_start_address, fmmu->data_size,
+            fmmu->logical_domain_offset, fmmu->data_size,
             sync->physical_start_address, fmmu->sync_index,
             fmmu->dir == EC_DIR_INPUT ? "in" : "out");
 
-    EC_WRITE_U32(data,      fmmu->logical_start_address);
+    EC_WRITE_U32(data,      fmmu->domain->logical_base_address +
+        fmmu->logical_domain_offset);
     EC_WRITE_U16(data + 4,  fmmu->data_size); // size of fmmu
     EC_WRITE_U8 (data + 6,  0x00); // logical start bit
     EC_WRITE_U8 (data + 7,  0x07); // logical end bit
--- a/master/fmmu_config.h	Fri Jan 22 10:11:58 2016 +0100
+++ b/master/fmmu_config.h	Fri Jan 22 13:09:43 2016 +0100
@@ -49,7 +49,8 @@
     const ec_domain_t *domain; /**< Domain. */
     uint8_t sync_index; /**< Index of sync manager to use. */
     ec_direction_t dir; /**< FMMU direction. */
-    uint32_t logical_start_address; /**< Logical start address. */
+    uint32_t logical_domain_offset; /**< Logical offset address relative to
+                domain->logical_base_address. */
     unsigned int data_size; /**< Covered PDO size. */
 } ec_fmmu_config_t;
 
@@ -58,6 +59,15 @@
 void ec_fmmu_config_init(ec_fmmu_config_t *, ec_slave_config_t *,
         ec_domain_t *, uint8_t, ec_direction_t);
 
+/**
+ * @param fmmu EtherCAT FMMU configuration.
+ * @param logical_domain_offset Logical offset address 
+        relative to domain->logical_base_address.
+ * @param data_size Covered PDO size.
+*/        
+void ec_fmmu_set_domain_offset_size(ec_fmmu_config_t *fmmu, 
+        uint32_t logical_domain_offset, unsigned data_size);
+
 void ec_fmmu_config_page(const ec_fmmu_config_t *, const ec_sync_t *,
         uint8_t *);
 
--- a/master/fsm_foe.c	Fri Jan 22 10:11:58 2016 +0100
+++ b/master/fsm_foe.c	Fri Jan 22 13:09:43 2016 +0100
@@ -42,9 +42,10 @@
 
 /*****************************************************************************/
 
-/** Maximum time in ms to wait for responses when reading out the dictionary.
- */
-#define EC_FSM_FOE_TIMEOUT 3000
+/** Maximum time in jiffies to wait for responses when reading out the
+ * dictionary.
+ */
+#define EC_FSM_FOE_TIMEOUT_JIFFIES (3 * HZ)
 
 /** Size of the FoE header.
  */
@@ -256,6 +257,10 @@
 
     EC_WRITE_U16(data, EC_FOE_OPCODE_DATA);    // OpCode = DataBlock req.
     EC_WRITE_U32(data + 2, fsm->tx_packet_no); // PacketNo, Password
+#ifdef DEBUG_FOE
+    EC_SLAVE_DBG(fsm->slave, 0, "sending opcode %u packet %u\n",
+            EC_FOE_OPCODE_DATA, fsm->tx_packet_no);
+#endif
 
     memcpy(data + EC_FOE_HEADER_SIZE,
             fsm->tx_buffer + fsm->tx_buffer_offset, current_size);
@@ -293,6 +298,10 @@
 
     EC_WRITE_U16( data, EC_FOE_OPCODE_WRQ); // fsm write request
     EC_WRITE_U32( data + 2, fsm->tx_packet_no );
+#ifdef DEBUG_FOE
+    EC_SLAVE_DBG(fsm->slave, 0, "sending opcode %u packet %u\n",
+            EC_FOE_OPCODE_WRQ, fsm->tx_packet_no);
+#endif
 
     memcpy(data + EC_FOE_HEADER_SIZE, fsm->tx_filename, current_size);
 
@@ -365,9 +374,8 @@
 
     if (!ec_slave_mbox_check(fsm->datagram)) {
         // slave did not put anything in the mailbox yet
-        unsigned long diff_ms = (fsm->datagram->jiffies_received -
-                fsm->jiffies_start) * 1000 / HZ;
-        if (diff_ms >= EC_FSM_FOE_TIMEOUT) {
+        if (time_after(fsm->datagram->jiffies_received,
+                    fsm->jiffies_start + EC_FSM_FOE_TIMEOUT_JIFFIES)) {
             ec_foe_set_tx_error(fsm, FOE_TIMEOUT_ERROR);
             EC_SLAVE_ERR(slave, "Timeout while waiting for ack response.\n");
             return;
@@ -431,6 +439,9 @@
     }
 
     opCode = EC_READ_U8(data);
+#ifdef DEBUG_FOE
+    EC_SLAVE_DBG(fsm->slave, 0, "received opcode %u\n", opCode);
+#endif
 
     if (opCode == EC_FOE_OPCODE_BUSY) {
         // slave not ready
@@ -443,6 +454,20 @@
         return;
     }
 
+    if (opCode == EC_FOE_OPCODE_ERR) {
+        fsm->request->error_code = EC_READ_U32(data + 2);
+        EC_SLAVE_ERR(slave, "Received FoE Error Request (code 0x%08x).\n",
+                fsm->request->error_code);
+        if (rec_size > 6 && data[6]) {
+            uint8_t text[256];
+            strncpy(text, data + 6, min(rec_size - 6, sizeof(text)));
+            text[sizeof(text)-1] = 0;
+            EC_SLAVE_ERR(slave, "FoE Error Text: %s\n", text);
+        }
+        ec_foe_set_tx_error(fsm, FOE_OPCODE_ERROR);
+        return;
+    }
+
     if (opCode == EC_FOE_OPCODE_ACK) {
         fsm->tx_packet_no++;
         fsm->tx_buffer_offset += fsm->tx_current_size;
@@ -566,6 +591,9 @@
     EC_WRITE_U16(data, EC_FOE_OPCODE_RRQ); // fsm read request
     EC_WRITE_U32(data + 2, 0x00000000); // no passwd
     memcpy(data + EC_FOE_HEADER_SIZE, fsm->rx_filename, current_size);
+#ifdef DEBUG_FOE
+    EC_SLAVE_DBG(fsm->slave, 0, "sending opcode %u\n", EC_FOE_OPCODE_RRQ);
+#endif
 
     if (fsm->slave->master->debug_level) {
         EC_SLAVE_DBG(fsm->slave, 1, "FoE Read Request:\n");
@@ -596,6 +624,10 @@
 
     EC_WRITE_U16(data, EC_FOE_OPCODE_ACK);
     EC_WRITE_U32(data + 2, fsm->rx_expected_packet_no);
+#ifdef DEBUG_FOE
+    EC_SLAVE_DBG(fsm->slave, 0, "sending opcode %u packet %u\n",
+            EC_FOE_OPCODE_ACK, fsm->rx_expected_packet_no);
+#endif
 
     return 0;
 }
@@ -704,9 +736,8 @@
     }
 
     if (!ec_slave_mbox_check(fsm->datagram)) {
-        unsigned long diff_ms = (fsm->datagram->jiffies_received -
-                fsm->jiffies_start) * 1000 / HZ;
-        if (diff_ms >= EC_FSM_FOE_TIMEOUT) {
+        if (time_after(fsm->datagram->jiffies_received,
+                    fsm->jiffies_start + EC_FSM_FOE_TIMEOUT_JIFFIES)) {
             ec_foe_set_tx_error(fsm, FOE_TIMEOUT_ERROR);
             EC_SLAVE_ERR(slave, "Timeout while waiting for ack response.\n");
             return;
@@ -734,7 +765,8 @@
         )
 {
     size_t rec_size;
-    uint8_t *data, opCode, packet_no, mbox_prot;
+    uint32_t packet_no;
+    uint8_t *data, opCode, mbox_prot;
 
     ec_slave_t *slave = fsm->slave;
 
@@ -770,11 +802,21 @@
     }
 
     opCode = EC_READ_U8(data);
+#ifdef DEBUG_FOE
+    EC_SLAVE_DBG(fsm->slave, 0, "received opcode %u\n", opCode);
+#endif
 
     if (opCode == EC_FOE_OPCODE_BUSY) {
+        fsm->rx_expected_packet_no--;
         if (ec_foe_prepare_send_ack(fsm, datagram)) {
             ec_foe_set_rx_error(fsm, FOE_PROT_ERROR);
+        } else {
+            fsm->state = ec_fsm_foe_state_sent_ack;
         }
+#ifdef DEBUG_FOE
+        EC_SLAVE_DBG(fsm->slave, 0, "%s() busy. Next pkt %u\n", __func__,
+                fsm->rx_expected_packet_no);
+#endif
         return;
     }
 
@@ -782,9 +824,10 @@
         fsm->request->error_code = EC_READ_U32(data + 2);
         EC_SLAVE_ERR(slave, "Received FoE Error Request (code 0x%08x).\n",
                 fsm->request->error_code);
-        if (rec_size > 6) {
+        if (rec_size > 6 && data[6]) {
             uint8_t text[256];
             strncpy(text, data + 6, min(rec_size - 6, sizeof(text)));
+            text[sizeof(text)-1] = 0;
             EC_SLAVE_ERR(slave, "FoE Error Text: %s\n", text);
         }
         ec_foe_set_rx_error(fsm, FOE_OPCODE_ERROR);
@@ -799,9 +842,10 @@
         return;
     }
 
-    packet_no = EC_READ_U16(data + 2);
+    packet_no = EC_READ_U32(data + 2);
     if (packet_no != fsm->rx_expected_packet_no) {
-        EC_SLAVE_ERR(slave, "Received unexpected packet number.\n");
+        EC_SLAVE_ERR(slave, "Received packet number %u, expected %u.\n",
+                packet_no, fsm->rx_expected_packet_no);
         ec_foe_set_rx_error(fsm, FOE_PACKETNO_ERROR);
         return;
     }
--- a/master/fsm_master.c	Fri Jan 22 10:11:58 2016 +0100
+++ b/master/fsm_master.c	Fri Jan 22 13:09:43 2016 +0100
@@ -246,6 +246,9 @@
         return;
     }
 
+    // check for detached config requests
+    ec_master_expire_slave_config_requests(fsm->master);
+
     ec_datagram_brd(fsm->datagram, 0x0130, 2);
     ec_datagram_zero(fsm->datagram);
     fsm->datagram->device_index = fsm->dev_idx;
@@ -490,6 +493,11 @@
             continue;
         }
 
+        if (!ec_fsm_slave_is_ready(&slave->fsm)) {
+            EC_SLAVE_DBG(slave, 1, "Busy - processing external request!\n");
+            continue;
+        }
+
         list_for_each_entry(req, &slave->config->sdo_requests, list) {
             if (req->state == EC_INT_REQUEST_QUEUED) {
 
--- a/master/fsm_slave.c	Fri Jan 22 10:11:58 2016 +0100
+++ b/master/fsm_slave.c	Fri Jan 22 13:09:43 2016 +0100
@@ -201,6 +201,18 @@
         ec_datagram_t *datagram /**< Datagram to use. */
         )
 {
+    ec_slave_t *slave = fsm->slave;
+    ec_sdo_request_t *req;
+
+    if (slave->config) {
+        list_for_each_entry(req, &slave->config->sdo_requests, list) {
+            if (req->state == EC_INT_REQUEST_QUEUED || req->state == EC_INT_REQUEST_BUSY) {
+                EC_SLAVE_DBG(slave, 1, "Busy - processing internal SDO request!\n");
+                return;
+            }
+        }
+    }
+
     // Check for pending external SDO requests
     if (ec_fsm_slave_action_process_sdo(fsm, datagram)) {
         return;
--- a/master/fsm_slave_config.c	Fri Jan 22 10:11:58 2016 +0100
+++ b/master/fsm_slave_config.c	Fri Jan 22 13:09:43 2016 +0100
@@ -1399,8 +1399,12 @@
             EC_SLAVE_WARN(slave, "Slave did not sync after %lu ms.\n",
                     diff_ms);
         } else {
-            EC_SLAVE_DBG(slave, 1, "Sync after %4lu ms: %10u ns\n",
-                    diff_ms, abs_sync_diff);
+            static unsigned long last_diff_ms = 0;
+            if ((diff_ms < last_diff_ms) || (diff_ms >= (last_diff_ms + 100))) {
+                last_diff_ms = diff_ms;
+                EC_SLAVE_DBG(slave, 1, "Sync after %4lu ms: %10u ns\n",
+                        diff_ms, abs_sync_diff);
+            }
 
             // check synchrony again
             ec_datagram_fprd(datagram, slave->station_address, 0x092c, 4);
--- a/master/globals.h	Fri Jan 22 10:11:58 2016 +0100
+++ b/master/globals.h	Fri Jan 22 13:09:43 2016 +0100
@@ -88,8 +88,14 @@
 #define EC_ADDR_LEN 4
 
 /** Resulting maximum data size of a single datagram in a frame. */
+#ifdef DEBUG_DATAGRAM_OVERFLOW
+// Define a runt datagram which can be easily overflowed on 
+// available hardware for use when testing ec_domain_finish()
+#define EC_MAX_DATA_SIZE (128)
+#else
 #define EC_MAX_DATA_SIZE (ETH_DATA_LEN - EC_FRAME_HEADER_SIZE \
                           - EC_DATAGRAM_HEADER_SIZE - EC_DATAGRAM_FOOTER_SIZE)
+#endif // DEBUG_DATAGRAM_OVERFLOW
 
 /** Mailbox header size.  */
 #define EC_MBOX_HEADER_SIZE 6
--- a/master/ioctl.c	Fri Jan 22 10:11:58 2016 +0100
+++ b/master/ioctl.c	Fri Jan 22 13:09:43 2016 +0100
@@ -544,7 +544,7 @@
     data.slave_config_position = fmmu->sc->position;
     data.sync_index = fmmu->sync_index;
     data.dir = fmmu->dir;
-    data.logical_address = fmmu->logical_start_address;
+    data.logical_address = fmmu->domain->logical_base_address + fmmu->logical_domain_offset;
     data.data_size = fmmu->data_size;
 
     up(&master->master_sem);
@@ -864,7 +864,7 @@
         return -ENOMEM;
     }
 
-    if (copy_from_user(sdo_data, (void __user *) data.data, data.data_size)) {
+    if (copy_from_user(sdo_data, (const void __user *) data.data, data.data_size)) {
         kfree(sdo_data);
         return -EFAULT;
     }
@@ -1913,11 +1913,18 @@
         ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
         )
 {
+    size_t sent_bytes;
+
     if (unlikely(!ctx->requested)) {
         return -EPERM;
     }
 
-    ecrt_master_send(master);
+    sent_bytes = ecrt_master_send(master);
+
+    if (copy_to_user((void __user *) arg, &sent_bytes, sizeof(sent_bytes))) {
+        return -EFAULT;
+    }
+
     return 0;
 }
 
@@ -2254,6 +2261,48 @@
 
 /*****************************************************************************/
 
+/** Configure wether a slave allows overlapping PDOs.
+ */
+static ATTRIBUTES int ec_ioctl_sc_allow_overlapping_pdos(
+        ec_master_t *master, /**< EtherCAT master. */
+        void *arg, /**< ioctl() argument. */
+        ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+        )
+{
+    ec_ioctl_config_t data;
+    ec_slave_config_t *sc;
+    int ret = 0;
+
+    if (unlikely(!ctx->requested)) {
+        ret = -EPERM;
+        goto out_return;
+    }
+
+    if (copy_from_user(&data, (void __user *) arg, sizeof(data))) {
+        ret = -EFAULT;
+        goto out_return;
+    }
+
+    if (down_interruptible(&master->master_sem)) {
+        ret = -EINTR;
+        goto out_return;
+    }
+
+    if (!(sc = ec_master_get_config(master, data.config_index))) {
+        ret = -ENOENT;
+        goto out_up;
+    }
+
+    ecrt_slave_config_overlapping_pdos(sc,
+            data.allow_overlapping_pdos);
+
+out_up:
+    up(&master->master_sem);
+out_return:
+    return ret;
+}
+/*****************************************************************************/
+
 /** Add a PDO to the assignment.
  *
  * \return Zero on success, otherwise a negative error code.
@@ -4447,6 +4496,13 @@
             }
             ret = ec_ioctl_sc_watchdog(master, arg, ctx);
             break;
+        case EC_IOCTL_SC_OVERLAPPING_IO:
+            if (!ctx->writable) {
+                ret = -EPERM;
+                break;
+            }
+            ret = ec_ioctl_sc_allow_overlapping_pdos(master, arg, ctx);
+            break;
         case EC_IOCTL_SC_ADD_PDO:
             if (!ctx->writable) {
                 ret = -EPERM;
--- a/master/ioctl.h	Fri Jan 22 10:11:58 2016 +0100
+++ b/master/ioctl.h	Fri Jan 22 13:09:43 2016 +0100
@@ -56,7 +56,7 @@
  *
  * Increment this when changing the ioctl interface!
  */
-#define EC_IOCTL_VERSION_MAGIC 29
+#define EC_IOCTL_VERSION_MAGIC 30
 
 // Command-line tool
 #define EC_IOCTL_MODULE                EC_IOR(0x00, ec_ioctl_module_t)
@@ -153,6 +153,7 @@
 #define EC_IOCTL_VOE_EXEC             EC_IOWR(0x57, ec_ioctl_voe_t)
 #define EC_IOCTL_VOE_DATA             EC_IOWR(0x58, ec_ioctl_voe_t)
 #define EC_IOCTL_SET_SEND_INTERVAL     EC_IOW(0x59, size_t)
+#define EC_IOCTL_SC_OVERLAPPING_IO     EC_IOW(0x5a, ec_ioctl_config_t)
 
 /*****************************************************************************/
 
@@ -399,7 +400,7 @@
     uint8_t sdo_entry_subindex;
     uint8_t complete_access;
     size_t data_size;
-    uint8_t *data;
+    const uint8_t *data;
 
     // outputs
     uint32_t abort_code;
@@ -439,7 +440,7 @@
     size_t data_size;
     uint32_t result;
     uint32_t error_code;
-    char file_name[32];
+    char file_name[255];
 } ec_ioctl_slave_foe_t;
 
 /*****************************************************************************/
@@ -495,6 +496,7 @@
     int32_t slave_position;
     uint16_t dc_assign_activate;
     ec_sync_signal_t dc_sync[EC_SYNC_SIGNAL_COUNT];
+    uint8_t allow_overlapping_pdos;
 } ec_ioctl_config_t;
 
 /*****************************************************************************/
--- a/master/master.c	Fri Jan 22 10:11:58 2016 +0100
+++ b/master/master.c	Fri Jan 22 13:09:43 2016 +0100
@@ -981,7 +981,7 @@
 /** Sends the datagrams in the queue for a certain device.
  *
  */
-void ec_master_send_datagrams(
+size_t ec_master_send_datagrams(
         ec_master_t *master, /**< EtherCAT master */
         ec_device_index_t device_index /**< Device index. */
         )
@@ -996,6 +996,7 @@
     unsigned long jiffies_sent;
     unsigned int frame_count, more_datagrams_waiting;
     struct list_head sent_datagrams;
+    size_t sent_bytes = 0;
 
 #ifdef EC_HAVE_CYCLES
     cycles_start = get_cycles();
@@ -1081,6 +1082,8 @@
         // send frame
         ec_device_send(&master->devices[device_index],
                 cur_data - frame_data);
+        /* preamble and inter-frame gap */
+        sent_bytes += ETH_HLEN + cur_data - frame_data + ETH_FCS_LEN + 20;
 #ifdef EC_HAVE_CYCLES
         cycles_sent = get_cycles();
 #endif
@@ -1098,7 +1101,7 @@
 
         frame_count++;
     }
-    while (more_datagrams_waiting);
+    while (more_datagrams_waiting && frame_count < EC_TX_RING_SIZE);
 
 #ifdef EC_HAVE_CYCLES
     if (unlikely(master->debug_level > 1)) {
@@ -1108,6 +1111,7 @@
                (unsigned int) (cycles_end - cycles_start) * 1000 / cpu_khz);
     }
 #endif
+    return sent_bytes;
 }
 
 /*****************************************************************************/
@@ -1535,9 +1539,7 @@
 {
     ec_master_t *master = (ec_master_t *) priv_data;
     int fsm_exec;
-#ifdef EC_USE_HRTIMER
     size_t sent_bytes;
-#endif
 
     // send interval in IDLE phase
     ec_master_set_send_interval(master, 1000000 / HZ);
@@ -1570,11 +1572,7 @@
         if (fsm_exec) {
             ec_master_queue_datagram(master, &master->fsm_datagram);
         }
-        ecrt_master_send(master);
-#ifdef EC_USE_HRTIMER
-        sent_bytes = master->devices[EC_DEVICE_MAIN].tx_skb[
-            master->devices[EC_DEVICE_MAIN].tx_ring_index]->len;
-#endif
+        sent_bytes = ecrt_master_send(master);
         up(&master->io_sem);
 
         if (ec_fsm_master_idle(&master->fsm)) {
@@ -1586,7 +1584,8 @@
 #endif
         } else {
 #ifdef EC_USE_HRTIMER
-            ec_master_nanosleep(sent_bytes * EC_BYTE_TRANSMISSION_TIME_NS);
+            ec_master_nanosleep(
+                    sent_bytes * EC_BYTE_TRANSMISSION_TIME_NS * 6 / 5);
 #else
             schedule();
 #endif
@@ -1785,6 +1784,21 @@
 
 /*****************************************************************************/
 
+/** Abort active requests for slave configs without attached slaves.
+ */
+void ec_master_expire_slave_config_requests(
+        ec_master_t *master /**< EtherCAT master. */
+        )
+{
+    ec_slave_config_t *sc;
+
+    list_for_each_entry(sc, &master->configs, list) {
+        ec_slave_config_expire_disconnected_requests(sc);
+    }
+}
+
+/*****************************************************************************/
+
 /** Common implementation for ec_master_find_slave()
  * and ec_master_find_slave_const().
  */
@@ -2224,10 +2238,12 @@
         }
     }
 
+#ifdef EC_REFCLKOP
     // always set DC reference clock to OP
     if (master->dc_ref_clock) {
         ec_slave_request_state(master->dc_ref_clock, EC_SLAVE_STATE_OP);
     }
+#endif
 }
 
 /******************************************************************************
@@ -2430,10 +2446,12 @@
 
 /*****************************************************************************/
 
-void ecrt_master_send(ec_master_t *master)
+size_t ecrt_master_send(ec_master_t *master)
 {
     ec_datagram_t *datagram, *n;
     ec_device_index_t dev_idx;
+    size_t sent_bytes = 0;
+
 
     if (master->injection_seq_rt != master->injection_seq_fsm) {
         // inject datagram produced by master FSM
@@ -2468,8 +2486,11 @@
         }
 
         // send frames
-        ec_master_send_datagrams(master, dev_idx);
-    }
+        sent_bytes = max(sent_bytes,
+            ec_master_send_datagrams(master, dev_idx));
+    }
+
+    return sent_bytes;
 }
 
 /*****************************************************************************/
@@ -2832,7 +2853,7 @@
 /*****************************************************************************/
 
 int ecrt_master_sdo_download(ec_master_t *master, uint16_t slave_position,
-        uint16_t index, uint8_t subindex, uint8_t *data,
+        uint16_t index, uint8_t subindex, const uint8_t *data,
         size_t data_size, uint32_t *abort_code)
 {
     ec_sdo_request_t request;
@@ -2916,7 +2937,7 @@
 /*****************************************************************************/
 
 int ecrt_master_sdo_download_complete(ec_master_t *master,
-        uint16_t slave_position, uint16_t index, uint8_t *data,
+        uint16_t slave_position, uint16_t index, const uint8_t *data,
         size_t data_size, uint32_t *abort_code)
 {
     ec_sdo_request_t request;
--- a/master/master.h	Fri Jan 22 10:11:58 2016 +0100
+++ b/master/master.h	Fri Jan 22 13:09:43 2016 +0100
@@ -351,6 +351,7 @@
 // misc.
 void ec_master_set_send_interval(ec_master_t *, unsigned int);
 void ec_master_attach_slave_configs(ec_master_t *);
+void ec_master_expire_slave_config_requests(ec_master_t *);
 ec_slave_t *ec_master_find_slave(ec_master_t *, uint16_t, uint16_t);
 const ec_slave_t *ec_master_find_slave_const(const ec_master_t *, uint16_t,
         uint16_t);
--- a/master/slave_config.c	Fri Jan 22 10:11:58 2016 +0100
+++ b/master/slave_config.c	Fri Jan 22 13:09:43 2016 +0100
@@ -70,6 +70,7 @@
     sc->vendor_id = vendor_id;
     sc->product_code = product_code;
     sc->watchdog_divider = 0; // use default
+    sc->allow_overlapping_pdos = 0; // default not allowed
     sc->watchdog_intervals = 0; // use default
 
     sc->slave = NULL;
@@ -181,7 +182,7 @@
     for (i = 0; i < sc->used_fmmus; i++) {
         fmmu = &sc->fmmu_configs[i];
         if (fmmu->domain == domain && fmmu->sync_index == sync_index)
-            return fmmu->logical_start_address;
+            return fmmu->logical_domain_offset;
     }
 
     if (sc->used_fmmus == EC_MAX_FMMUS) {
@@ -189,13 +190,44 @@
         return -EOVERFLOW;
     }
 
-    fmmu = &sc->fmmu_configs[sc->used_fmmus++];
+    fmmu = &sc->fmmu_configs[sc->used_fmmus];
 
     down(&sc->master->master_sem);
     ec_fmmu_config_init(fmmu, sc, domain, sync_index, dir);
+
+#if 0 //TODO overlapping PDOs
+    // Overlapping PDO Support from 4751747d4e6d
+    // FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME
+    // parent code does not call ec_fmmu_config_domain
+    // FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME
+    fmmu_logical_start_address = domain->tx_size;
+    tx_size = fmmu->data_size;
+
+    // FIXME is it enough to take only the *previous* FMMU into account?
+
+    // FIXME Need to qualify allow_overlapping_pdos with slave->sii.general_flags.enable_not_lrw
+
+    if (sc->allow_overlapping_pdos && sc->used_fmmus > 0) {
+        prev_fmmu = &sc->fmmu_configs[sc->used_fmmus - 1];
+        if (fmmu->dir != prev_fmmu->dir && prev_fmmu->tx_size != 0) {
+            // prev fmmu has opposite direction
+            // and is not already paired with prev-prev fmmu
+            old_prev_tx_size = prev_fmmu->tx_size;
+            prev_fmmu->tx_size = max(fmmu->data_size, prev_fmmu->data_size);
+            domain->tx_size += prev_fmmu->tx_size - old_prev_tx_size;
+            tx_size = 0;
+            fmmu_logical_start_address = prev_fmmu->logical_domain_offset;
+        }
+    }
+
+    ec_fmmu_config_domain(fmmu, domain, fmmu_logical_start_address, tx_size);
+    // Overlapping PDO Support from 4751747d4e6d
+#endif
+
+    sc->used_fmmus++;
     up(&sc->master->master_sem);
 
-    return fmmu->logical_start_address;
+    return fmmu->logical_domain_offset;
 }
 
 /*****************************************************************************/
@@ -276,6 +308,10 @@
         list_for_each_entry(reg, &sc->reg_requests, list) {
             if (sc->slave->fsm.reg_request == reg) {
                 sc->slave->fsm.reg_request = NULL;
+                EC_SLAVE_WARN(sc->slave, "Aborting register request,"
+                        " slave is detaching.\n");
+                reg->state = EC_INT_REQUEST_FAILURE;
+                wake_up_all(&sc->slave->master->request_queue);
                 break;
             }
         }
@@ -517,6 +553,36 @@
     return NULL;
 }
 
+/*****************************************************************************/
+
+/** Expires any requests that have been started on a detached slave.
+ */
+void ec_slave_config_expire_disconnected_requests(
+        ec_slave_config_t *sc /**< Slave configuration. */
+        )
+{
+    ec_sdo_request_t *sdo_req;
+    ec_reg_request_t *reg_req;
+
+    if (sc->slave) { return; }
+    
+    list_for_each_entry(sdo_req, &sc->sdo_requests, list) {
+        if (sdo_req->state == EC_INT_REQUEST_QUEUED ||
+                sdo_req->state == EC_INT_REQUEST_BUSY) {
+            EC_CONFIG_DBG(sc, 1, "Aborting SDO request; no slave attached.\n");
+            sdo_req->state = EC_INT_REQUEST_FAILURE;
+        }
+    }
+    
+    list_for_each_entry(reg_req, &sc->reg_requests, list) {
+        if (reg_req->state == EC_INT_REQUEST_QUEUED ||
+                reg_req->state == EC_INT_REQUEST_BUSY) {
+            EC_CONFIG_DBG(sc, 1, "Aborting register request; no slave attached.\n");
+            reg_req->state = EC_INT_REQUEST_FAILURE;
+        }
+    }
+}
+
 /******************************************************************************
  *  Application interface
  *****************************************************************************/
@@ -560,6 +626,17 @@
 
 /*****************************************************************************/
 
+void ecrt_slave_config_overlapping_pdos(ec_slave_config_t *sc,
+        uint8_t allow_overlapping_pdos )
+{
+    EC_CONFIG_DBG(sc, 1, "%s(sc = 0x%p, allow_overlapping_pdos = %u)\n",
+                __func__, sc, allow_overlapping_pdos);
+
+    sc->allow_overlapping_pdos = allow_overlapping_pdos;
+}
+
+/*****************************************************************************/
+
 int ecrt_slave_config_pdo_assign_add(ec_slave_config_t *sc,
         uint8_t sync_index, uint16_t pdo_index)
 {
--- a/master/slave_config.h	Fri Jan 22 10:11:58 2016 +0100
+++ b/master/slave_config.h	Fri Jan 22 13:09:43 2016 +0100
@@ -130,6 +130,8 @@
     uint16_t watchdog_intervals; /**< Process data watchdog intervals (see
                                    spec. reg. 0x0420). */
 
+    uint8_t allow_overlapping_pdos;	/**< Allow input PDOs use the same frame space
+                                      as output PDOs. */
     ec_slave_t *slave; /**< Slave pointer. This is \a NULL, if the slave is
                          offline. */
 
@@ -172,6 +174,7 @@
         unsigned int);
 ec_voe_handler_t *ec_slave_config_find_voe_handler(ec_slave_config_t *,
         unsigned int);
+void ec_slave_config_expire_disconnected_requests(ec_slave_config_t *);
 
 ec_sdo_request_t *ecrt_slave_config_create_sdo_request_err(
         ec_slave_config_t *, uint16_t, uint8_t, size_t);
--- a/script/init.d/ethercat.in	Fri Jan 22 10:11:58 2016 +0100
+++ b/script/init.d/ethercat.in	Fri Jan 22 13:09:43 2016 +0100
@@ -227,6 +227,7 @@
     $0 stop || exit 1
     sleep 1
     $0 start
+    exit
     ;;
 
 status)
--- a/tool/CommandDownload.cpp	Fri Jan 22 10:11:58 2016 +0100
+++ b/tool/CommandDownload.cpp	Fri Jan 22 13:09:43 2016 +0100
@@ -173,16 +173,17 @@
             throwCommandException(err);
         }
         data.data_size = contents.size();
-        data.data = new uint8_t[data.data_size + 1];
+        uint8_t *sdo_data = new uint8_t[data.data_size + 1];
+        data.data = sdo_data;
 
         try {
             data.data_size = interpretAsType(
-                    dataType, contents, data.data, data.data_size);
+                    dataType, contents, sdo_data, data.data_size);
         } catch (SizeException &e) {
-            delete [] data.data;
+            delete [] sdo_data;
             throwCommandException(e.what());
         } catch (ios::failure &e) {
-            delete [] data.data;
+            delete [] sdo_data;
             err << "Invalid value argument '" << args[2]
                 << "' for type '" << dataType->name << "'!";
             throwInvalidUsageException(err);
@@ -195,16 +196,17 @@
             data.data_size = DefaultBufferSize;
         }
 
-        data.data = new uint8_t[data.data_size + 1];
+        uint8_t *sdo_data = new uint8_t[data.data_size + 1];
+        data.data = sdo_data;
 
         try {
             data.data_size = interpretAsType(
-                    dataType, args[valueIndex], data.data, data.data_size);
+                    dataType, args[valueIndex], sdo_data, data.data_size);
         } catch (SizeException &e) {
-            delete [] data.data;
+            delete [] sdo_data;
             throwCommandException(e.what());
         } catch (ios::failure &e) {
-            delete [] data.data;
+            delete [] sdo_data;
             err << "Invalid value argument '" << args[2]
                 << "' for type '" << dataType->name << "'!";
             throwInvalidUsageException(err);
@@ -212,19 +214,6 @@
     }
 
     try {
-        data.data_size = interpretAsType(
-                dataType, args[valueIndex], data.data, data.data_size);
-    } catch (SizeException &e) {
-        delete [] data.data;
-        throwCommandException(e.what());
-    } catch (ios::failure &e) {
-        delete [] data.data;
-        err << "Invalid value argument '" << args[2]
-            << "' for type '" << dataType->name << "'!";
-        throwInvalidUsageException(err);
-    }
-
-    try {
         m.sdoDownload(&data);
     } catch (MasterDeviceSdoAbortException &e) {
         delete [] data.data;
--- a/tool/DataTypeHandler.cpp	Fri Jan 22 10:11:58 2016 +0100
+++ b/tool/DataTypeHandler.cpp	Fri Jan 22 13:09:43 2016 +0100
@@ -33,6 +33,7 @@
 #include <iostream>
 #endif
 
+#include <string.h>
 #include <iomanip>
 #include <sstream>
 using namespace std;
@@ -106,6 +107,8 @@
     stringstream str;
     size_t dataSize = type->byteSize;
 
+    memset(target, 0, targetSize);
+
 #if DEBUG
 	cerr << __func__ << "(targetSize=" << targetSize << ")" << endl;
 #endif
@@ -192,7 +195,7 @@
                     << dataSize << " > " << targetSize << ")";
                 throw SizeException(err.str());
             }
-            str >> (char *) target;
+            str.read((char *) target, dataSize);
             break;
         case 0x0011: // double
             {