Added configure option for redundancy (--with-devices). stable-1.5
authorFlorian Pose <fp@igh-essen.com>
Fri, 30 Nov 2012 20:15:31 +0100
branchstable-1.5
changeset 2453 d461b1f07296
parent 2452 abc1d1caead7
child 2454 5cf6bb14a579
Added configure option for redundancy (--with-devices).
TODO
configure.ac
master/datagram_pair.c
master/datagram_pair.h
master/device.c
master/domain.c
master/domain.h
master/fsm_master.c
master/fsm_master.h
master/globals.h
master/ioctl.c
master/ioctl.h
master/master.c
master/master.h
master/module.c
tool/Command.cpp
tool/Command.h
tool/CommandData.cpp
tool/CommandDomains.cpp
tool/CommandDomains.h
tool/CommandMaster.cpp
--- a/TODO	Fri Nov 30 15:24:38 2012 +0100
+++ b/TODO	Fri Nov 30 20:15:31 2012 +0100
@@ -8,6 +8,15 @@
 
 -------------------------------------------------------------------------------
 
+Version 1.5.2:
+
+• Try redundancy with more then 2 devices.
+• Document --with-devices.
+• Document RTDM interface.
+• Set the "Cyclic Generation Start Time" based on the slave's
+  "System Time" register instead of using the application time.
+• Add ecrt_slave_config_reg_pdo_entry_by_pos().
+
 Future issues:
 
 * Fix link detection in generic driver.
@@ -19,7 +28,6 @@
 * Only output watchdog config if not default.
 * Implement CompleteAccess for SDO uploads.
 * Output warning when send_ext() is called in illegal context.
-* Change SDO index at runtime for SDO request.
 * Implement ecrt_slave_config_request_state().
 * Remove default buffer size in SDO upload.
 * Override sync manager size?
@@ -29,8 +37,6 @@
     - Skip setting system time offset when application detached.
     - How to use the SYNC1 shift time?
     - Do not output graph, if topology calculation failed.
-    - Set the "Cyclic Generation Start Time" based on the slave's
-      "System Time" register instead of using the application time.
     - Check if register 0x0980 is working, to avoid clearing it when
       configuring.
 * Mailbox protocol handlers.
@@ -43,7 +49,6 @@
 * Separate CoE debugging.
 * Evaluate EEPROM contents after writing.
 * Optimize alignment of process data.
-* Redundancy with 2 network adapters.
 * Interface/buffers for asynchronous domain IO.
 * Make scanning and configuration run parallel (each).
 * ethercat tool:
--- a/configure.ac	Fri Nov 30 15:24:38 2012 +0100
+++ b/configure.ac	Fri Nov 30 20:15:31 2012 +0100
@@ -782,6 +782,38 @@
 fi
 
 #------------------------------------------------------------------------------
+# Redundancy (number of devices)
+#------------------------------------------------------------------------------
+
+AC_ARG_WITH([devices],
+    AC_HELP_STRING(
+        [--with-devices=<NUMBER>],
+        [Number of Ethernet devices per master. Default: 1]
+    ),
+    [
+        devices=[$withval]
+    ],
+    [
+        devices=1
+    ]
+)
+
+AC_MSG_CHECKING([for number of Ethernet devices])
+
+if test "${devices}" -lt 1; then
+    AC_MSG_ERROR([Number must be greater zero!])
+else
+    if test "${devices}" -gt 1; then
+        AC_MSG_RESULT([$devices (Redundancy enabled)])
+    else
+        AC_MSG_RESULT([$devices (Redundancy disabled)])
+    fi
+fi
+
+AC_DEFINE_UNQUOTED([EC_MAX_NUM_DEVICES], $devices,
+    [Max. number of Ethernet devices per master])
+
+#------------------------------------------------------------------------------
 
 AC_CONFIG_FILES([
         Doxyfile
--- a/master/datagram_pair.c	Fri Nov 30 15:24:38 2012 +0100
+++ b/master/datagram_pair.c	Fri Nov 30 20:15:31 2012 +0100
@@ -58,29 +58,34 @@
     INIT_LIST_HEAD(&pair->list);
     pair->domain = domain;
 
-    for (dev_idx = EC_DEVICE_MAIN; dev_idx < EC_NUM_DEVICES; dev_idx++) {
+    for (dev_idx = EC_DEVICE_MAIN;
+            dev_idx < ec_master_num_devices(domain->master); 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, ec_device_names[dev_idx]);
+                logical_offset, ec_device_names[dev_idx != 0]);
         pair->datagrams[dev_idx].device_index = dev_idx;
     }
 
     pair->expected_working_counter = 0U;
 
-    /* backup datagram has its own memory */
-    ret = ec_datagram_prealloc(&pair->datagrams[EC_DEVICE_BACKUP],
-            data_size);
-    if (ret) {
-        goto out_datagrams;
+    for (dev_idx = EC_DEVICE_BACKUP;
+            dev_idx < ec_master_num_devices(domain->master); dev_idx++) {
+        /* backup datagrams have their own memory */
+        ret = ec_datagram_prealloc(&pair->datagrams[dev_idx], data_size);
+        if (ret) {
+            goto out_datagrams;
+        }
     }
 
+#if EC_MAX_NUM_DEVICES > 1
     if (!(pair->send_buffer = kmalloc(data_size, GFP_KERNEL))) {
         EC_MASTER_ERR(domain->master,
                 "Failed to allocate domain send buffer!\n");
         ret = -ENOMEM;
         goto out_datagrams;
     }
+#endif
 
     /* The ec_datagram_lxx() calls below can not fail, because either the
      * datagram has external memory or it is preallocated. */
@@ -88,8 +93,12 @@
     if (used[EC_DIR_OUTPUT] && used[EC_DIR_INPUT]) { // inputs and outputs
         ec_datagram_lrw_ext(&pair->datagrams[EC_DEVICE_MAIN],
                 logical_offset, data_size, data);
-        ec_datagram_lrw(&pair->datagrams[EC_DEVICE_BACKUP],
-                logical_offset, data_size);
+
+        for (dev_idx = EC_DEVICE_BACKUP;
+                dev_idx < ec_master_num_devices(domain->master); dev_idx++) {
+            ec_datagram_lrw(&pair->datagrams[dev_idx],
+                    logical_offset, data_size);
+        }
 
         // If LRW is used, output FMMUs increment the working counter by 2,
         // while input FMMUs increment it by 1.
@@ -98,27 +107,35 @@
     } else if (used[EC_DIR_OUTPUT]) { // outputs only
         ec_datagram_lwr_ext(&pair->datagrams[EC_DEVICE_MAIN],
                 logical_offset, data_size, data);
-        ec_datagram_lwr(&pair->datagrams[EC_DEVICE_BACKUP],
-                logical_offset, data_size);
+        for (dev_idx = EC_DEVICE_BACKUP;
+                dev_idx < ec_master_num_devices(domain->master); dev_idx++) {
+            ec_datagram_lwr(&pair->datagrams[dev_idx],
+                    logical_offset, data_size);
+        }
 
         pair->expected_working_counter = used[EC_DIR_OUTPUT];
     } else { // inputs only (or nothing)
         ec_datagram_lrd_ext(&pair->datagrams[EC_DEVICE_MAIN],
                 logical_offset, data_size, data);
-        ec_datagram_lrd(&pair->datagrams[EC_DEVICE_BACKUP],
-                logical_offset, data_size);
+        for (dev_idx = EC_DEVICE_BACKUP;
+                dev_idx < ec_master_num_devices(domain->master); dev_idx++) {
+            ec_datagram_lrd(&pair->datagrams[dev_idx], logical_offset,
+                    data_size);
+        }
 
         pair->expected_working_counter = used[EC_DIR_INPUT];
     }
 
-    for (dev_idx = 0; dev_idx < EC_NUM_DEVICES; dev_idx++) {
+    for (dev_idx = EC_DEVICE_MAIN;
+            dev_idx < ec_master_num_devices(domain->master); dev_idx++) {
         ec_datagram_zero(&pair->datagrams[dev_idx]);
     }
 
     return 0;
 
 out_datagrams:
-    for (dev_idx = 0; dev_idx < EC_NUM_DEVICES; dev_idx++) {
+    for (dev_idx = EC_DEVICE_MAIN;
+            dev_idx < ec_master_num_devices(domain->master); dev_idx++) {
         ec_datagram_clear(&pair->datagrams[dev_idx]);
     }
 
@@ -135,13 +152,17 @@
 {
     unsigned int dev_idx;
 
-    for (dev_idx = 0; dev_idx < EC_NUM_DEVICES; dev_idx++) {
+    for (dev_idx = EC_DEVICE_MAIN;
+            dev_idx < ec_master_num_devices(pair->domain->master);
+            dev_idx++) {
         ec_datagram_clear(&pair->datagrams[dev_idx]);
     }
 
+#if EC_MAX_NUM_DEVICES > 1
     if (pair->send_buffer) {
         kfree(pair->send_buffer);
     }
+#endif
 }
 
 /*****************************************************************************/
@@ -150,13 +171,14 @@
  */
 uint16_t ec_datagram_pair_process(
         ec_datagram_pair_t *pair, /**< Datagram pair. */
-        uint16_t wc_sum[EC_NUM_DEVICES] /**< Working counter sums. */
+        uint16_t wc_sum[] /**< Working counter sums. */
         )
 {
     unsigned int dev_idx;
     uint16_t pair_wc = 0;
 
-    for (dev_idx = 0; dev_idx < EC_NUM_DEVICES; dev_idx++) {
+    for (dev_idx = 0; dev_idx < ec_master_num_devices(pair->domain->master);
+            dev_idx++) {
         ec_datagram_t *datagram = &pair->datagrams[dev_idx];
 
         ec_datagram_output_stats(datagram);
--- a/master/datagram_pair.h	Fri Nov 30 15:24:38 2012 +0100
+++ b/master/datagram_pair.h	Fri Nov 30 20:15:31 2012 +0100
@@ -49,9 +49,10 @@
 typedef struct {
     struct list_head list; /**< List header. */
     ec_domain_t *domain;
-    ec_datagram_t datagrams[EC_NUM_DEVICES]; /**< Main and backup datagram.
-                                               */
+    ec_datagram_t datagrams[EC_MAX_NUM_DEVICES]; /**< Datagrams.  */
+#if EC_MAX_NUM_DEVICES > 1
     uint8_t *send_buffer;
+#endif
     unsigned int expected_working_counter; /**< Expectord working conter. */
 } ec_datagram_pair_t;
 
@@ -61,8 +62,7 @@
         uint8_t *, size_t, const unsigned int []);
 void ec_datagram_pair_clear(ec_datagram_pair_t *);
 
-uint16_t ec_datagram_pair_process(ec_datagram_pair_t *,
-        uint16_t[EC_NUM_DEVICES]);
+uint16_t ec_datagram_pair_process(ec_datagram_pair_t *, uint16_t[]);
 
 /*****************************************************************************/
 
--- a/master/device.c	Fri Nov 30 15:24:38 2012 +0100
+++ b/master/device.c	Fri Nov 30 20:15:31 2012 +0100
@@ -81,10 +81,12 @@
     device->tx_ring_index = 0;
 
 #ifdef EC_DEBUG_IF
-    if (device == &master->main_device)
+    if (device == &master->devices[EC_DEVICE_MAIN]) {
         mb = 'm';
-    else if (device == &master->backup_device)
+    }
+    else {
         mb = 'b';
+    }
 
     sprintf(ifname, "ecdbg%c%u", mb, master->index);
 
@@ -136,7 +138,9 @@
 {
     unsigned int i;
 
-    if (device->open) ec_device_close(device);
+    if (device->open) {
+        ec_device_close(device);
+    }
     for (i = 0; i < EC_TX_RING_SIZE; i++)
         dev_kfree_skb(device->tx_skb[i]);
 #ifdef EC_DEBUG_IF
@@ -541,6 +545,7 @@
 {
     int ret;
     ec_master_t *master = device->master;
+    unsigned int all_open = 1, dev_idx;
 
     ret = ec_device_open(device);
     if (ret) {
@@ -548,9 +553,15 @@
         return ret;
     }
 
-    if (master->devices[EC_DEVICE_MAIN].open &&
-            (ec_mac_is_zero(master->macs[EC_DEVICE_BACKUP]) ||
-             master->devices[EC_DEVICE_BACKUP].open)) {
+    for (dev_idx = EC_DEVICE_MAIN;
+            dev_idx < ec_master_num_devices(device->master); dev_idx++) {
+        if (!master->devices[dev_idx].open) {
+            all_open = 0;
+            break;
+        }
+    }
+
+    if (all_open) {
         ret = ec_master_enter_idle_phase(device->master);
         if (ret) {
             EC_MASTER_ERR(device->master, "Failed to enter IDLE phase!\n");
--- a/master/domain.c	Fri Nov 30 15:24:38 2012 +0100
+++ b/master/domain.c	Fri Nov 30 20:15:31 2012 +0100
@@ -59,6 +59,8 @@
         unsigned int index /**< Index. */
         )
 {
+    unsigned int dev_idx;
+
     domain->master = master;
     domain->index = index;
     INIT_LIST_HEAD(&domain->fmmu_configs);
@@ -67,8 +69,10 @@
     domain->data_origin = EC_ORIG_INTERNAL;
     domain->logical_base_address = 0x00000000;
     INIT_LIST_HEAD(&domain->datagram_pairs);
-    domain->working_counter[EC_DEVICE_MAIN] = 0x0000;
-    domain->working_counter[EC_DEVICE_BACKUP] = 0x0000;
+    for (dev_idx = EC_DEVICE_MAIN; dev_idx < ec_master_num_devices(master);
+            dev_idx++) {
+        domain->working_counter[dev_idx] = 0x0000;
+    }
     domain->expected_working_counter = 0x0000;
     domain->working_counter_changes = 0;
     domain->redundancy_active = 0;
@@ -355,6 +359,8 @@
 
 /*****************************************************************************/
 
+#if EC_MAX_NUM_DEVICES > 1
+
 /** Process received data.
  */
 int data_changed(
@@ -377,6 +383,8 @@
     return 0;
 }
 
+#endif
+
 /******************************************************************************
  *  Application interface
  *****************************************************************************/
@@ -443,15 +451,17 @@
 
 void ecrt_domain_process(ec_domain_t *domain)
 {
-    uint16_t wc_sum[EC_NUM_DEVICES] = {};
+    uint16_t wc_sum[EC_MAX_NUM_DEVICES] = {}, redundant_wc, wc_total;
     ec_datagram_pair_t *pair;
-    ec_datagram_t *main_datagram, *backup_datagram;
+    ec_datagram_t *main_datagram;
     uint32_t logical_datagram_address;
     size_t datagram_size;
     uint16_t datagram_pair_wc;
-    unsigned int datagram_offset;
+    unsigned int dev_idx, wc_change;
+#if EC_MAX_NUM_DEVICES > 1
     ec_fmmu_config_t *fmmu =
         list_first_entry(&domain->fmmu_configs, ec_fmmu_config_t, list);
+#endif
     unsigned int redundancy;
 
 #if DEBUG_REDUNDANCY
@@ -461,7 +471,6 @@
     list_for_each_entry(pair, &domain->datagram_pairs, list) {
 
         main_datagram = &pair->datagrams[EC_DEVICE_MAIN];
-        backup_datagram = &pair->datagrams[EC_DEVICE_BACKUP];
         logical_datagram_address = EC_READ_U32(main_datagram->address);
         datagram_size = main_datagram->data_size;
 
@@ -472,8 +481,16 @@
 
         datagram_pair_wc = ec_datagram_pair_process(pair, wc_sum);
 
-        /* Go through all FMMU configs to detect data changes. */
+#if EC_MAX_NUM_DEVICES > 1
+        if (ec_master_num_devices(domain->master) < 2) {
+            continue;
+        }
+
+        /* Redundancy: Go through all FMMU configs to detect data changes. */
         list_for_each_entry_from(fmmu, &domain->fmmu_configs, list) {
+            unsigned int datagram_offset;
+            ec_datagram_t *backup_datagram =
+                &pair->datagrams[EC_DEVICE_BACKUP];
 
             if (fmmu->dir != EC_DIR_INPUT) {
                 continue;
@@ -533,9 +550,16 @@
 #endif
             }
         }
-    }
-
-    redundancy = wc_sum[EC_DEVICE_BACKUP] > 0;
+#endif // EC_MAX_NUM_DEVICES > 1
+    }
+
+    redundant_wc = 0;
+    for (dev_idx = EC_DEVICE_BACKUP;
+            dev_idx < ec_master_num_devices(domain->master); dev_idx++) {
+        redundant_wc += wc_sum[dev_idx];
+    }
+
+    redundancy = redundant_wc > 0;
     if (redundancy != domain->redundancy_active) {
         if (redundancy) {
             EC_MASTER_WARN(domain->master,
@@ -549,12 +573,19 @@
         domain->redundancy_active = redundancy;
     }
 
-    if ((wc_sum[EC_DEVICE_MAIN] != domain->working_counter[EC_DEVICE_MAIN])
-            || (wc_sum[EC_DEVICE_BACKUP]
-                != domain->working_counter[EC_DEVICE_BACKUP])) {
+    wc_change = 0;
+    wc_total = 0;
+    for (dev_idx = EC_DEVICE_MAIN;
+            dev_idx < ec_master_num_devices(domain->master); dev_idx++) {
+        if (wc_sum[dev_idx] != domain->working_counter[dev_idx]) {
+            wc_change = 1;
+            domain->working_counter[dev_idx] = wc_sum[dev_idx];
+        }
+        wc_total += wc_sum[dev_idx];
+    }
+
+    if (wc_change) {
         domain->working_counter_changes++;
-        domain->working_counter[EC_DEVICE_MAIN] = wc_sum[EC_DEVICE_MAIN];
-        domain->working_counter[EC_DEVICE_BACKUP] = wc_sum[EC_DEVICE_BACKUP];
     }
 
     if (domain->working_counter_changes &&
@@ -562,20 +593,28 @@
         domain->notify_jiffies = jiffies;
         if (domain->working_counter_changes == 1) {
             EC_MASTER_INFO(domain->master, "Domain %u: Working counter"
-                    " changed to %u/%u (%u+%u).\n", domain->index,
-                    domain->working_counter[EC_DEVICE_MAIN] +
-                    domain->working_counter[EC_DEVICE_BACKUP],
-                    domain->expected_working_counter,
-                    wc_sum[EC_DEVICE_MAIN], wc_sum[EC_DEVICE_BACKUP]);
+                    " changed to %u/%u", domain->index,
+                    wc_total, domain->expected_working_counter);
         } else {
             EC_MASTER_INFO(domain->master, "Domain %u: %u working counter"
-                    " changes - now %u/%u (%u+%u).\n", domain->index,
+                    " changes - now %u/%u", domain->index,
                     domain->working_counter_changes,
-                    domain->working_counter[EC_DEVICE_MAIN] +
-                    domain->working_counter[EC_DEVICE_BACKUP],
-                    domain->expected_working_counter,
-                    wc_sum[EC_DEVICE_MAIN], wc_sum[EC_DEVICE_BACKUP]);
-        }
+                    wc_total, domain->expected_working_counter);
+        }
+        if (ec_master_num_devices(domain->master) > 1) {
+            printk(" (");
+            for (dev_idx = EC_DEVICE_MAIN;
+                    dev_idx < ec_master_num_devices(domain->master);
+                    dev_idx++) {
+                printk("%u", domain->working_counter[dev_idx]);
+                if (dev_idx + 1 < ec_master_num_devices(domain->master)) {
+                    printk("+");
+                }
+            }
+            printk(")");
+        }
+        printk(".\n");
+
         domain->working_counter_changes = 0;
     }
 }
@@ -589,17 +628,21 @@
 
     list_for_each_entry(datagram_pair, &domain->datagram_pairs, list) {
 
+#if EC_MAX_NUM_DEVICES > 1
         /* copy main data to send buffer */
         memcpy(datagram_pair->send_buffer,
                 datagram_pair->datagrams[EC_DEVICE_MAIN].data,
                 datagram_pair->datagrams[EC_DEVICE_MAIN].data_size);
+#endif
+        ec_master_queue_datagram(domain->master,
+                &datagram_pair->datagrams[EC_DEVICE_MAIN]);
 
         /* copy main data to backup datagram */
-        memcpy(datagram_pair->datagrams[EC_DEVICE_BACKUP].data,
-                datagram_pair->datagrams[EC_DEVICE_MAIN].data,
-                datagram_pair->datagrams[EC_DEVICE_MAIN].data_size);
-
-        for (dev_idx = EC_DEVICE_MAIN; dev_idx < EC_NUM_DEVICES; dev_idx++) {
+        for (dev_idx = EC_DEVICE_BACKUP;
+                dev_idx < ec_master_num_devices(domain->master); dev_idx++) {
+            memcpy(datagram_pair->datagrams[dev_idx].data,
+                    datagram_pair->datagrams[EC_DEVICE_MAIN].data,
+                    datagram_pair->datagrams[EC_DEVICE_MAIN].data_size);
             ec_master_queue_datagram(domain->master,
                     &datagram_pair->datagrams[dev_idx]);
         }
@@ -610,9 +653,15 @@
 
 void ecrt_domain_state(const ec_domain_t *domain, ec_domain_state_t *state)
 {
-    state->working_counter =
-        domain->working_counter[EC_DEVICE_MAIN]
-        + domain->working_counter[EC_DEVICE_BACKUP];
+    unsigned int dev_idx;
+    uint16_t wc = 0;
+
+    for (dev_idx = EC_DEVICE_MAIN;
+            dev_idx < ec_master_num_devices(domain->master); dev_idx++) {
+        wc += domain->working_counter[dev_idx];
+    }
+
+    state->working_counter = wc;
 
     if (state->working_counter) {
         if (state->working_counter == domain->expected_working_counter) {
--- a/master/domain.h	Fri Nov 30 15:24:38 2012 +0100
+++ b/master/domain.h	Fri Nov 30 20:15:31 2012 +0100
@@ -65,8 +65,7 @@
                                      process data. */
     struct list_head datagram_pairs; /**< Datagrams pairs (main/backup) for
                                        process data exchange. */
-
-    uint16_t working_counter[EC_NUM_DEVICES]; /**< Last working counter
+    uint16_t working_counter[EC_MAX_NUM_DEVICES]; /**< Last working counter
                                                 values. */
     uint16_t expected_working_counter; /**< Expected working counter. */
     unsigned int working_counter_changes; /**< Working counter changes
--- a/master/fsm_master.c	Fri Nov 30 15:24:38 2012 +0100
+++ b/master/fsm_master.c	Fri Nov 30 20:15:31 2012 +0100
@@ -86,7 +86,8 @@
     fsm->state = ec_fsm_master_state_start;
     fsm->idle = 0;
     fsm->dev_idx = EC_DEVICE_MAIN;
-    for (dev_idx = EC_DEVICE_MAIN; dev_idx < EC_NUM_DEVICES; dev_idx++) {
+    for (dev_idx = EC_DEVICE_MAIN; dev_idx < ec_master_num_devices(master);
+            dev_idx++) {
         fsm->link_state[dev_idx] = 0;
         fsm->slaves_responding[dev_idx] = 0;
         fsm->slave_states[dev_idx] = EC_SLAVE_STATE_UNKNOWN;
@@ -209,7 +210,7 @@
         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]);
+                ec_device_names[fsm->dev_idx != 0]);
     }
 
     if (fsm->link_state[fsm->dev_idx] &&
@@ -218,7 +219,7 @@
 
         EC_MASTER_DBG(master, 1, "Master state machine detected "
                 "link down on %s device. Clearing slave list.\n",
-                ec_device_names[fsm->dev_idx]);
+                ec_device_names[fsm->dev_idx != 0]);
 
 #ifdef EC_EOE
         ec_master_eoe_stop(master);
@@ -226,7 +227,8 @@
 #endif
         ec_master_clear_slaves(master);
 
-        for (dev_idx = EC_DEVICE_MAIN; dev_idx < EC_NUM_DEVICES; dev_idx++) {
+        for (dev_idx = EC_DEVICE_MAIN;
+                dev_idx < ec_master_num_devices(master); dev_idx++) {
             fsm->slave_states[dev_idx] = 0x00;
             fsm->slaves_responding[dev_idx] = 0; /* Reset to trigger rescan on
                                                     next link up. */
@@ -243,14 +245,14 @@
             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);
+                    ec_device_names[fsm->dev_idx != 0], state_str);
         }
     } else {
         fsm->slave_states[fsm->dev_idx] = 0x00;
     }
 
     fsm->dev_idx++;
-    if (fsm->dev_idx < EC_NUM_DEVICES) {
+    if (fsm->dev_idx < ec_master_num_devices(master)) {
         // check number of responding slaves on next device
         fsm->state = ec_fsm_master_state_start;
         fsm->state(fsm); // execute immediately
@@ -279,8 +281,8 @@
 #endif
             ec_master_clear_slaves(master);
 
-            for (dev_idx = EC_DEVICE_MAIN; dev_idx < EC_NUM_DEVICES;
-                    dev_idx++) {
+            for (dev_idx = EC_DEVICE_MAIN;
+                    dev_idx < ec_master_num_devices(master); dev_idx++) {
                 count += fsm->slaves_responding[dev_idx];
             }
 
@@ -708,7 +710,7 @@
     if (datagram->state != EC_DATAGRAM_RECEIVED) {
         EC_MASTER_ERR(master, "Failed to receive address"
                 " clearing datagram on %s link: ",
-                ec_device_names[fsm->dev_idx]);
+                ec_device_names[fsm->dev_idx != 0]);
         ec_datagram_print_state(datagram);
         master->scan_busy = 0;
         wake_up_interruptible(&master->scan_queue);
@@ -719,13 +721,13 @@
     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",
-                ec_device_names[fsm->dev_idx], datagram->working_counter,
+                ec_device_names[fsm->dev_idx != 0], datagram->working_counter,
                 fsm->slaves_responding[fsm->dev_idx]);
     }
 
     EC_MASTER_DBG(master, 1, "Sending broadcast-write"
             " to measure transmission delays on %s link.\n",
-            ec_device_names[fsm->dev_idx]);
+            ec_device_names[fsm->dev_idx != 0]);
 
     ec_datagram_bwr(datagram, 0x0900, 1);
     ec_datagram_zero(datagram);
@@ -750,7 +752,7 @@
 
     if (datagram->state != EC_DATAGRAM_RECEIVED) {
         EC_MASTER_ERR(master, "Failed to receive delay measuring datagram"
-                " on %s link: ", ec_device_names[fsm->dev_idx]);
+                " on %s link: ", ec_device_names[fsm->dev_idx != 0]);
         ec_datagram_print_state(datagram);
         master->scan_busy = 0;
         wake_up_interruptible(&master->scan_queue);
@@ -760,13 +762,13 @@
 
     EC_MASTER_DBG(master, 1, "%u slaves responded to delay measuring"
             " on %s link.\n",
-            datagram->working_counter, ec_device_names[fsm->dev_idx]);
+            datagram->working_counter, ec_device_names[fsm->dev_idx != 0]);
 
     do {
         fsm->dev_idx++;
-    } while (fsm->dev_idx < EC_NUM_DEVICES &&
+    } while (fsm->dev_idx < ec_master_num_devices(master) &&
             !fsm->slaves_responding[fsm->dev_idx]);
-    if (fsm->dev_idx < EC_NUM_DEVICES) {
+    if (fsm->dev_idx < ec_master_num_devices(master)) {
         ec_fsm_master_enter_clear_addresses(fsm);
         return;
     }
@@ -777,7 +779,7 @@
     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]);
+            ec_device_names[fsm->slave->device_index != 0]);
     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
@@ -823,7 +825,7 @@
     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_device_names[fsm->slave->device_index != 0]);
         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;
--- a/master/fsm_master.h	Fri Nov 30 15:24:38 2012 +0100
+++ b/master/fsm_master.h	Fri Nov 30 20:15:31 2012 +0100
@@ -105,15 +105,15 @@
                                 */
     int idle; /**< state machine is in idle phase */
     unsigned long scan_jiffies; /**< beginning of slave scanning */
-    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.
-                                                     */
+    uint8_t link_state[EC_MAX_NUM_DEVICES]; /**< Last link state for every
+                                              device. */
+    unsigned int slaves_responding[EC_MAX_NUM_DEVICES]; /**< Number of
+                                                          responding slaves
+                                                          for every device. */
     unsigned int rescan_required; /**< A bus rescan is required. */
-    ec_slave_state_t slave_states[EC_NUM_DEVICES]; /**< AL states of
-                                                     responding slaves for
-                                                     every device. */
+    ec_slave_state_t slave_states[EC_MAX_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/globals.h	Fri Nov 30 15:24:38 2012 +0100
+++ b/master/globals.h	Fri Nov 30 20:15:31 2012 +0100
@@ -200,11 +200,10 @@
  */
 typedef enum {
     EC_DEVICE_MAIN, /**< Main device. */
-    EC_DEVICE_BACKUP, /**< Backup device */
-    EC_NUM_DEVICES /**< Number of devices. */
+    EC_DEVICE_BACKUP /**< Backup device */
 } ec_device_index_t;
 
-extern const char *ec_device_names[EC_NUM_DEVICES];
+extern const char *ec_device_names[2]; // only main and backup!
 
 /*****************************************************************************/
 
--- a/master/ioctl.c	Fri Nov 30 15:24:38 2012 +0100
+++ b/master/ioctl.c	Fri Nov 30 20:15:31 2012 +0100
@@ -93,80 +93,86 @@
         void *arg /**< Userspace address to store the results. */
         )
 {
-    ec_ioctl_master_t data;
-    unsigned int i, j;
-
-    if (down_interruptible(&master->master_sem))
+    ec_ioctl_master_t io;
+    unsigned int dev_idx, j;
+
+    if (down_interruptible(&master->master_sem)) {
         return -EINTR;
-
-    data.slave_count = master->slave_count;
-    data.config_count = ec_master_config_count(master);
-    data.domain_count = ec_master_domain_count(master);
+    }
+
+    io.slave_count = master->slave_count;
+    io.config_count = ec_master_config_count(master);
+    io.domain_count = ec_master_domain_count(master);
 #ifdef EC_EOE
-    data.eoe_handler_count = ec_master_eoe_handler_count(master);
+    io.eoe_handler_count = ec_master_eoe_handler_count(master);
 #endif
-    data.phase = (uint8_t) master->phase;
-    data.active = (uint8_t) master->active;
-    data.scan_busy = master->scan_busy;
+    io.phase = (uint8_t) master->phase;
+    io.active = (uint8_t) master->active;
+    io.scan_busy = master->scan_busy;
 
     up(&master->master_sem);
 
-    if (down_interruptible(&master->device_sem))
+    if (down_interruptible(&master->device_sem)) {
         return -EINTR;
-
-    for (i = 0; i < EC_NUM_DEVICES; i++) {
-        ec_device_t *device = &master->devices[i];
+    }
+
+    for (dev_idx = EC_DEVICE_MAIN;
+            dev_idx < ec_master_num_devices(master); dev_idx++) {
+        ec_device_t *device = &master->devices[dev_idx];
 
         if (device->dev) {
-            memcpy(data.devices[i].address,
-                    device->dev->dev_addr, ETH_ALEN);
+            memcpy(io.devices[dev_idx].address, device->dev->dev_addr,
+                    ETH_ALEN);
         } else {
-            memcpy(data.devices[i].address, master->macs[i], ETH_ALEN);
+            memcpy(io.devices[dev_idx].address, master->macs[dev_idx],
+                    ETH_ALEN);
         }
-        data.devices[i].attached = device->dev ? 1 : 0;
-        data.devices[i].link_state = device->link_state ? 1 : 0;
-        data.devices[i].tx_count = device->tx_count;
-        data.devices[i].rx_count = device->rx_count;
-        data.devices[i].tx_bytes = device->tx_bytes;
-        data.devices[i].rx_bytes = device->rx_bytes;
-        data.devices[i].tx_errors = device->tx_errors;
+        io.devices[dev_idx].attached = device->dev ? 1 : 0;
+        io.devices[dev_idx].link_state = device->link_state ? 1 : 0;
+        io.devices[dev_idx].tx_count = device->tx_count;
+        io.devices[dev_idx].rx_count = device->rx_count;
+        io.devices[dev_idx].tx_bytes = device->tx_bytes;
+        io.devices[dev_idx].rx_bytes = device->rx_bytes;
+        io.devices[dev_idx].tx_errors = device->tx_errors;
         for (j = 0; j < EC_RATE_COUNT; j++) {
-            data.devices[i].tx_frame_rates[j] =
+            io.devices[dev_idx].tx_frame_rates[j] =
                 device->tx_frame_rates[j];
-            data.devices[i].rx_frame_rates[j] =
+            io.devices[dev_idx].rx_frame_rates[j] =
                 device->rx_frame_rates[j];
-            data.devices[i].tx_byte_rates[j] =
+            io.devices[dev_idx].tx_byte_rates[j] =
                 device->tx_byte_rates[j];
-            data.devices[i].rx_byte_rates[j] =
+            io.devices[dev_idx].rx_byte_rates[j] =
                 device->rx_byte_rates[j];
         }
     }
-
-    data.tx_count = master->device_stats.tx_count;
-    data.rx_count = master->device_stats.rx_count;
-    data.tx_bytes = master->device_stats.tx_bytes;
-    data.rx_bytes = master->device_stats.rx_bytes;
-    for (i = 0; i < EC_RATE_COUNT; i++) {
-        data.tx_frame_rates[i] =
-            master->device_stats.tx_frame_rates[i];
-        data.rx_frame_rates[i] =
-            master->device_stats.rx_frame_rates[i];
-        data.tx_byte_rates[i] =
-            master->device_stats.tx_byte_rates[i];
-        data.rx_byte_rates[i] =
-            master->device_stats.rx_byte_rates[i];
-        data.loss_rates[i] =
-            master->device_stats.loss_rates[i];
+    io.num_devices = ec_master_num_devices(master);
+
+    io.tx_count = master->device_stats.tx_count;
+    io.rx_count = master->device_stats.rx_count;
+    io.tx_bytes = master->device_stats.tx_bytes;
+    io.rx_bytes = master->device_stats.rx_bytes;
+    for (j = 0; j < EC_RATE_COUNT; j++) {
+        io.tx_frame_rates[j] =
+            master->device_stats.tx_frame_rates[j];
+        io.rx_frame_rates[j] =
+            master->device_stats.rx_frame_rates[j];
+        io.tx_byte_rates[j] =
+            master->device_stats.tx_byte_rates[j];
+        io.rx_byte_rates[j] =
+            master->device_stats.rx_byte_rates[j];
+        io.loss_rates[j] =
+            master->device_stats.loss_rates[j];
     }
 
     up(&master->device_sem);
 
-    data.app_time = master->app_time;
-    data.ref_clock =
+    io.app_time = master->app_time;
+    io.ref_clock =
         master->dc_ref_clock ? master->dc_ref_clock->ring_position : 0xffff;
 
-    if (copy_to_user((void __user *) arg, &data, sizeof(data)))
-        return -EFAULT;
+    if (copy_to_user((void __user *) arg, &io, sizeof(io))) {
+        return -EFAULT;
+    }
 
     return 0;
 }
@@ -459,7 +465,8 @@
 
     data.data_size = domain->data_size;
     data.logical_base_address = domain->logical_base_address;
-    for (dev_idx = 0; dev_idx < EC_NUM_DEVICES; dev_idx++) {
+    for (dev_idx = EC_DEVICE_MAIN;
+            dev_idx < ec_master_num_devices(domain->master); dev_idx++) {
         data.working_counter[dev_idx] = domain->working_counter[dev_idx];
     }
     data.expected_working_counter = domain->expected_working_counter;
--- a/master/ioctl.h	Fri Nov 30 15:24:38 2012 +0100
+++ b/master/ioctl.h	Fri Nov 30 20:15:31 2012 +0100
@@ -56,7 +56,7 @@
  *
  * Increment this when changing the ioctl interface!
  */
-#define EC_IOCTL_VERSION_MAGIC 23
+#define EC_IOCTL_VERSION_MAGIC 24
 
 // Command-line tool
 #define EC_IOCTL_MODULE                EC_IOR(0x00, ec_ioctl_module_t)
@@ -187,7 +187,8 @@
         int32_t rx_frame_rates[EC_RATE_COUNT];
         int32_t tx_byte_rates[EC_RATE_COUNT];
         int32_t rx_byte_rates[EC_RATE_COUNT];
-    } devices[EC_NUM_DEVICES];
+    } devices[EC_MAX_NUM_DEVICES];
+    uint32_t num_devices;
     uint64_t tx_count;
     uint64_t rx_count;
     uint64_t tx_bytes;
@@ -304,7 +305,7 @@
     // outputs
     uint32_t data_size;
     uint32_t logical_base_address;
-    uint16_t working_counter[EC_NUM_DEVICES];
+    uint16_t working_counter[EC_MAX_NUM_DEVICES];
     uint16_t expected_working_counter;
     uint32_t fmmu_count;
 } ec_ioctl_domain_t;
--- a/master/master.c	Fri Nov 30 15:24:38 2012 +0100
+++ b/master/master.c	Fri Nov 30 20:15:31 2012 +0100
@@ -136,14 +136,28 @@
         )
 {
     int ret;
+    unsigned int dev_idx;
 
     master->index = index;
     master->reserved = 0;
 
     sema_init(&master->master_sem, 1);
 
+    for (dev_idx = EC_DEVICE_MAIN; dev_idx < EC_MAX_NUM_DEVICES; dev_idx++) {
+        master->macs[dev_idx] = NULL;
+    }
+
     master->macs[EC_DEVICE_MAIN] = main_mac;
+
+#if EC_MAX_NUM_DEVICES > 1
     master->macs[EC_DEVICE_BACKUP] = backup_mac;
+    master->num_devices = 1 + !ec_mac_is_zero(backup_mac);
+#else
+    if (!ec_mac_is_zero(backup_mac)) {
+        EC_MASTER_WARN(master, "Ignoring backup MAC address!");
+    }
+#endif
+
     ec_master_clear_device_stats(master);
 
     sema_init(&master->device_sem, 1);
@@ -209,13 +223,13 @@
     init_waitqueue_head(&master->sii_queue);
 
     // init devices
-    ret = ec_device_init(&master->devices[EC_DEVICE_MAIN], master);
-    if (ret < 0)
-        goto out_return;
-
-    ret = ec_device_init(&master->devices[EC_DEVICE_BACKUP], master);
-    if (ret < 0)
-        goto out_clear_main;
+    for (dev_idx = EC_DEVICE_MAIN; dev_idx < ec_master_num_devices(master);
+            dev_idx++) {
+        ret = ec_device_init(&master->devices[dev_idx], master);
+        if (ret < 0) {
+            goto out_clear_devices;
+        }
+    }
 
     // init state machine datagram
     ec_datagram_init(&master->fsm_datagram);
@@ -224,7 +238,7 @@
     if (ret < 0) {
         ec_datagram_clear(&master->fsm_datagram);
         EC_MASTER_ERR(master, "Failed to allocate FSM datagram.\n");
-        goto out_clear_backup;
+        goto out_clear_devices;
     }
 
     // create state machine object
@@ -325,11 +339,10 @@
 out_clear_fsm:
     ec_fsm_master_clear(&master->fsm);
     ec_datagram_clear(&master->fsm_datagram);
-out_clear_backup:
-    ec_device_clear(&master->devices[EC_DEVICE_BACKUP]);
-out_clear_main:
-    ec_device_clear(&master->devices[EC_DEVICE_MAIN]);
-out_return:
+out_clear_devices:
+    for (; dev_idx > 0; dev_idx--) {
+        ec_device_clear(&master->devices[dev_idx - 1]);
+    }
     return ret;
 }
 
@@ -341,6 +354,8 @@
         ec_master_t *master /**< EtherCAT master */
         )
 {
+    unsigned int dev_idx;
+
 #ifdef EC_RTDM
     ec_rtdm_dev_clear(&master->rtdm_dev);
 #endif
@@ -365,8 +380,11 @@
     ec_datagram_clear(&master->ref_sync_datagram);
     ec_fsm_master_clear(&master->fsm);
     ec_datagram_clear(&master->fsm_datagram);
-    ec_device_clear(&master->devices[EC_DEVICE_BACKUP]);
-    ec_device_clear(&master->devices[EC_DEVICE_MAIN]);
+
+    for (dev_idx = EC_DEVICE_MAIN; dev_idx < ec_master_num_devices(master);
+            dev_idx++) {
+        ec_device_clear(&master->devices[dev_idx]);
+    }
 }
 
 /*****************************************************************************/
@@ -575,7 +593,8 @@
     master->phase = EC_IDLE;
 
     // reset number of responding slaves to trigger scanning
-    for (dev_idx = EC_DEVICE_MAIN; dev_idx < EC_NUM_DEVICES; dev_idx++) {
+    for (dev_idx = EC_DEVICE_MAIN; dev_idx < ec_master_num_devices(master);
+            dev_idx++) {
         master->fsm.slaves_responding[dev_idx] = 0;
     }
 
@@ -1222,7 +1241,7 @@
     ec_device_stats_t *s = &master->device_stats;
     s32 tx_frame_rate, rx_frame_rate, tx_byte_rate, rx_byte_rate, loss_rate;
     u64 loss;
-    unsigned int i;
+    unsigned int i, dev_idx;
 
     // frame statistics
     if (likely(jiffies - s->jiffies < HZ)) {
@@ -1255,8 +1274,10 @@
     s->last_rx_bytes = s->rx_bytes;
     s->last_loss = loss;
 
-    ec_device_update_stats(&master->devices[EC_DEVICE_MAIN]);
-    ec_device_update_stats(&master->devices[EC_DEVICE_BACKUP]);
+    for (dev_idx = EC_DEVICE_MAIN; dev_idx < ec_master_num_devices(master);
+            dev_idx++) {
+        ec_device_update_stats(&master->devices[dev_idx]);
+    }
 
     s->jiffies = jiffies;
 }
@@ -2265,7 +2286,8 @@
 
     ec_master_inject_external_datagrams(master);
 
-    for (dev_idx = EC_DEVICE_MAIN; dev_idx < EC_NUM_DEVICES; dev_idx++) {
+    for (dev_idx = EC_DEVICE_MAIN; dev_idx < ec_master_num_devices(master);
+            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,
@@ -2297,12 +2319,13 @@
 
 void ecrt_master_receive(ec_master_t *master)
 {
+    unsigned int dev_idx;
     ec_datagram_t *datagram, *next;
 
     // receive datagrams
-    ec_device_poll(&master->devices[EC_DEVICE_MAIN]);
-    if (master->devices[EC_DEVICE_BACKUP].dev) {
-        ec_device_poll(&master->devices[EC_DEVICE_BACKUP]);
+    for (dev_idx = EC_DEVICE_MAIN; dev_idx < ec_master_num_devices(master);
+            dev_idx++) {
+        ec_device_poll(&master->devices[dev_idx]);
     }
     ec_master_update_device_stats(master);
 
@@ -2540,7 +2563,8 @@
     state->al_states = 0;
     state->link_up = 0U;
 
-    for (dev_idx = EC_DEVICE_MAIN; dev_idx < EC_NUM_DEVICES; dev_idx++) {
+    for (dev_idx = EC_DEVICE_MAIN; dev_idx < ec_master_num_devices(master);
+            dev_idx++) {
         /* Announce sum of responding slaves on all links. */
         state->slaves_responding += master->fsm.slaves_responding[dev_idx];
 
@@ -2557,7 +2581,7 @@
 int ecrt_master_link_state(const ec_master_t *master, unsigned int dev_idx,
         ec_master_link_state_t *state)
 {
-    if (dev_idx >= EC_NUM_DEVICES) {
+    if (dev_idx >= ec_master_num_devices(master)) {
         return -EINVAL;
     }
 
--- a/master/master.h	Fri Nov 30 15:24:38 2012 +0100
+++ b/master/master.h	Fri Nov 30 20:15:31 2012 +0100
@@ -172,6 +172,12 @@
 
 /*****************************************************************************/
 
+#if EC_MAX_NUM_DEVICES < 1
+#error Invalid number of devices
+#endif
+
+/*****************************************************************************/
+
 /** EtherCAT master.
  *
  * Manages slaves, domains and IO.
@@ -193,8 +199,13 @@
 
     struct semaphore master_sem; /**< Master semaphore. */
 
-    ec_device_t devices[EC_NUM_DEVICES]; /**< EtherCAT devices. */
-    const uint8_t *macs[EC_NUM_DEVICES]; /**< Device MAC addresses. */
+    ec_device_t devices[EC_MAX_NUM_DEVICES]; /**< EtherCAT devices. */
+    const uint8_t *macs[EC_MAX_NUM_DEVICES]; /**< Device MAC addresses. */
+#if EC_MAX_NUM_DEVICES > 1
+    unsigned int num_devices; /**< Number of devices. Access this always via
+                                ec_master_num_devices(), because it may be
+                                optimized! */
+#endif
     struct semaphore device_sem; /**< Device semaphore. */
     ec_device_stats_t device_stats; /**< Device statistics. */
 
@@ -290,6 +301,12 @@
         const uint8_t *, dev_t, struct class *, unsigned int);
 void ec_master_clear(ec_master_t *);
 
+#if EC_MAX_NUM_DEVICES > 1
+#define ec_master_num_devices(MASTER) ((MASTER)->num_devices)
+#else
+#define ec_master_num_devices(MASTER) 1
+#endif
+
 // phase transitions
 int ec_master_enter_idle_phase(ec_master_t *);
 void ec_master_leave_idle_phase(ec_master_t *);
--- a/master/module.c	Fri Nov 30 15:24:38 2012 +0100
+++ b/master/module.c	Fri Nov 30 20:15:31 2012 +0100
@@ -453,7 +453,7 @@
 
 /** Device names.
  */
-const char *ec_device_names[EC_NUM_DEVICES] = {
+const char *ec_device_names[2] = {
     "main",
     "backup"
 };
@@ -476,29 +476,34 @@
 {
     ec_master_t *master;
     char str[EC_MAX_MAC_STRING_SIZE];
-    unsigned int i, j;
+    unsigned int i, dev_idx;
 
     for (i = 0; i < master_count; i++) {
         master = &masters[i];
         ec_mac_print(net_dev->dev_addr, str);
 
-        down(&master->device_sem);
-
-        for (j = 0; j < EC_NUM_DEVICES; j++) {
-            if (!master->devices[j].dev
-                && (ec_mac_equal(master->macs[j], net_dev->dev_addr)
-                    || ec_mac_is_broadcast(master->macs[j]))) {
+        if (down_interruptible(&master->device_sem)) {
+            EC_MASTER_WARN(master, "%s() interrupted!\n", __func__);
+            return NULL;
+        }
+
+        for (dev_idx = EC_DEVICE_MAIN;
+                dev_idx < ec_master_num_devices(master); dev_idx++) {
+            if (!master->devices[dev_idx].dev
+                && (ec_mac_equal(master->macs[dev_idx], net_dev->dev_addr)
+                    || ec_mac_is_broadcast(master->macs[dev_idx]))) {
 
                 EC_INFO("Accepting %s as %s device for master %u.\n",
-                        str, ec_device_names[j], master->index);
-
-                ec_device_attach(&master->devices[j], net_dev, poll, module);
+                        str, ec_device_names[dev_idx != 0], master->index);
+
+                ec_device_attach(&master->devices[dev_idx],
+                        net_dev, poll, module);
                 up(&master->device_sem);
 
                 snprintf(net_dev->name, IFNAMSIZ, "ec%c%u",
-                        ec_device_names[j][0], master->index);
-
-                return &master->devices[j]; // offer accepted
+                        ec_device_names[dev_idx != 0][0], master->index);
+
+                return &master->devices[dev_idx]; // offer accepted
             }
         }
 
@@ -523,7 +528,7 @@
         )
 {
     ec_master_t *master, *errptr = NULL;
-    unsigned int i, got_modules = 0;
+    unsigned int dev_idx = EC_DEVICE_MAIN;
 
     EC_INFO("Requesting master %u...\n", master_index);
 
@@ -560,17 +565,14 @@
         goto out_release;
     }
 
-    for (i = 0; i < EC_NUM_DEVICES; i++) {
-        ec_device_t *device = &master->devices[i];
-        if (device->dev) {
-            if (!try_module_get(device->module)) {
-                up(&master->device_sem);
-                EC_MASTER_ERR(master, "Device module is unloading!\n");
-                errptr = ERR_PTR(-ENODEV);
-                goto out_module_put;
-            }
-        }
-        got_modules++;
+    for (; dev_idx < ec_master_num_devices(master); dev_idx++) {
+        ec_device_t *device = &master->devices[dev_idx];
+        if (!try_module_get(device->module)) {
+            up(&master->device_sem);
+            EC_MASTER_ERR(master, "Device module is unloading!\n");
+            errptr = ERR_PTR(-ENODEV);
+            goto out_module_put;
+        }
     }
 
     up(&master->device_sem);
@@ -585,11 +587,9 @@
     return master;
 
  out_module_put:
-    for (; got_modules > 0; got_modules--) {
-        ec_device_t *device = &master->devices[i - 1];
-        if (device->dev) {
-            module_put(device->module);
-        }
+    for (; dev_idx > 0; dev_idx--) {
+        ec_device_t *device = &master->devices[dev_idx - 1];
+        module_put(device->module);
     }
  out_release:
     master->reserved = 0;
@@ -609,7 +609,7 @@
 
 void ecrt_release_master(ec_master_t *master)
 {
-    unsigned int i;
+    unsigned int dev_idx;
 
     EC_MASTER_INFO(master, "Releasing master...\n");
 
@@ -621,10 +621,9 @@
 
     ec_master_leave_operation_phase(master);
 
-    for (i = 0; i < EC_NUM_DEVICES; i++) {
-        if (master->devices[i].dev) {
-            module_put(master->devices[i].module);
-        }
+    for (dev_idx = EC_DEVICE_MAIN; dev_idx < ec_master_num_devices(master);
+            dev_idx++) {
+        module_put(master->devices[dev_idx].module);
     }
 
     master->reserved = 0;
--- a/tool/Command.cpp	Fri Nov 30 15:24:38 2012 +0100
+++ b/tool/Command.cpp	Fri Nov 30 20:15:31 2012 +0100
@@ -467,19 +467,17 @@
 
 /****************************************************************************/
 
-Command::DomainList Command::selectedDomains(MasterDevice &m)
-{
-    ec_ioctl_master_t master;
+Command::DomainList Command::selectedDomains(MasterDevice &m,
+        const ec_ioctl_master_t &io)
+{
     DomainList list;
 
-    m.getMaster(&master);
-
-    PositionParser pp(master.domain_count);
+    PositionParser pp(io.domain_count);
     NumberListParser::List domList = pp.parse(domains.c_str());
     NumberListParser::List::const_iterator di;
 
     for (di = domList.begin(); di != domList.end(); di++) {
-        if (*di < master.domain_count) {
+        if (*di < io.domain_count) {
             ec_ioctl_domain_t d;
             m.getDomain(&d, *di);
             list.push_back(d);
--- a/tool/Command.h	Fri Nov 30 15:24:38 2012 +0100
+++ b/tool/Command.h	Fri Nov 30 20:15:31 2012 +0100
@@ -139,7 +139,7 @@
         typedef list<ec_ioctl_config_t> ConfigList;
         ConfigList selectedConfigs(MasterDevice &);
         typedef list<ec_ioctl_domain_t> DomainList;
-        DomainList selectedDomains(MasterDevice &);
+        DomainList selectedDomains(MasterDevice &, const ec_ioctl_master_t &);
 
         static string alStateString(uint8_t);
 
--- a/tool/CommandData.cpp	Fri Nov 30 15:24:38 2012 +0100
+++ b/tool/CommandData.cpp	Fri Nov 30 20:15:31 2012 +0100
@@ -82,9 +82,12 @@
     MasterIndexList::const_iterator mi;
     for (mi = masterIndices.begin();
             mi != masterIndices.end(); mi++) {
+        ec_ioctl_master_t io;
         MasterDevice m(*mi);
         m.open(MasterDevice::Read);
-        domains = selectedDomains(m);
+        m.getMaster(&io);
+
+        domains = selectedDomains(m, io);
 
         for (di = domains.begin(); di != domains.end(); di++) {
             outputDomainData(m, *di);
--- a/tool/CommandDomains.cpp	Fri Nov 30 15:24:38 2012 +0100
+++ b/tool/CommandDomains.cpp	Fri Nov 30 20:15:31 2012 +0100
@@ -108,16 +108,18 @@
     MasterIndexList::const_iterator mi;
     for (mi = masterIndices.begin();
             mi != masterIndices.end(); mi++) {
+        ec_ioctl_master_t io;
         MasterDevice m(*mi);
         m.open(MasterDevice::Read);
-        domains = selectedDomains(m);
+        m.getMaster(&io);
+        domains = selectedDomains(m, io);
 
         if (domains.size() && doIndent) {
             cout << "Master" << dec << *mi << endl;
         }
 
         for (di = domains.begin(); di != domains.end(); di++) {
-            showDomain(m, *di, doIndent);
+            showDomain(m, io, *di, doIndent);
         }
     }
 }
@@ -126,6 +128,7 @@
 
 void CommandDomains::showDomain(
         MasterDevice &m,
+        const ec_ioctl_master_t &master,
         const ec_ioctl_domain_t &domain,
         bool doIndent
         )
@@ -138,7 +141,7 @@
     string indent(doIndent ? "  " : "");
     unsigned int wc_sum = 0, dev_idx;
 
-    for (dev_idx = 0; dev_idx < EC_NUM_DEVICES; dev_idx++) {
+    for (dev_idx = EC_DEVICE_MAIN; dev_idx < master.num_devices; dev_idx++) {
         wc_sum += domain.working_counter[dev_idx];
     }
 
@@ -151,10 +154,16 @@
         << ", WorkingCounter "
         << wc_sum << "/"
         << domain.expected_working_counter;
-    if (EC_NUM_DEVICES == 2) {
-        cout << " (" << domain.working_counter[EC_DEVICE_MAIN]
-            << "+" << domain.working_counter[EC_DEVICE_BACKUP]
-            << ")";
+    if (master.num_devices > 1) {
+        cout << " (";
+        for (dev_idx = EC_DEVICE_MAIN; dev_idx < master.num_devices;
+                dev_idx++) {
+            cout << domain.working_counter[dev_idx];
+            if (dev_idx + 1 < master.num_devices) {
+                cout << "+";
+            }
+        }
+        cout << ")";
     }
     cout << endl;
 
--- a/tool/CommandDomains.h	Fri Nov 30 15:24:38 2012 +0100
+++ b/tool/CommandDomains.h	Fri Nov 30 20:15:31 2012 +0100
@@ -44,7 +44,8 @@
         void execute(const StringVector &);
 
     protected:
-        void showDomain(MasterDevice &, const ec_ioctl_domain_t &, bool);
+        void showDomain(MasterDevice &, const ec_ioctl_master_t &,
+                const ec_ioctl_domain_t &, bool);
 };
 
 /****************************************************************************/
--- a/tool/CommandMaster.cpp	Fri Nov 30 15:24:38 2012 +0100
+++ b/tool/CommandMaster.cpp	Fri Nov 30 20:15:31 2012 +0100
@@ -70,7 +70,7 @@
 	MasterIndexList masterIndices;
     ec_ioctl_master_t data;
     stringstream err;
-    unsigned int i, j;
+    unsigned int dev_idx, j;
     time_t epoch;
     char time_str[MAX_TIME_STR_SIZE + 1];
     size_t time_str_size;
@@ -104,84 +104,78 @@
             << "  Slaves: " << data.slave_count << endl
             << "  Ethernet devices:" << endl;
 
-        for (i = 0; i < 2; i++) {
-            cout << "    " << (i == 0 ? "Main" : "Backup") << ": ";
-            if (data.devices[i].address[0] == 0x00
-                    && data.devices[i].address[1] == 0x00
-                    && data.devices[i].address[2] == 0x00
-                    && data.devices[i].address[3] == 0x00
-                    && data.devices[i].address[4] == 0x00
-                    && data.devices[i].address[5] == 0x00) {
-                cout << "None." << endl;
-            } else {
-                cout << hex << setfill('0')
-                    << setw(2) << (unsigned int) data.devices[i].address[0]
-                    << ":"
-                    << setw(2) << (unsigned int) data.devices[i].address[1]
-                    << ":"
-                    << setw(2) << (unsigned int) data.devices[i].address[2]
-                    << ":"
-                    << setw(2) << (unsigned int) data.devices[i].address[3]
-                    << ":"
-                    << setw(2) << (unsigned int) data.devices[i].address[4]
-                    << ":"
-                    << setw(2) << (unsigned int) data.devices[i].address[5]
-                    << " ("
-                    << (data.devices[i].attached ? "attached" : "waiting...")
-                    << ")" << endl << dec
-                    << "      Link: "
-                    << (data.devices[i].link_state ? "UP" : "DOWN") << endl
-                    << "      Tx frames:   "
-                    << data.devices[i].tx_count << endl
-                    << "      Tx bytes:    "
-                    << data.devices[i].tx_bytes << endl
-                    << "      Rx frames:   "
-                    << data.devices[i].rx_count << endl
-                    << "      Rx bytes:    "
-                    << data.devices[i].rx_bytes << endl
-                    << "      Tx errors:   "
-                    << data.devices[i].tx_errors << endl
-                    << "      Tx frame rate [1/s]: "
-                    << setfill(' ') << setprecision(0) << fixed;
-                for (j = 0; j < EC_RATE_COUNT; j++) {
-                    cout << setw(ColWidth)
-                        << data.devices[i].tx_frame_rates[j] / 1000.0;
-                    if (j < EC_RATE_COUNT - 1) {
-                        cout << " ";
-                    }
-                }
-                cout << endl
-                    << "      Tx rate [KByte/s]:   "
-                    << setprecision(1) << fixed;
-                for (j = 0; j < EC_RATE_COUNT; j++) {
-                    cout << setw(ColWidth)
-                        << data.devices[i].tx_byte_rates[j] / 1024.0;
-                    if (j < EC_RATE_COUNT - 1) {
-                        cout << " ";
-                    }
-                }
-                cout << endl
-                    << "      Rx frame rate [1/s]: "
-                    << setfill(' ') << setprecision(0) << fixed;
-                for (j = 0; j < EC_RATE_COUNT; j++) {
-                    cout << setw(ColWidth)
-                        << data.devices[i].rx_frame_rates[j] / 1000.0;
-                    if (j < EC_RATE_COUNT - 1) {
-                        cout << " ";
-                    }
-                }
-                cout << endl
-                    << "      Rx rate [KByte/s]:   "
-                    << setprecision(1) << fixed;
-                for (j = 0; j < EC_RATE_COUNT; j++) {
-                    cout << setw(ColWidth)
-                        << data.devices[i].rx_byte_rates[j] / 1024.0;
-                    if (j < EC_RATE_COUNT - 1) {
-                        cout << " ";
-                    }
-                }
-                cout << setprecision(0) << endl;
-            }
+        for (dev_idx = EC_DEVICE_MAIN; dev_idx < data.num_devices;
+                dev_idx++) {
+            cout << "    " << (dev_idx == EC_DEVICE_MAIN ? "Main" : "Backup")
+                << ": ";
+            cout << hex << setfill('0')
+                << setw(2) << (unsigned int) data.devices[dev_idx].address[0]
+                << ":"
+                << setw(2) << (unsigned int) data.devices[dev_idx].address[1]
+                << ":"
+                << setw(2) << (unsigned int) data.devices[dev_idx].address[2]
+                << ":"
+                << setw(2) << (unsigned int) data.devices[dev_idx].address[3]
+                << ":"
+                << setw(2) << (unsigned int) data.devices[dev_idx].address[4]
+                << ":"
+                << setw(2) << (unsigned int) data.devices[dev_idx].address[5]
+                << " ("
+                << (data.devices[dev_idx].attached ?
+                        "attached" : "waiting...")
+                << ")" << endl << dec
+                << "      Link: "
+                << (data.devices[dev_idx].link_state ? "UP" : "DOWN") << endl
+                << "      Tx frames:   "
+                << data.devices[dev_idx].tx_count << endl
+                << "      Tx bytes:    "
+                << data.devices[dev_idx].tx_bytes << endl
+                << "      Rx frames:   "
+                << data.devices[dev_idx].rx_count << endl
+                << "      Rx bytes:    "
+                << data.devices[dev_idx].rx_bytes << endl
+                << "      Tx errors:   "
+                << data.devices[dev_idx].tx_errors << endl
+                << "      Tx frame rate [1/s]: "
+                << setfill(' ') << setprecision(0) << fixed;
+            for (j = 0; j < EC_RATE_COUNT; j++) {
+                cout << setw(ColWidth)
+                    << data.devices[dev_idx].tx_frame_rates[j] / 1000.0;
+                if (j < EC_RATE_COUNT - 1) {
+                    cout << " ";
+                }
+            }
+            cout << endl
+                << "      Tx rate [KByte/s]:   "
+                << setprecision(1) << fixed;
+            for (j = 0; j < EC_RATE_COUNT; j++) {
+                cout << setw(ColWidth)
+                    << data.devices[dev_idx].tx_byte_rates[j] / 1024.0;
+                if (j < EC_RATE_COUNT - 1) {
+                    cout << " ";
+                }
+            }
+            cout << endl
+                << "      Rx frame rate [1/s]: "
+                << setfill(' ') << setprecision(0) << fixed;
+            for (j = 0; j < EC_RATE_COUNT; j++) {
+                cout << setw(ColWidth)
+                    << data.devices[dev_idx].rx_frame_rates[j] / 1000.0;
+                if (j < EC_RATE_COUNT - 1) {
+                    cout << " ";
+                }
+            }
+            cout << endl
+                << "      Rx rate [KByte/s]:   "
+                << setprecision(1) << fixed;
+            for (j = 0; j < EC_RATE_COUNT; j++) {
+                cout << setw(ColWidth)
+                    << data.devices[dev_idx].rx_byte_rates[j] / 1024.0;
+                if (j < EC_RATE_COUNT - 1) {
+                    cout << " ";
+                }
+            }
+            cout << setprecision(0) << endl;
         }
         unsigned int lost = data.tx_count - data.rx_count;
         if (lost == 1) {