Slave scanning and configuration on all links. redundancy
authorFlorian Pose <fp@igh-essen.com>
Fri, 06 Apr 2012 23:35:05 +0200
branchredundancy
changeset 2374 e898451c054a
parent 2373 593272e5a169
child 2379 6f100ee02e65
Slave scanning and configuration on all links.
master/cdev.c
master/datagram_pair.c
master/domain.c
master/fsm_master.c
master/fsm_master.h
master/fsm_slave.c
master/fsm_slave_config.c
master/globals.h
master/ioctl.h
master/master.c
master/master.h
master/module.c
master/slave.c
master/slave.h
master/voe_handler.c
tool/CommandSlaves.cpp
tool/CommandSlaves.h
--- a/master/cdev.c	Fri Apr 06 23:30:35 2012 +0200
+++ b/master/cdev.c	Fri Apr 06 23:35:05 2012 +0200
@@ -294,6 +294,7 @@
         return -EINVAL;
     }
 
+    data.device_index = slave->device_index;
     data.vendor_id = slave->sii.vendor_id;
     data.product_code = slave->sii.product_code;
     data.revision_number = slave->sii.revision_number;
--- a/master/datagram_pair.c	Fri Apr 06 23:30:35 2012 +0200
+++ b/master/datagram_pair.c	Fri Apr 06 23:35:05 2012 +0200
@@ -52,17 +52,18 @@
         const unsigned int used[] /**< input/output use count. */
         )
 {
-    unsigned int dev_idx;
+    ec_device_index_t dev_idx;
     int ret;
 
     INIT_LIST_HEAD(&pair->list);
     pair->domain = domain;
 
-    for (dev_idx = 0; dev_idx < EC_NUM_DEVICES; dev_idx++) {
+    for (dev_idx = EC_DEVICE_MAIN; dev_idx < EC_NUM_DEVICES; dev_idx++) {
         ec_datagram_init(&pair->datagrams[dev_idx]);
         snprintf(pair->datagrams[dev_idx].name,
                 EC_DATAGRAM_NAME_SIZE, "domain%u-%u-%s", domain->index,
-                logical_offset, dev_idx ? "backup" : "main");
+                logical_offset, ec_device_names[dev_idx]);
+        pair->datagrams[dev_idx].device_index = dev_idx;
     }
 
     pair->expected_working_counter = 0U;
--- a/master/domain.c	Fri Apr 06 23:30:35 2012 +0200
+++ b/master/domain.c	Fri Apr 06 23:35:05 2012 +0200
@@ -585,7 +585,7 @@
 void ecrt_domain_queue(ec_domain_t *domain)
 {
     ec_datagram_pair_t *datagram_pair;
-    unsigned int dev_idx;
+    ec_device_index_t dev_idx;
 
     list_for_each_entry(datagram_pair, &domain->datagram_pairs, list) {
 
@@ -599,9 +599,9 @@
                 datagram_pair->datagrams[EC_DEVICE_MAIN].data,
                 datagram_pair->datagrams[EC_DEVICE_MAIN].data_size);
 
-        for (dev_idx = 0; dev_idx < EC_NUM_DEVICES; dev_idx++) {
+        for (dev_idx = EC_DEVICE_MAIN; dev_idx < EC_NUM_DEVICES; dev_idx++) {
             ec_master_queue_datagram(domain->master,
-                    &datagram_pair->datagrams[dev_idx], dev_idx);
+                    &datagram_pair->datagrams[dev_idx]);
         }
     }
 }
--- a/master/fsm_master.c	Fri Apr 06 23:30:35 2012 +0200
+++ b/master/fsm_master.c	Fri Apr 06 23:35:05 2012 +0200
@@ -67,6 +67,7 @@
 void ec_fsm_master_state_sdo_request(ec_fsm_master_t *);
 void ec_fsm_master_state_reg_request(ec_fsm_master_t *);
 
+void ec_fsm_master_enter_clear_addresses(ec_fsm_master_t *);
 void ec_fsm_master_enter_write_system_times(ec_fsm_master_t *);
 
 /*****************************************************************************/
@@ -79,14 +80,19 @@
         ec_datagram_t *datagram /**< Datagram object to use. */
         )
 {
+    ec_device_index_t dev_idx;
+
     fsm->master = master;
     fsm->datagram = datagram;
     fsm->state = ec_fsm_master_state_start;
     fsm->idle = 0;
-    fsm->link_state = 0;
-    fsm->slaves_responding = 0;
+    fsm->dev_idx = EC_DEVICE_MAIN;
+    for (dev_idx = EC_DEVICE_MAIN; dev_idx < EC_NUM_DEVICES; dev_idx++) {
+        fsm->link_state[dev_idx] = 0;
+        fsm->slaves_responding[dev_idx] = 0;
+        fsm->slave_states[dev_idx] = EC_SLAVE_STATE_UNKNOWN;
+    }
     fsm->rescan_required = 0;
-    fsm->slave_states = EC_SLAVE_STATE_UNKNOWN;
 
     // init sub-state-machines
     ec_fsm_coe_init(&fsm->fsm_coe, fsm->datagram);
@@ -159,6 +165,7 @@
         ec_fsm_master_t *fsm /**< Master state machine. */
         )
 {
+    fsm->dev_idx = EC_DEVICE_MAIN;
     fsm->state = ec_fsm_master_state_start;
     fsm->state(fsm); // execute immediately
 }
@@ -178,6 +185,7 @@
     fsm->idle = 1;
     ec_datagram_brd(fsm->datagram, 0x0130, 2);
     ec_datagram_zero(fsm->datagram);
+    fsm->datagram->device_index = fsm->dev_idx;
     fsm->state = ec_fsm_master_state_broadcast;
 }
 
@@ -196,48 +204,58 @@
     ec_slave_t *slave;
     ec_master_t *master = fsm->master;
 
-    if (datagram->state == EC_DATAGRAM_TIMED_OUT)
-        return; // always retry
-
     // bus topology change?
-    if (datagram->working_counter != fsm->slaves_responding) {
+    if (datagram->working_counter != fsm->slaves_responding[fsm->dev_idx]) {
         fsm->rescan_required = 1;
-        fsm->slaves_responding = datagram->working_counter;
-        EC_MASTER_INFO(master, "%u slave(s) responding.\n",
-                fsm->slaves_responding);
-    }
-
-    if (fsm->link_state && !master->devices[EC_DEVICE_MAIN].link_state) {
-        // link went down
+        fsm->slaves_responding[fsm->dev_idx] = datagram->working_counter;
+        EC_MASTER_INFO(master, "%u slave(s) responding on %s device.\n",
+                fsm->slaves_responding[fsm->dev_idx],
+                ec_device_names[fsm->dev_idx]);
+    }
+
+    if (fsm->link_state[fsm->dev_idx] &&
+            !master->devices[fsm->dev_idx].link_state) {
+        ec_device_index_t dev_idx;
+
         EC_MASTER_DBG(master, 1, "Master state machine detected "
-                "link down. Clearing slave list.\n");
+                "link down on %s device. Clearing slave list.\n",
+                ec_device_names[fsm->dev_idx]);
 
 #ifdef EC_EOE
         ec_master_eoe_stop(master);
         ec_master_clear_eoe_handlers(master);
 #endif
         ec_master_clear_slaves(master);
-        fsm->slave_states = 0x00;
-        fsm->slaves_responding = 0; /* reset to trigger rescan on next link
-                                       up. */
-    }
-    fsm->link_state = master->devices[EC_DEVICE_MAIN].link_state;
-
-    if (datagram->state != EC_DATAGRAM_RECEIVED) {
-        ec_fsm_master_restart(fsm);
-        return;
-    }
-
-    if (fsm->slaves_responding) {
+
+        for (dev_idx = EC_DEVICE_MAIN; dev_idx < EC_NUM_DEVICES; dev_idx++) {
+            fsm->slave_states[dev_idx] = 0x00;
+            fsm->slaves_responding[dev_idx] = 0; /* Reset to trigger rescan on
+                                                    next link up. */
+        }
+    }
+    fsm->link_state[fsm->dev_idx] = master->devices[fsm->dev_idx].link_state;
+
+    if (datagram->state == EC_DATAGRAM_RECEIVED &&
+            fsm->slaves_responding[fsm->dev_idx]) {
         uint8_t states = EC_READ_U8(datagram->data);
-        if (states != fsm->slave_states) { // slave states changed?
+        if (states != fsm->slave_states[fsm->dev_idx]) {
+            // slave states changed
             char state_str[EC_STATE_STRING_SIZE];
-            fsm->slave_states = states;
-            ec_state_string(fsm->slave_states, state_str, 1);
-            EC_MASTER_INFO(master, "Slave states: %s.\n", state_str);
+            fsm->slave_states[fsm->dev_idx] = states;
+            ec_state_string(states, state_str, 1);
+            EC_MASTER_INFO(master, "Slave states on %s device: %s.\n",
+                    ec_device_names[fsm->dev_idx], state_str);
         }
     } else {
-        fsm->slave_states = 0x00;
+        fsm->slave_states[fsm->dev_idx] = 0x00;
+    }
+
+    fsm->dev_idx++;
+    if (fsm->dev_idx < EC_NUM_DEVICES) {
+        // check number of responding slaves on next device
+        fsm->state = ec_fsm_master_state_start;
+        fsm->state(fsm); // execute immediately
+        return;
     }
 
     if (fsm->rescan_required) {
@@ -245,6 +263,9 @@
         if (!master->allow_scan) {
             up(&master->scan_sem);
         } else {
+            unsigned int count = 0, next_dev_slave, ring_position;
+            ec_device_index_t dev_idx;
+
             master->scan_busy = 1;
             up(&master->scan_sem);
 
@@ -259,9 +280,12 @@
 #endif
             ec_master_clear_slaves(master);
 
-            master->slave_count = fsm->slaves_responding;
-
-            if (!master->slave_count) {
+            for (dev_idx = EC_DEVICE_MAIN; dev_idx < EC_NUM_DEVICES;
+                    dev_idx++) {
+                count += fsm->slaves_responding[dev_idx];
+            }
+
+            if (!count) {
                 // no slaves present -> finish state machine.
                 master->scan_busy = 0;
                 wake_up_interruptible(&master->scan_queue);
@@ -269,12 +293,11 @@
                 return;
             }
 
-            size = sizeof(ec_slave_t) * master->slave_count;
+            size = sizeof(ec_slave_t) * count;
             if (!(master->slaves =
                         (ec_slave_t *) kmalloc(size, GFP_KERNEL))) {
                 EC_MASTER_ERR(master, "Failed to allocate %u bytes"
                         " of slave memory!\n", size);
-                master->slave_count = 0; // TODO avoid retrying scan!
                 master->scan_busy = 0;
                 wake_up_interruptible(&master->scan_queue);
                 ec_fsm_master_restart(fsm);
@@ -282,21 +305,35 @@
             }
 
             // init slaves
-            for (i = 0; i < master->slave_count; i++) {
+            dev_idx = EC_DEVICE_MAIN;
+            next_dev_slave = fsm->slaves_responding[dev_idx];
+            ring_position = 0;
+            for (i = 0; i < count; i++, ring_position++) {
                 slave = master->slaves + i;
-                ec_slave_init(slave, master, i, i + 1);
+                while (i >= next_dev_slave) {
+                    dev_idx++;
+                    next_dev_slave += fsm->slaves_responding[dev_idx];
+                    ring_position = 0;
+                }
+
+                ec_slave_init(slave, master, dev_idx, ring_position, i + 1);
 
                 // do not force reconfiguration in operation phase to avoid
                 // unnecesssary process data interruptions
-                if (master->phase != EC_OPERATION)
+                if (master->phase != EC_OPERATION) {
                     slave->force_config = 1;
+                }
             }
-
-            // broadcast clear all station addresses
-            ec_datagram_bwr(datagram, 0x0010, 2);
-            EC_WRITE_U16(datagram->data, 0x0000);
-            fsm->retries = EC_FSM_RETRIES;
-            fsm->state = ec_fsm_master_state_clear_addresses;
+            master->slave_count = count;
+
+            /* start with first device with slaves responding; at least one
+             * has responding slaves, otherwise count would be zero. */
+            fsm->dev_idx = EC_DEVICE_MAIN;
+            while (!fsm->slaves_responding[fsm->dev_idx]) {
+                fsm->dev_idx++;
+            }
+
+            ec_fsm_master_enter_clear_addresses(fsm);
             return;
         }
     }
@@ -318,6 +355,7 @@
             ec_datagram_fprd(fsm->datagram, fsm->slave->station_address,
                     0x0130, 2);
             ec_datagram_zero(datagram);
+            fsm->datagram->device_index = fsm->slave->device_index;
             fsm->retries = EC_FSM_RETRIES;
             fsm->state = ec_fsm_master_state_read_state;
         }
@@ -411,6 +449,7 @@
                     request->offset, request->length);
             memcpy(fsm->datagram->data, request->data, request->length);
         }
+        fsm->datagram->device_index = request->slave->device_index;
         fsm->retries = EC_FSM_RETRIES;
         fsm->state = ec_fsm_master_state_reg_request;
         return 1;
@@ -518,6 +557,7 @@
         fsm->state = ec_fsm_master_state_sdo_dictionary;
         ec_fsm_coe_dictionary(&fsm->fsm_coe, slave);
         ec_fsm_coe_exec(&fsm->fsm_coe); // execute immediately
+        fsm->datagram->device_index = fsm->slave->device_index;
         return;
     }
 
@@ -550,6 +590,7 @@
         ec_datagram_fprd(fsm->datagram,
                 fsm->slave->station_address, 0x0130, 2);
         ec_datagram_zero(fsm->datagram);
+        fsm->datagram->device_index = fsm->slave->device_index;
         fsm->retries = EC_FSM_RETRIES;
         fsm->state = ec_fsm_master_state_read_state;
         return;
@@ -607,6 +648,7 @@
         fsm->state = ec_fsm_master_state_configure_slave;
         ec_fsm_slave_config_start(&fsm->fsm_slave_config, slave);
         fsm->state(fsm); // execute immediately
+        fsm->datagram->device_index = fsm->slave->device_index;
         return;
     }
 
@@ -680,8 +722,9 @@
 {
     ec_slave_t *slave = fsm->slave;
 
-    if (ec_fsm_change_exec(&fsm->fsm_change))
-        return;
+    if (ec_fsm_change_exec(&fsm->fsm_change)) {
+        return;
+    }
 
     if (!ec_fsm_change_success(&fsm->fsm_change)) {
         fsm->slave->error_flag = 1;
@@ -693,6 +736,22 @@
 
 /*****************************************************************************/
 
+/** Start clearing slave addresses.
+ */
+void ec_fsm_master_enter_clear_addresses(
+        ec_fsm_master_t *fsm /**< Master state machine. */
+        )
+{
+    // broadcast clear all station addresses
+    ec_datagram_bwr(fsm->datagram, 0x0010, 2);
+    EC_WRITE_U16(fsm->datagram->data, 0x0000);
+    fsm->datagram->device_index = fsm->dev_idx;
+    fsm->retries = EC_FSM_RETRIES;
+    fsm->state = ec_fsm_master_state_clear_addresses;
+}
+
+/*****************************************************************************/
+
 /** Master state: CLEAR ADDRESSES.
  */
 void ec_fsm_master_state_clear_addresses(
@@ -702,12 +761,14 @@
     ec_master_t *master = fsm->master;
     ec_datagram_t *datagram = fsm->datagram;
 
-    if (datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--)
-        return;
+    if (datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--) {
+        return;
+    }
 
     if (datagram->state != EC_DATAGRAM_RECEIVED) {
         EC_MASTER_ERR(master, "Failed to receive address"
-                " clearing datagram: ");
+                " clearing datagram on %s link: ",
+                ec_device_names[fsm->dev_idx]);
         ec_datagram_print_state(datagram);
         master->scan_busy = 0;
         wake_up_interruptible(&master->scan_queue);
@@ -715,17 +776,20 @@
         return;
     }
 
-    if (datagram->working_counter != master->slave_count) {
-        EC_MASTER_WARN(master, "Failed to clear all station addresses:"
+    if (datagram->working_counter != fsm->slaves_responding[fsm->dev_idx]) {
+        EC_MASTER_WARN(master, "Failed to clear station addresses on %s link:"
                 " Cleared %u of %u",
-                datagram->working_counter, master->slave_count);
+                ec_device_names[fsm->dev_idx], datagram->working_counter,
+                fsm->slaves_responding[fsm->dev_idx]);
     }
 
     EC_MASTER_DBG(master, 1, "Sending broadcast-write"
-            " to measure transmission delays.\n");
+            " to measure transmission delays on %s link.\n",
+            ec_device_names[fsm->dev_idx]);
 
     ec_datagram_bwr(datagram, 0x0900, 1);
     ec_datagram_zero(datagram);
+    fsm->datagram->device_index = fsm->dev_idx;
     fsm->retries = EC_FSM_RETRIES;
     fsm->state = ec_fsm_master_state_dc_measure_delays;
 }
@@ -745,7 +809,8 @@
         return;
 
     if (datagram->state != EC_DATAGRAM_RECEIVED) {
-        EC_MASTER_ERR(master, "Failed to receive delay measuring datagram: ");
+        EC_MASTER_ERR(master, "Failed to receive delay measuring datagram"
+                " on %s link: ", ec_device_names[fsm->dev_idx]);
         ec_datagram_print_state(datagram);
         master->scan_busy = 0;
         wake_up_interruptible(&master->scan_queue);
@@ -753,16 +818,30 @@
         return;
     }
 
-    EC_MASTER_DBG(master, 1, "%u slaves responded to delay measuring.\n",
-            datagram->working_counter);
+    EC_MASTER_DBG(master, 1, "%u slaves responded to delay measuring"
+            " on %s link.\n",
+            datagram->working_counter, ec_device_names[fsm->dev_idx]);
+
+    do {
+        fsm->dev_idx++;
+    } while (fsm->dev_idx < EC_NUM_DEVICES &&
+            !fsm->slaves_responding[fsm->dev_idx]);
+    if (fsm->dev_idx < EC_NUM_DEVICES) {
+        ec_fsm_master_enter_clear_addresses(fsm);
+        return;
+    }
 
     EC_MASTER_INFO(master, "Scanning bus.\n");
 
     // begin scanning of slaves
     fsm->slave = master->slaves;
+    EC_MASTER_DBG(master, 1, "Scanning slave %u on %s link.\n",
+            fsm->slave->ring_position,
+            ec_device_names[fsm->slave->device_index]);
     fsm->state = ec_fsm_master_state_scan_slave;
     ec_fsm_slave_scan_start(&fsm->fsm_slave_scan, fsm->slave);
     ec_fsm_slave_scan_exec(&fsm->fsm_slave_scan); // execute immediately
+    fsm->datagram->device_index = fsm->slave->device_index;
 }
 
 /*****************************************************************************/
@@ -779,8 +858,10 @@
 #ifdef EC_EOE
     ec_slave_t *slave = fsm->slave;
 #endif
-    if (ec_fsm_slave_scan_exec(&fsm->fsm_slave_scan))
-        return;
+
+    if (ec_fsm_slave_scan_exec(&fsm->fsm_slave_scan)) {
+        return;
+    }
 
 #ifdef EC_EOE
     if (slave->sii.mailbox_protocols & EC_MBOX_EOE) {
@@ -800,8 +881,12 @@
     // another slave to fetch?
     fsm->slave++;
     if (fsm->slave < master->slaves + master->slave_count) {
+        EC_MASTER_DBG(master, 1, "Scanning slave %u on %s link.\n",
+                fsm->slave->ring_position,
+                ec_device_names[fsm->slave->device_index]);
         ec_fsm_slave_scan_start(&fsm->fsm_slave_scan, fsm->slave);
         ec_fsm_slave_scan_exec(&fsm->fsm_slave_scan); // execute immediately
+        fsm->datagram->device_index = fsm->slave->device_index;
         return;
     }
 
@@ -843,8 +928,9 @@
 {
     ec_master_t *master = fsm->master;
 
-    if (ec_fsm_slave_config_exec(&fsm->fsm_slave_config))
-        return;
+    if (ec_fsm_slave_config_exec(&fsm->fsm_slave_config)) {
+        return;
+    }
 
     fsm->slave->force_config = 0;
 
@@ -886,6 +972,7 @@
             //     and time offset (0x0920, 64 bit)
             ec_datagram_fprd(fsm->datagram, fsm->slave->station_address,
                     0x0910, 24);
+            fsm->datagram->device_index = fsm->slave->device_index;
             fsm->retries = EC_FSM_RETRIES;
             fsm->state = ec_fsm_master_state_dc_read_offset;
             return;
@@ -1031,6 +1118,7 @@
     ec_datagram_fpwr(datagram, slave->station_address, 0x0920, 12);
     EC_WRITE_U64(datagram->data, new_offset);
     EC_WRITE_U32(datagram->data + 8, slave->transmission_delay);
+    fsm->datagram->device_index = slave->device_index;
     fsm->retries = EC_FSM_RETRIES;
     fsm->state = ec_fsm_master_state_dc_write_offset;
 }
@@ -1135,7 +1223,9 @@
     ec_slave_t *slave = fsm->slave;
     ec_master_t *master = fsm->master;
 
-    if (ec_fsm_coe_exec(&fsm->fsm_coe)) return;
+    if (ec_fsm_coe_exec(&fsm->fsm_coe)) {
+        return;
+    }
 
     if (!ec_fsm_coe_success(&fsm->fsm_coe)) {
         ec_fsm_master_restart(fsm);
--- a/master/fsm_master.h	Fri Apr 06 23:30:35 2012 +0200
+++ b/master/fsm_master.h	Fri Apr 06 23:35:05 2012 +0200
@@ -115,12 +115,19 @@
     unsigned int retries; /**< retries on datagram timeout. */
 
     void (*state)(ec_fsm_master_t *); /**< master state function */
+    ec_device_index_t dev_idx; /**< Current device index (for scanning etc.).
+                                */
     int idle; /**< state machine is in idle phase */
     unsigned long scan_jiffies; /**< beginning of slave scanning */
-    uint8_t link_state; /**< Last main device link state. */
-    unsigned int slaves_responding; /**< number of responding slaves */
+    uint8_t link_state[EC_NUM_DEVICES]; /**< Last link state for every device.
+                                         */
+    unsigned int slaves_responding[EC_NUM_DEVICES]; /**< Number of responding
+                                                      slaves for every device.
+                                                     */
     unsigned int rescan_required; /**< A bus rescan is required. */
-    ec_slave_state_t slave_states; /**< states of responding slaves */
+    ec_slave_state_t slave_states[EC_NUM_DEVICES]; /**< AL states of
+                                                     responding slaves for
+                                                     every device. */
     ec_slave_t *slave; /**< current slave */
     ec_sii_write_request_t *sii_request; /**< SII write request */
     off_t sii_index; /**< index to SII write request data */
--- a/master/fsm_slave.c	Fri Apr 06 23:30:35 2012 +0200
+++ b/master/fsm_slave.c	Fri Apr 06 23:35:05 2012 +0200
@@ -206,8 +206,7 @@
         fsm->state = ec_fsm_slave_state_sdo_request;
         ec_fsm_coe_transfer(&fsm->fsm_coe, slave, &request->req);
         ec_fsm_coe_exec(&fsm->fsm_coe); // execute immediately
-        ec_master_queue_external_datagram(fsm->slave->master, fsm->datagram,
-                EC_DEVICE_MAIN);
+        ec_master_queue_external_datagram(fsm->slave->master, fsm->datagram);
         return 1;
     }
     return 0;
@@ -225,8 +224,7 @@
     ec_sdo_request_t *request = fsm->sdo_request;
 
     if (ec_fsm_coe_exec(&fsm->fsm_coe)) {
-        ec_master_queue_external_datagram(fsm->slave->master, fsm->datagram,
-                EC_DEVICE_MAIN);
+        ec_master_queue_external_datagram(fsm->slave->master, fsm->datagram);
         return;
     }
     if (!ec_fsm_coe_success(&fsm->fsm_coe)) {
@@ -281,8 +279,7 @@
         fsm->state = ec_fsm_slave_state_foe_request;
         ec_fsm_foe_transfer(&fsm->fsm_foe, slave, &request->req);
         ec_fsm_foe_exec(&fsm->fsm_foe);
-        ec_master_queue_external_datagram(fsm->slave->master, fsm->datagram,
-                EC_DEVICE_MAIN);
+        ec_master_queue_external_datagram(fsm->slave->master, fsm->datagram);
         return 1;
     }
     return 0;
@@ -300,8 +297,7 @@
     ec_foe_request_t *request = fsm->foe_request;
 
     if (ec_fsm_foe_exec(&fsm->fsm_foe)) {
-        ec_master_queue_external_datagram(fsm->slave->master, fsm->datagram,
-                EC_DEVICE_MAIN);
+        ec_master_queue_external_datagram(fsm->slave->master, fsm->datagram);
         return;
     }
 
@@ -369,8 +365,7 @@
         fsm->state = ec_fsm_slave_state_soe_request;
         ec_fsm_soe_transfer(&fsm->fsm_soe, slave, &req->req);
         ec_fsm_soe_exec(&fsm->fsm_soe); // execute immediately
-        ec_master_queue_external_datagram(fsm->slave->master, fsm->datagram,
-                EC_DEVICE_MAIN);
+        ec_master_queue_external_datagram(fsm->slave->master, fsm->datagram);
         return 1;
     }
     return 0;
@@ -388,8 +383,7 @@
     ec_soe_request_t *request = fsm->soe_request;
 
     if (ec_fsm_soe_exec(&fsm->fsm_soe)) {
-        ec_master_queue_external_datagram(fsm->slave->master, fsm->datagram,
-                EC_DEVICE_MAIN);
+        ec_master_queue_external_datagram(fsm->slave->master, fsm->datagram);
         return;
     }
 
--- a/master/fsm_slave_config.c	Fri Apr 06 23:30:35 2012 +0200
+++ b/master/fsm_slave_config.c	Fri Apr 06 23:35:05 2012 +0200
@@ -744,7 +744,7 @@
             ec_fsm_soe_transfer(fsm_soe, fsm->slave, &fsm->soe_request_copy);
             ec_fsm_soe_exec(fsm_soe); // execute immediately
             ec_master_queue_external_datagram(slave->master,
-                    fsm_soe->datagram, EC_DEVICE_MAIN);
+                    fsm_soe->datagram);
             return;
         }
     }
@@ -765,8 +765,7 @@
     ec_fsm_soe_t *fsm_soe = &slave->fsm.fsm_soe;
 
     if (ec_fsm_soe_exec(fsm_soe)) {
-        ec_master_queue_external_datagram(slave->master, fsm_soe->datagram,
-                EC_DEVICE_MAIN);
+        ec_master_queue_external_datagram(slave->master, fsm_soe->datagram);
         return;
     }
 
@@ -792,7 +791,7 @@
             ec_fsm_soe_transfer(fsm_soe, fsm->slave, &fsm->soe_request_copy);
             ec_fsm_soe_exec(fsm_soe); // execute immediately
             ec_master_queue_external_datagram(slave->master,
-                    fsm_soe->datagram, EC_DEVICE_MAIN);
+                    fsm_soe->datagram);
             return;
         }
     }
@@ -1474,7 +1473,7 @@
             ec_fsm_soe_transfer(fsm_soe, fsm->slave, &fsm->soe_request_copy);
             ec_fsm_soe_exec(fsm_soe); // execute immediately
             ec_master_queue_external_datagram(slave->master,
-                    fsm_soe->datagram, EC_DEVICE_MAIN);
+                    fsm_soe->datagram);
             return;
         }
     }
@@ -1495,8 +1494,7 @@
     ec_fsm_soe_t *fsm_soe = &slave->fsm.fsm_soe;
 
     if (ec_fsm_soe_exec(fsm_soe)) {
-        ec_master_queue_external_datagram(slave->master, fsm_soe->datagram,
-                EC_DEVICE_MAIN);
+        ec_master_queue_external_datagram(slave->master, fsm_soe->datagram);
         return;
     }
 
@@ -1522,7 +1520,7 @@
             ec_fsm_soe_transfer(fsm_soe, fsm->slave, &fsm->soe_request_copy);
             ec_fsm_soe_exec(fsm_soe); // execute immediately
             ec_master_queue_external_datagram(slave->master,
-                    fsm_soe->datagram, EC_DEVICE_MAIN);
+                    fsm_soe->datagram);
             return;
         }
     }
--- a/master/globals.h	Fri Apr 06 23:30:35 2012 +0200
+++ b/master/globals.h	Fri Apr 06 23:35:05 2012 +0200
@@ -204,6 +204,8 @@
     EC_NUM_DEVICES /**< Number of devices. */
 } ec_device_index_t;
 
+extern const char *ec_device_names[EC_NUM_DEVICES];
+
 /*****************************************************************************/
 
 /** Convenience macro for printing EtherCAT-specific information to syslog.
--- a/master/ioctl.h	Fri Apr 06 23:30:35 2012 +0200
+++ b/master/ioctl.h	Fri Apr 06 23:35:05 2012 +0200
@@ -56,7 +56,7 @@
  *
  * Increment this when changing the ioctl interface!
  */
-#define EC_IOCTL_VERSION_MAGIC 16
+#define EC_IOCTL_VERSION_MAGIC 17
 
 // Command-line tool
 #define EC_IOCTL_MODULE                EC_IOR(0x00, ec_ioctl_module_t)
@@ -195,6 +195,7 @@
     uint16_t position;
 
     // outputs
+    unsigned int device_index;
     uint32_t vendor_id;
     uint32_t product_code;
     uint32_t revision_number;
--- a/master/master.c	Fri Apr 06 23:30:35 2012 +0200
+++ b/master/master.c	Fri Apr 06 23:35:05 2012 +0200
@@ -554,6 +554,7 @@
         )
 {
     int ret;
+    ec_device_index_t dev_idx;
 
     EC_MASTER_DBG(master, 1, "ORPHANED -> IDLE.\n");
 
@@ -564,7 +565,9 @@
     master->phase = EC_IDLE;
 
     // reset number of responding slaves to trigger scanning
-    master->fsm.slaves_responding = 0;
+    for (dev_idx = EC_DEVICE_MAIN; dev_idx < EC_NUM_DEVICES; dev_idx++) {
+        master->fsm.slaves_responding[dev_idx] = 0;
+    }
 
     ret = ec_master_thread_start(master, ec_master_idle_thread,
             "EtherCAT-IDLE");
@@ -727,8 +730,7 @@
             datagram->cycles_sent = 0;
 #endif
             datagram->jiffies_sent = 0;
-            ec_master_queue_datagram(master, datagram,
-                    datagram->device_index);
+            ec_master_queue_datagram(master, datagram);
         } else {
             if (datagram->data_size > master->max_queue_size) {
                 list_del_init(&datagram->queue);
@@ -800,8 +802,7 @@
  */
 void ec_master_queue_external_datagram(
         ec_master_t *master, /**< EtherCAT master */
-        ec_datagram_t *datagram, /**< datagram */
-        ec_device_index_t device_index /**< Device index. */
+        ec_datagram_t *datagram /**< datagram */
         )
 {
     ec_datagram_t *queued_datagram;
@@ -824,7 +825,6 @@
 
     list_add_tail(&datagram->queue, &master->external_datagram_queue);
     datagram->state = EC_DATAGRAM_QUEUED;
-    datagram->device_index = device_index;
 #ifdef EC_HAVE_CYCLES
     datagram->cycles_sent = get_cycles();
 #endif
@@ -840,8 +840,7 @@
  */
 void ec_master_queue_datagram(
         ec_master_t *master, /**< EtherCAT master */
-        ec_datagram_t *datagram, /**< datagram */
-        ec_device_index_t device_index /**< Device index. */
+        ec_datagram_t *datagram /**< datagram */
         )
 {
     ec_datagram_t *queued_datagram;
@@ -881,7 +880,6 @@
 
     list_add_tail(&datagram->queue, &master->datagram_queue);
     datagram->state = EC_DATAGRAM_QUEUED;
-    datagram->device_index = device_index;
 }
 
 /*****************************************************************************/
@@ -1388,8 +1386,7 @@
         // queue and send
         down(&master->io_sem);
         if (fsm_exec) {
-            ec_master_queue_datagram(master, &master->fsm_datagram,
-                    EC_DEVICE_MAIN);
+            ec_master_queue_datagram(master, &master->fsm_datagram);
         }
         ecrt_master_send(master);
 #ifdef EC_USE_HRTIMER
@@ -2231,42 +2228,41 @@
 void ecrt_master_send(ec_master_t *master)
 {
     ec_datagram_t *datagram, *n;
-    unsigned int i;
+    ec_device_index_t dev_idx;
 
     if (master->injection_seq_rt != master->injection_seq_fsm) {
-        // inject datagrams produced by master & slave FSMs
-        ec_master_queue_datagram(master, &master->fsm_datagram,
-                EC_DEVICE_MAIN);
+        // inject datagrams produced by master and slave FSMs
+        ec_master_queue_datagram(master, &master->fsm_datagram);
         master->injection_seq_rt = master->injection_seq_fsm;
     }
 
     ec_master_inject_external_datagrams(master);
 
-    for (i = 0; i < EC_NUM_DEVICES; i++) {
-        if (unlikely(!master->devices[i].link_state)) {
+    for (dev_idx = EC_DEVICE_MAIN; dev_idx < EC_NUM_DEVICES; dev_idx++) {
+        if (unlikely(!master->devices[dev_idx].link_state)) {
             // link is down, no datagram can be sent
             list_for_each_entry_safe(datagram, n,
                     &master->datagram_queue, queue) {
-                if (datagram->device_index == i) {
+                if (datagram->device_index == dev_idx) {
                     datagram->state = EC_DATAGRAM_ERROR;
                     list_del_init(&datagram->queue);
                 }
             }
 
-            if (!master->devices[i].dev) {
+            if (!master->devices[dev_idx].dev) {
                 continue;
             }
 
             // query link state
-            ec_device_poll(&master->devices[i]);
+            ec_device_poll(&master->devices[dev_idx]);
 
             // clear frame statistics
-            ec_device_clear_stats(&master->devices[i]);
+            ec_device_clear_stats(&master->devices[dev_idx]);
             continue;
         }
 
         // send frames
-        ec_master_send_datagrams(master, i);
+        ec_master_send_datagrams(master, dev_idx);
     }
 }
 
@@ -2327,7 +2323,7 @@
     list_for_each_entry_safe(datagram, next, &master->ext_datagram_queue,
             queue) {
         list_del(&datagram->queue);
-        ec_master_queue_datagram(master, datagram, EC_DEVICE_MAIN);
+        ec_master_queue_datagram(master, datagram);
     }
 
     ecrt_master_send(master);
@@ -2491,9 +2487,22 @@
 
 void ecrt_master_state(const ec_master_t *master, ec_master_state_t *state)
 {
-    state->slaves_responding = master->fsm.slaves_responding;
-    state->al_states = master->fsm.slave_states;
-    state->link_up = master->devices[EC_DEVICE_MAIN].link_state;
+    ec_device_index_t dev_idx;
+
+    state->slaves_responding = 0U;
+    state->al_states = 0;
+    state->link_up = 0U;
+
+    for (dev_idx = EC_DEVICE_MAIN; dev_idx < EC_NUM_DEVICES; dev_idx++) {
+        /* Announce sum of responding slaves on all links. */
+        state->slaves_responding += master->fsm.slaves_responding[dev_idx];
+
+        /* Binary-or slave states of all links. */
+        state->al_states |= master->fsm.slave_states[dev_idx];
+
+        /* Signal link up if at least one device has link. */
+        state->link_up |= master->devices[dev_idx].link_state;
+    }
 }
 
 /*****************************************************************************/
@@ -2513,8 +2522,7 @@
 void ecrt_master_sync_reference_clock(ec_master_t *master)
 {
     EC_WRITE_U32(master->ref_sync_datagram.data, master->app_time);
-    ec_master_queue_datagram(master, &master->ref_sync_datagram,
-            EC_DEVICE_MAIN);
+    ec_master_queue_datagram(master, &master->ref_sync_datagram);
 }
 
 /*****************************************************************************/
@@ -2522,8 +2530,7 @@
 void ecrt_master_sync_slave_clocks(ec_master_t *master)
 {
     ec_datagram_zero(&master->sync_datagram);
-    ec_master_queue_datagram(master, &master->sync_datagram,
-            EC_DEVICE_MAIN);
+    ec_master_queue_datagram(master, &master->sync_datagram);
 }
 
 /*****************************************************************************/
@@ -2531,8 +2538,7 @@
 void ecrt_master_sync_monitor_queue(ec_master_t *master)
 {
     ec_datagram_zero(&master->sync_mon_datagram);
-    ec_master_queue_datagram(master, &master->sync_mon_datagram,
-            EC_DEVICE_MAIN);
+    ec_master_queue_datagram(master, &master->sync_mon_datagram);
 }
 
 /*****************************************************************************/
--- a/master/master.h	Fri Apr 06 23:30:35 2012 +0200
+++ b/master/master.h	Fri Apr 06 23:35:05 2012 +0200
@@ -297,11 +297,9 @@
 
 // datagram IO
 void ec_master_receive_datagrams(ec_master_t *, const uint8_t *, size_t);
-void ec_master_queue_datagram(ec_master_t *, ec_datagram_t *,
-        ec_device_index_t);
+void ec_master_queue_datagram(ec_master_t *, ec_datagram_t *);
 void ec_master_queue_datagram_ext(ec_master_t *, ec_datagram_t *);
-void ec_master_queue_external_datagram(ec_master_t *, ec_datagram_t *,
-        ec_device_index_t);
+void ec_master_queue_external_datagram(ec_master_t *, ec_datagram_t *);
 void ec_master_inject_external_datagrams(ec_master_t *);
 
 // misc.
--- a/master/module.c	Fri Apr 06 23:30:35 2012 +0200
+++ b/master/module.c	Fri Apr 06 23:35:05 2012 +0200
@@ -453,7 +453,7 @@
 
 /** Device names.
  */
-static const char *ec_device_names[EC_NUM_DEVICES] = {
+const char *ec_device_names[EC_NUM_DEVICES] = {
     "main",
     "backup"
 };
--- a/master/slave.c	Fri Apr 06 23:30:35 2012 +0200
+++ b/master/slave.c	Fri Apr 06 23:35:05 2012 +0200
@@ -62,6 +62,7 @@
 void ec_slave_init(
         ec_slave_t *slave, /**< EtherCAT slave */
         ec_master_t *master, /**< EtherCAT master */
+        ec_device_index_t dev_idx, /**< Device index. */
         uint16_t ring_position, /**< ring position */
         uint16_t station_address /**< station address to configure */
         )
@@ -70,6 +71,7 @@
     int ret;
 
     slave->master = master;
+    slave->device_index = dev_idx;
     slave->ring_position = ring_position;
     slave->station_address = station_address;
     slave->effective_alias = 0x0000;
--- a/master/slave.h	Fri Apr 06 23:30:35 2012 +0200
+++ b/master/slave.h	Fri Apr 06 23:35:05 2012 +0200
@@ -174,6 +174,8 @@
 struct ec_slave
 {
     ec_master_t *master; /**< Master owning the slave. */
+    ec_device_index_t device_index; /**< Index of device the slave responds
+                                      on. */
 
     // addresses
     uint16_t ring_position; /**< Ring position. */
@@ -238,7 +240,8 @@
 /*****************************************************************************/
 
 // slave construction/destruction
-void ec_slave_init(ec_slave_t *, ec_master_t *, uint16_t, uint16_t);
+void ec_slave_init(ec_slave_t *, ec_master_t *, ec_device_index_t,
+        uint16_t, uint16_t);
 void ec_slave_clear(ec_slave_t *);
 
 void ec_slave_clear_sync_managers(ec_slave_t *);
--- a/master/voe_handler.c	Fri Apr 06 23:30:35 2012 +0200
+++ b/master/voe_handler.c	Fri Apr 06 23:35:05 2012 +0200
@@ -190,9 +190,9 @@
 {
     if (voe->config->slave) { // FIXME locking?
         voe->state(voe);
-        if (voe->request_state == EC_INT_REQUEST_BUSY)
-            ec_master_queue_datagram(voe->config->master, &voe->datagram,
-                    EC_DEVICE_MAIN);
+        if (voe->request_state == EC_INT_REQUEST_BUSY) {
+            ec_master_queue_datagram(voe->config->master, &voe->datagram);
+        }
     } else {
         voe->state = ec_voe_handler_state_error;
         voe->request_state = EC_INT_REQUEST_FAILURE;
--- a/tool/CommandSlaves.cpp	Fri Apr 06 23:30:35 2012 +0200
+++ b/tool/CommandSlaves.cpp	Fri Apr 06 23:35:05 2012 +0200
@@ -144,7 +144,7 @@
         )
 {
     ec_ioctl_master_t master;
-    unsigned int i;
+    unsigned int i, lastDevice;
     ec_ioctl_slave_t slave;
     uint16_t lastAlias, aliasIndex;
     Info info;
@@ -184,6 +184,7 @@
 
             info.state = alStateString(slave.al_state);
             info.flag = (slave.error_flag ? 'E' : '+');
+            info.device = slave.device_index;
 
             if (strlen(slave.name)) {
                 info.name = slave.name;
@@ -215,7 +216,12 @@
         cout << "Master" << dec << m.getIndex() << endl;
     }
 
+    lastDevice = EC_DEVICE_MAIN;
     for (iter = infoList.begin(); iter != infoList.end(); iter++) {
+        if (iter->device != lastDevice) {
+            lastDevice = iter->device;
+            cout << "xxx LINK FAILURE xxx" << endl;
+        }
         cout << indent << setfill(' ') << right
             << setw(maxPosWidth) << iter->pos << "  "
             << setw(maxAliasWidth) << iter->alias
@@ -245,6 +251,7 @@
             cout << "Alias: " << si->alias << endl;
 
         cout
+            << "Device: " << (si->device_index ? "Backup" : "Main") << endl
             << "State: " << alStateString(si->al_state) << endl
             << "Flag: " << (si->error_flag ? 'E' : '+') << endl
             << "Identity:" << endl
@@ -332,7 +339,8 @@
                 }
                 cout << "  " << setw(10);
                 if (!si->ports[i].link.loop_closed) {
-                    cout << si->ports[i].receive_time - si->ports[0].receive_time;
+                    cout << si->ports[i].receive_time -
+                        si->ports[0].receive_time;
                 } else {
                     cout << "-";
                 }
@@ -406,7 +414,8 @@
                     << "    Enable SDO: "
                     << (si->coe_details.enable_sdo ? "yes" : "no") << endl
                     << "    Enable SDO Info: "
-                    << (si->coe_details.enable_sdo_info ? "yes" : "no") << endl
+                    << (si->coe_details.enable_sdo_info ? "yes" : "no")
+                    << endl
                     << "    Enable PDO Assign: "
                     << (si->coe_details.enable_pdo_assign
                             ? "yes" : "no") << endl
--- a/tool/CommandSlaves.h	Fri Apr 06 23:30:35 2012 +0200
+++ b/tool/CommandSlaves.h	Fri Apr 06 23:35:05 2012 +0200
@@ -51,6 +51,7 @@
             string state;
             string flag;
             string name;
+            unsigned int device;
         };
 
         void listSlaves(MasterDevice &, const SlaveList &, bool);