master/fsm_master.c
branchstable-1.3
changeset 1744 7bc131b92039
parent 1739 5fcbd29151d2
child 1745 07fd94c5119d
--- a/master/fsm_master.c	Fri Aug 10 15:08:44 2007 +0000
+++ b/master/fsm_master.c	Fri Aug 10 15:27:08 2007 +0000
@@ -41,6 +41,7 @@
 #include "globals.h"
 #include "master.h"
 #include "mailbox.h"
+#include "ethernet.h"
 #include "fsm_master.h"
 
 /*****************************************************************************/
@@ -74,10 +75,12 @@
     fsm->master = master;
     fsm->datagram = datagram;
     fsm->state = ec_fsm_master_state_start;
+    fsm->idle = 0;
     fsm->slaves_responding = 0;
     fsm->topology_change_pending = 0;
     fsm->slave_states = EC_SLAVE_STATE_UNKNOWN;
     fsm->validate = 0;
+    fsm->tainted = 0;
 
     // init sub-state-machines
     ec_fsm_slave_init(&fsm->fsm_slave, fsm->datagram);
@@ -125,10 +128,12 @@
 /*****************************************************************************/
 
 /**
-   \return false, if state machine has terminated
-*/
-
-int ec_fsm_master_running(ec_fsm_master_t *fsm /**< master state machine */)
+ * \return false, if state machine has terminated
+ */
+
+int ec_fsm_master_running(
+        const ec_fsm_master_t *fsm /**< master state machine */
+        )
 {
     return fsm->state != ec_fsm_master_state_end
         && fsm->state != ec_fsm_master_state_error;
@@ -137,16 +142,18 @@
 /*****************************************************************************/
 
 /**
-   \return true, if the master state machine terminated gracefully
-*/
-
-int ec_fsm_master_success(ec_fsm_master_t *fsm /**< master state machine */)
-{
-    return fsm->state == ec_fsm_master_state_end;
+ * \return true, if the state machine is in an idle phase
+ */
+
+int ec_fsm_master_idle(
+        const ec_fsm_master_t *fsm /**< master state machine */
+        )
+{
+    return fsm->idle;
 }
 
 /******************************************************************************
- *  operation/idle state machine
+ *  master state machine
  *****************************************************************************/
 
 /**
@@ -156,8 +163,8 @@
 
 void ec_fsm_master_state_start(ec_fsm_master_t *fsm)
 {
+    fsm->idle = 1;
     ec_datagram_brd(fsm->datagram, 0x0130, 2);
-    ec_master_queue_datagram(fsm->master, fsm->datagram);
     fsm->state = ec_fsm_master_state_broadcast;
 }
 
@@ -175,17 +182,13 @@
     ec_slave_t *slave;
     ec_master_t *master = fsm->master;
 
-    if (datagram->state == EC_DATAGRAM_TIMED_OUT) {
-        // always retry
-        ec_master_queue_datagram(fsm->master, fsm->datagram);
-        return;
-    }
-
-    if (datagram->state != EC_DATAGRAM_RECEIVED) { // EC_DATAGRAM_ERROR
-        // link is down
+    if (datagram->state == EC_DATAGRAM_TIMED_OUT)
+        return; // always retry
+
+    if (datagram->state != EC_DATAGRAM_RECEIVED) { // link is down
         fsm->slaves_responding = 0;
         list_for_each_entry(slave, &master->slaves, list) {
-            slave->online = 0;
+            ec_slave_set_online_state(slave, EC_SLAVE_OFFLINE);
         }
         fsm->state = ec_fsm_master_state_error;
         return;
@@ -206,6 +209,7 @@
             }
             else {
                 EC_WARN("Invalid slave count. Bus in tainted state.\n");
+                fsm->tainted = 1;
             }
         }
     }
@@ -218,56 +222,74 @@
         EC_INFO("Slave states: %s.\n", states);
     }
 
-    // topology change in idle mode: clear all slaves and scan the bus
-    if (fsm->topology_change_pending &&
-            master->mode == EC_MASTER_MODE_IDLE) {
-        fsm->topology_change_pending = 0;
-
-        ec_master_eoe_stop(master);
-        ec_master_destroy_slaves(master);
-
-        master->slave_count = datagram->working_counter;
-
-        if (!master->slave_count) {
-            // no slaves present -> finish state machine.
-            fsm->state = ec_fsm_master_state_end;
-            return;
-        }
-
-        // init slaves
-        for (i = 0; i < master->slave_count; i++) {
-            if (!(slave = (ec_slave_t *) kmalloc(sizeof(ec_slave_t),
-                                                 GFP_ATOMIC))) {
-                EC_ERR("Failed to allocate slave %i!\n", i);
-                ec_master_destroy_slaves(master);
-                fsm->state = ec_fsm_master_state_error;
+    if (fsm->topology_change_pending) {
+        down(&master->scan_sem);
+        if (!master->allow_scan) {
+            up(&master->scan_sem);
+        }
+        else {
+            master->scan_state = EC_REQUEST_IN_PROGRESS;
+            up(&master->scan_sem);
+            
+            // topology change when scan is allowed:
+            // clear all slaves and scan the bus
+            fsm->topology_change_pending = 0;
+            fsm->tainted = 0;
+            fsm->idle = 0;
+            fsm->scan_jiffies = jiffies;
+
+            ec_master_eoe_stop(master);
+            ec_master_clear_eoe_handlers(master);
+            ec_master_destroy_slaves(master);
+
+            master->slave_count = datagram->working_counter;
+
+            if (!master->slave_count) {
+                // no slaves present -> finish state machine.
+                master->scan_state = EC_REQUEST_COMPLETE;
+                wake_up_interruptible(&master->scan_queue);
+                fsm->state = ec_fsm_master_state_end;
                 return;
             }
 
-            if (ec_slave_init(slave, master, i, i + 1)) {
-                // freeing of "slave" already done
-                ec_master_destroy_slaves(master);
-                fsm->state = ec_fsm_master_state_error;
-                return;
+            // init slaves
+            for (i = 0; i < master->slave_count; i++) {
+                if (!(slave = (ec_slave_t *) kmalloc(sizeof(ec_slave_t),
+                                GFP_ATOMIC))) {
+                    EC_ERR("Failed to allocate slave %i!\n", i);
+                    ec_master_destroy_slaves(master);
+                    master->scan_state = EC_REQUEST_FAILURE;
+                    wake_up_interruptible(&master->scan_queue);
+                    fsm->state = ec_fsm_master_state_error;
+                    return;
+                }
+
+                if (ec_slave_init(slave, master, i, i + 1)) {
+                    // freeing of "slave" already done
+                    ec_master_destroy_slaves(master);
+                    master->scan_state = EC_REQUEST_FAILURE;
+                    wake_up_interruptible(&master->scan_queue);
+                    fsm->state = ec_fsm_master_state_error;
+                    return;
+                }
+
+                list_add_tail(&slave->list, &master->slaves);
             }
 
-            list_add_tail(&slave->list, &master->slaves);
-        }
-
-        EC_INFO("Scanning bus.\n");
-
-        // begin scanning of slaves
-        fsm->slave = list_entry(master->slaves.next, ec_slave_t, list);
-        ec_fsm_slave_start_scan(&fsm->fsm_slave, fsm->slave);
-        ec_fsm_slave_exec(&fsm->fsm_slave); // execute immediately
-        fsm->state = ec_fsm_master_state_scan_slaves;
-        return;
+            EC_INFO("Scanning bus.\n");
+
+            // begin scanning of slaves
+            fsm->slave = list_entry(master->slaves.next, ec_slave_t, list);
+            fsm->state = ec_fsm_master_state_scan_slaves;
+            ec_fsm_slave_start_scan(&fsm->fsm_slave, fsm->slave);
+            ec_fsm_slave_exec(&fsm->fsm_slave); // execute immediately
+            return;
+        }
     }
 
     // fetch state from each slave
     fsm->slave = list_entry(master->slaves.next, ec_slave_t, list);
     ec_datagram_nprd(fsm->datagram, fsm->slave->station_address, 0x0130, 2);
-    ec_master_queue_datagram(master, fsm->datagram);
     fsm->retries = EC_FSM_RETRIES;
     fsm->state = ec_fsm_master_state_read_states;
 }
@@ -275,81 +297,207 @@
 /*****************************************************************************/
 
 /**
-   Master action: PROC_STATES.
-   Processes the slave states.
-*/
-
-void ec_fsm_master_action_process_states(ec_fsm_master_t *fsm
-                                         /**< master state machine */
-                                         )
+ * Check for pending EEPROM write requests and process one.
+ * \return non-zero, if an EEPROM write request is processed.
+ */
+
+int ec_fsm_master_action_process_eeprom(
+        ec_fsm_master_t *fsm /**< master state machine */
+        )
 {
     ec_master_t *master = fsm->master;
+    ec_eeprom_write_request_t *request;
     ec_slave_t *slave;
+
+    // search the first request to be processed
+    while (1) {
+        down(&master->eeprom_sem);
+        if (list_empty(&master->eeprom_requests)) {
+            up(&master->eeprom_sem);
+            break;
+        }
+        // get first request
+        request = list_entry(master->eeprom_requests.next,
+                ec_eeprom_write_request_t, list);
+        list_del_init(&request->list); // dequeue
+        request->state = EC_REQUEST_IN_PROGRESS;
+        up(&master->eeprom_sem);
+
+        slave = request->slave;
+        if (slave->online_state == EC_SLAVE_OFFLINE || slave->error_flag) {
+            EC_ERR("Discarding EEPROM data, slave %i not ready.\n",
+                    slave->ring_position);
+            request->state = EC_REQUEST_FAILURE;
+            wake_up(&master->eeprom_queue);
+            continue;
+        }
+
+        // found pending EEPROM write operation. execute it!
+        if (master->debug_level)
+            EC_DBG("Writing EEPROM data to slave %i...\n",
+                    slave->ring_position);
+        fsm->eeprom_request = request;
+        fsm->eeprom_index = 0;
+        ec_fsm_sii_write(&fsm->fsm_sii, request->slave, request->offset,
+                request->words, EC_FSM_SII_NODE);
+        fsm->state = ec_fsm_master_state_write_eeprom;
+        fsm->state(fsm); // execute immediately
+        return 1;
+    }
+
+    return 0;
+}
+
+/*****************************************************************************/
+
+/**
+ * Check for pending SDO requests and process one.
+ * \return non-zero, if an SDO request is processed.
+ */
+
+int ec_fsm_master_action_process_sdo(
+        ec_fsm_master_t *fsm /**< master state machine */
+        )
+{
+    ec_master_t *master = fsm->master;
+    ec_sdo_request_t *request;
+    ec_slave_t *slave;
+
+    // search the first request to be processed
+    while (1) {
+        down(&master->sdo_sem);
+        if (list_empty(&master->sdo_requests)) {
+            up(&master->sdo_sem);
+            break;
+        }
+        // get first request
+        request =
+            list_entry(master->sdo_requests.next, ec_sdo_request_t, list);
+        list_del_init(&request->list); // dequeue
+        request->state = EC_REQUEST_IN_PROGRESS;
+        up(&master->sdo_sem);
+
+        slave = request->sdo->slave;
+        if (slave->current_state == EC_SLAVE_STATE_INIT ||
+                slave->online_state == EC_SLAVE_OFFLINE ||
+                slave->error_flag) {
+            EC_ERR("Discarding SDO request, slave %i not ready.\n",
+                    slave->ring_position);
+            request->state = EC_REQUEST_FAILURE;
+            wake_up(&master->sdo_queue);
+            continue;
+        }
+
+        // found pending SDO request. execute it!
+        if (master->debug_level)
+            EC_DBG("Processing SDO request for slave %i...\n",
+                    slave->ring_position);
+
+        // start uploading SDO
+        fsm->idle = 0;
+        fsm->slave = slave;
+        fsm->sdo_request = request;
+        fsm->state = ec_fsm_master_state_sdo_request;
+        ec_fsm_coe_upload(&fsm->fsm_coe, slave, fsm->sdo_request);
+        ec_fsm_coe_exec(&fsm->fsm_coe); // execute immediately
+        return 1;
+    }
+
+    return 0;
+}
+
+/*****************************************************************************/
+
+/**
+ */
+
+int ec_fsm_master_action_configure(
+        ec_fsm_master_t *fsm /**< master state machine */
+        )
+{
+    ec_slave_t *slave;
+    ec_master_t *master = fsm->master;
     char old_state[EC_STATE_STRING_SIZE], new_state[EC_STATE_STRING_SIZE];
 
     // check if any slaves are not in the state, they're supposed to be
+    // FIXME do not check all slaves in every cycle...
     list_for_each_entry(slave, &master->slaves, list) {
         if (slave->error_flag
-            || !slave->online
-            || slave->requested_state == EC_SLAVE_STATE_UNKNOWN
-            || (slave->current_state == slave->requested_state
-                && slave->self_configured)) continue;
+                || slave->online_state == EC_SLAVE_OFFLINE
+                || slave->requested_state == EC_SLAVE_STATE_UNKNOWN
+                || (slave->current_state == slave->requested_state
+                    && slave->self_configured)) continue;
 
         if (master->debug_level) {
             ec_state_string(slave->current_state, old_state);
             if (slave->current_state != slave->requested_state) {
                 ec_state_string(slave->requested_state, new_state);
                 EC_DBG("Changing state of slave %i (%s -> %s).\n",
-                       slave->ring_position, old_state, new_state);
+                        slave->ring_position, old_state, new_state);
             }
             else if (!slave->self_configured) {
                 EC_DBG("Reconfiguring slave %i (%s).\n",
-                       slave->ring_position, old_state);
+                        slave->ring_position, old_state);
             }
         }
 
+        fsm->idle = 0;
         fsm->slave = slave;
+        fsm->state = ec_fsm_master_state_configure_slave;
         ec_fsm_slave_start_conf(&fsm->fsm_slave, slave);
         ec_fsm_slave_exec(&fsm->fsm_slave); // execute immediately
-        fsm->state = ec_fsm_master_state_configure_slave;
-        return;
-    }
-
-    // Check, if EoE processing has to be started
-    ec_master_eoe_start(master);
+        return 1;
+    }
+
+    if (fsm->config_error)
+        master->config_state = EC_REQUEST_FAILURE;
+    else
+        master->config_state = EC_REQUEST_COMPLETE;
+    wake_up_interruptible(&master->config_queue);
+    return 0;
+}
+
+/*****************************************************************************/
+
+/**
+   Master action: PROC_STATES.
+   Processes the slave states.
+*/
+
+void ec_fsm_master_action_process_states(ec_fsm_master_t *fsm
+                                         /**< master state machine */
+                                         )
+{
+    ec_master_t *master = fsm->master;
+    ec_slave_t *slave;
+
+    down(&master->config_sem);
+    if (!master->allow_config) {
+        up(&master->config_sem);
+    }
+    else {
+        master->config_state = EC_REQUEST_IN_PROGRESS;
+        fsm->config_error = 0;
+        up(&master->config_sem);
+        
+        // check for pending slave configurations
+        if (ec_fsm_master_action_configure(fsm))
+            return;
+    }
+
+    // Check for a pending SDO request
+    if (ec_fsm_master_action_process_sdo(fsm))
+        return;
 
     if (master->mode == EC_MASTER_MODE_IDLE) {
 
-        // Check for a pending SDO request
-        if (master->sdo_seq_master != master->sdo_seq_user) {
-            if (master->debug_level)
-                EC_DBG("Processing SDO request...\n");
-            slave = master->sdo_request->sdo->slave;
-            if (slave->current_state == EC_SLAVE_STATE_INIT
-                || !slave->online) {
-                EC_ERR("Failed to process SDO request, slave %i not ready.\n",
-                       slave->ring_position);
-                master->sdo_request->return_code = -1;
-                master->sdo_seq_master++;
-            }
-            else {
-                // start uploading SDO
-                fsm->slave = slave;
-                fsm->state = ec_fsm_master_state_sdo_request;
-                fsm->sdo_request = master->sdo_request;
-                ec_fsm_coe_upload(&fsm->fsm_coe, slave, fsm->sdo_request);
-                ec_fsm_coe_exec(&fsm->fsm_coe); // execute immediately
-                return;
-            }
-        }
-
         // check, if slaves have an SDO dictionary to read out.
         list_for_each_entry(slave, &master->slaves, list) {
             if (!(slave->sii_mailbox_protocols & EC_MBOX_COE)
                 || slave->sdo_dictionary_fetched
                 || slave->current_state == EC_SLAVE_STATE_INIT
                 || jiffies - slave->jiffies_preop < EC_WAIT_SDO_DICT * HZ
-                || !slave->online
+                || slave->online_state == EC_SLAVE_OFFLINE
                 || slave->error_flag) continue;
 
             if (master->debug_level) {
@@ -360,6 +508,7 @@
             slave->sdo_dictionary_fetched = 1;
 
             // start fetching SDO dictionary
+            fsm->idle = 0;
             fsm->slave = slave;
             fsm->state = ec_fsm_master_state_sdodict;
             ec_fsm_coe_dictionary(&fsm->fsm_coe, slave);
@@ -368,27 +517,8 @@
         }
 
         // check for pending EEPROM write operations.
-        list_for_each_entry(slave, &master->slaves, list) {
-            if (!slave->new_eeprom_data) continue;
-
-            if (!slave->online || slave->error_flag) {
-                kfree(slave->new_eeprom_data);
-                slave->new_eeprom_data = NULL;
-                EC_ERR("Discarding EEPROM data, slave %i not ready.\n",
-                       slave->ring_position);
-                continue;
-            }
-
-            // found pending EEPROM write operation. execute it!
-            EC_INFO("Writing EEPROM of slave %i...\n", slave->ring_position);
-            fsm->slave = slave;
-            fsm->sii_offset = 0x0000;
-            ec_fsm_sii_write(&fsm->fsm_sii, slave, fsm->sii_offset,
-                             slave->new_eeprom_data, EC_FSM_SII_NODE);
-            fsm->state = ec_fsm_master_state_write_eeprom;
-            fsm->state(fsm); // execute immediately
-            return;
-        }
+        if (ec_fsm_master_action_process_eeprom(fsm))
+            return; // EEPROM write request found
     }
 
     fsm->state = ec_fsm_master_state_end;
@@ -409,10 +539,10 @@
     // is there another slave to query?
     if (slave->list.next != &master->slaves) {
         // process next slave
-        fsm->slave = list_entry(fsm->slave->list.next, ec_slave_t, list);
+        fsm->idle = 1;
+        fsm->slave = list_entry(slave->list.next, ec_slave_t, list);
         ec_datagram_nprd(fsm->datagram, fsm->slave->station_address,
                          0x0130, 2);
-        ec_master_queue_datagram(master, fsm->datagram);
         fsm->retries = EC_FSM_RETRIES;
         fsm->state = ec_fsm_master_state_read_states;
         return;
@@ -424,10 +554,11 @@
     if (fsm->validate) {
         fsm->validate = 0;
         list_for_each_entry(slave, &master->slaves, list) {
-            if (slave->online) continue;
+            if (slave->online_state == EC_SLAVE_ONLINE) continue;
 
             // At least one slave is offline. validate!
             EC_INFO("Validating bus.\n");
+            fsm->idle = 0;
             fsm->slave = list_entry(master->slaves.next, ec_slave_t, list);
             fsm->state = ec_fsm_master_state_validate_vendor;
             ec_fsm_sii_read(&fsm->fsm_sii, slave, 0x0008, EC_FSM_SII_POSITION);
@@ -450,61 +581,34 @@
 {
     ec_slave_t *slave = fsm->slave;
     ec_datagram_t *datagram = fsm->datagram;
-    uint8_t new_state;
-
-    if (datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--) {
-        ec_master_queue_datagram(fsm->master, fsm->datagram);
-        return;
-    }
+
+    if (datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--)
+        return;
 
     if (datagram->state != EC_DATAGRAM_RECEIVED) {
-        EC_ERR("Failed to receive AL state datagram for slave %i!\n",
-               slave->ring_position);
+        EC_ERR("Failed to receive AL state datagram for slave %i"
+                " (datagram state %i)\n", slave->ring_position, datagram->state);
         fsm->state = ec_fsm_master_state_error;
         return;
     }
 
     // did the slave not respond to its station address?
     if (datagram->working_counter != 1) {
-        if (slave->online) {
-            slave->online = 0;
-            if (slave->master->debug_level)
-                EC_DBG("Slave %i: offline.\n", slave->ring_position);
-        }
+        ec_slave_set_online_state(slave, EC_SLAVE_OFFLINE);
         ec_fsm_master_action_next_slave_state(fsm);
         return;
     }
 
     // slave responded
-    new_state = EC_READ_U8(datagram->data);
-    if (!slave->online) { // slave was offline before
-        slave->online = 1;
-        slave->error_flag = 0; // clear error flag
-        slave->current_state = new_state;
-        if (slave->master->debug_level) {
-            char cur_state[EC_STATE_STRING_SIZE];
-            ec_state_string(slave->current_state, cur_state);
-            EC_DBG("Slave %i: online (%s).\n",
-                   slave->ring_position, cur_state);
-        }
-    }
-    else if (new_state != slave->current_state) {
-        if (slave->master->debug_level) {
-            char old_state[EC_STATE_STRING_SIZE],
-                cur_state[EC_STATE_STRING_SIZE];
-            ec_state_string(slave->current_state, old_state);
-            ec_state_string(new_state, cur_state);
-            EC_DBG("Slave %i: %s -> %s.\n",
-                   slave->ring_position, old_state, cur_state);
-        }
-        slave->current_state = new_state;
-    }
+    ec_slave_set_state(slave, EC_READ_U8(datagram->data)); // set app state first
+    ec_slave_set_online_state(slave, EC_SLAVE_ONLINE);
 
     // check, if new slave state has to be acknowledged
     if (slave->current_state & EC_SLAVE_STATE_ACK_ERR && !slave->error_flag) {
+        fsm->idle = 0;
+        fsm->state = ec_fsm_master_state_acknowledge;
         ec_fsm_change_ack(&fsm->fsm_change, slave);
         ec_fsm_change_exec(&fsm->fsm_change);
-        fsm->state = ec_fsm_master_state_acknowledge;
         return;
     }
 
@@ -579,10 +683,9 @@
 {
     ec_datagram_t *datagram = fsm->datagram;
 
-    while (fsm->slave->online) {
+    while (fsm->slave->online_state == EC_SLAVE_ONLINE) {
         if (fsm->slave->list.next == &fsm->master->slaves) { // last slave?
-            fsm->state = ec_fsm_master_state_start;
-            fsm->state(fsm); // execute immediately
+            fsm->state = ec_fsm_master_state_end;
             return;
         }
         // check next slave
@@ -595,7 +698,6 @@
     // write station address
     ec_datagram_apwr(datagram, fsm->slave->ring_position, 0x0010, 2);
     EC_WRITE_U16(datagram->data, fsm->slave->station_address);
-    ec_master_queue_datagram(fsm->master, datagram);
     fsm->retries = EC_FSM_RETRIES;
     fsm->state = ec_fsm_master_state_rewrite_addresses;
 }
@@ -631,6 +733,8 @@
 
     // have all states been validated?
     if (slave->list.next == &fsm->master->slaves) {
+        fsm->topology_change_pending = 0;
+        fsm->tainted = 0;
         fsm->slave = list_entry(fsm->master->slaves.next, ec_slave_t, list);
         // start writing addresses to offline slaves
         ec_fsm_master_action_addresses(fsm);
@@ -658,14 +762,13 @@
     ec_slave_t *slave = fsm->slave;
     ec_datagram_t *datagram = fsm->datagram;
 
-    if (datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--) {
-        ec_master_queue_datagram(fsm->master, fsm->datagram);
-        return;
-    }
+    if (datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--)
+        return;
 
     if (datagram->state != EC_DATAGRAM_RECEIVED) {
-        EC_ERR("Failed to receive address datagram for slave %i.\n",
-               slave->ring_position);
+        EC_ERR("Failed to receive address datagram for slave %i"
+                " (datagram state %i).\n",
+                slave->ring_position, datagram->state);
         fsm->state = ec_fsm_master_state_error;
         return;
     }
@@ -678,8 +781,7 @@
     }
 
     if (fsm->slave->list.next == &fsm->master->slaves) { // last slave?
-        fsm->state = ec_fsm_master_state_start;
-        fsm->state(fsm); // execute immediately
+        fsm->state = ec_fsm_master_state_end;
         return;
     }
 
@@ -692,35 +794,53 @@
 /*****************************************************************************/
 
 /**
-   Master state: SCAN SLAVES.
-   Executes the sub-statemachine for the scanning of a slave.
-*/
-
-void ec_fsm_master_state_scan_slaves(ec_fsm_master_t *fsm /**< master state machine */)
+ * Master state: SCAN SLAVES.
+ * Executes the sub-statemachine for the scanning of a slave.
+ */
+
+void ec_fsm_master_state_scan_slaves(
+        ec_fsm_master_t *fsm /**< master state machine */
+        )
 {
     ec_master_t *master = fsm->master;
-    ec_slave_t *slave;
+    ec_slave_t *slave = fsm->slave;
 
     if (ec_fsm_slave_exec(&fsm->fsm_slave)) // execute slave state machine
         return;
 
+    if (slave->sii_mailbox_protocols & EC_MBOX_EOE) {
+        // create EoE handler for this slave
+        ec_eoe_t *eoe;
+        if (!(eoe = kmalloc(sizeof(ec_eoe_t), GFP_KERNEL))) {
+            EC_ERR("Failed to allocate EoE handler memory for slave %u!\n",
+                    slave->ring_position);
+        }
+        else if (ec_eoe_init(eoe, slave)) {
+            EC_ERR("Failed to init EoE handler for slave %u!\n",
+                    slave->ring_position);
+            kfree(eoe);
+        }
+        else {
+            list_add_tail(&eoe->list, &master->eoe_handlers);
+        }
+    }
+
     // another slave to fetch?
-    if (fsm->slave->list.next != &master->slaves) {
-        fsm->slave = list_entry(fsm->slave->list.next, ec_slave_t, list);
+    if (slave->list.next != &master->slaves) {
+        fsm->slave = list_entry(slave->list.next, ec_slave_t, list);
         ec_fsm_slave_start_scan(&fsm->fsm_slave, fsm->slave);
         ec_fsm_slave_exec(&fsm->fsm_slave); // execute immediately
         return;
     }
 
-    EC_INFO("Bus scanning completed.\n");
-
-    ec_master_calc_addressing(master);
-
-    // set initial states of all slaves to PREOP to make mailbox
-    // communication possible
-    list_for_each_entry(slave, &master->slaves, list) {
-        ec_slave_request_state(slave, EC_SLAVE_STATE_PREOP);
-    }
+    EC_INFO("Bus scanning completed in %u ms.\n",
+            (u32) (jiffies - fsm->scan_jiffies) * 1000 / HZ);
+
+    // check if EoE processing has to be started
+    ec_master_eoe_start(master);
+
+    master->scan_state = EC_REQUEST_COMPLETE;
+    wake_up_interruptible(&master->scan_queue);
 
     fsm->state = ec_fsm_master_state_end;
 }
@@ -739,7 +859,14 @@
     if (ec_fsm_slave_exec(&fsm->fsm_slave)) // execute slave's state machine
         return;
 
-    ec_fsm_master_action_process_states(fsm);
+    if (!ec_fsm_slave_success(&fsm->fsm_slave))
+        fsm->config_error = 1;
+
+    // configure next slave, if necessary
+    if (ec_fsm_master_action_configure(fsm))
+        return;
+
+    fsm->state = ec_fsm_master_state_end;
 }
 
 /*****************************************************************************/
@@ -748,41 +875,49 @@
    Master state: WRITE EEPROM.
 */
 
-void ec_fsm_master_state_write_eeprom(ec_fsm_master_t *fsm /**< master state machine */)
-{
-    ec_slave_t *slave = fsm->slave;
+void ec_fsm_master_state_write_eeprom(
+        ec_fsm_master_t *fsm /**< master state machine */)
+{
+    ec_master_t *master = fsm->master;
+    ec_eeprom_write_request_t *request = fsm->eeprom_request;
+    ec_slave_t *slave = request->slave;
 
     if (ec_fsm_sii_exec(&fsm->fsm_sii)) return;
 
     if (!ec_fsm_sii_success(&fsm->fsm_sii)) {
-        fsm->slave->error_flag = 1;
-        EC_ERR("Failed to write EEPROM contents to slave %i.\n",
-               slave->ring_position);
-        kfree(slave->new_eeprom_data);
-        slave->new_eeprom_data = NULL;
-        fsm->state = ec_fsm_master_state_error;
-        return;
-    }
-
-    fsm->sii_offset++;
-    if (fsm->sii_offset < slave->new_eeprom_size) {
-        ec_fsm_sii_write(&fsm->fsm_sii, slave, fsm->sii_offset,
-                         slave->new_eeprom_data + fsm->sii_offset,
-                         EC_FSM_SII_NODE);
+        slave->error_flag = 1;
+        EC_ERR("Failed to write EEPROM data to slave %i.\n",
+                slave->ring_position);
+        request->state = EC_REQUEST_FAILURE;
+        wake_up(&master->eeprom_queue);
+        fsm->state = ec_fsm_master_state_error;
+        return;
+    }
+
+    fsm->eeprom_index++;
+    if (fsm->eeprom_index < request->size) {
+        ec_fsm_sii_write(&fsm->fsm_sii, slave,
+                request->offset + fsm->eeprom_index,
+                request->words + fsm->eeprom_index,
+                EC_FSM_SII_NODE);
         ec_fsm_sii_exec(&fsm->fsm_sii); // execute immediately
         return;
     }
 
     // finished writing EEPROM
-    EC_INFO("Finished writing EEPROM of slave %i.\n", slave->ring_position);
-    kfree(slave->new_eeprom_data);
-    slave->new_eeprom_data = NULL;
+    if (master->debug_level)
+        EC_DBG("Finished writing EEPROM data to slave %i.\n",
+                slave->ring_position);
+    request->state = EC_REQUEST_COMPLETE;
+    wake_up(&master->eeprom_queue);
 
     // TODO: Evaluate new EEPROM contents!
 
-    // restart master state machine.
-    fsm->state = ec_fsm_master_state_start;
-    fsm->state(fsm); // execute immediately
+    // check for another EEPROM write request
+    if (ec_fsm_master_action_process_eeprom(fsm))
+        return; // processing another request
+
+    fsm->state = ec_fsm_master_state_end;
 }
 
 /*****************************************************************************/
@@ -812,9 +947,7 @@
                sdo_count, entry_count, slave->ring_position);
     }
 
-    // restart master state machine.
-    fsm->state = ec_fsm_master_state_start;
-    fsm->state(fsm); // execute immediately
+    fsm->state = ec_fsm_master_state_end;
 }
 
 /*****************************************************************************/
@@ -831,20 +964,27 @@
     if (ec_fsm_coe_exec(&fsm->fsm_coe)) return;
 
     if (!ec_fsm_coe_success(&fsm->fsm_coe)) {
-        request->return_code = -1;
-        master->sdo_seq_master++;
-        fsm->state = ec_fsm_master_state_error;
-        return;
-    }
-
-    // SDO dictionary fetching finished
-
-    request->return_code = 1;
-    master->sdo_seq_master++;
-
-    // restart master state machine.
-    fsm->state = ec_fsm_master_state_start;
-    fsm->state(fsm); // execute immediately
+        EC_DBG("Failed to process SDO request for slave %i.\n",
+                fsm->slave->ring_position);
+        request->state = EC_REQUEST_FAILURE;
+        wake_up(&master->sdo_queue);
+        fsm->state = ec_fsm_master_state_error;
+        return;
+    }
+
+    // SDO request finished 
+    request->state = EC_REQUEST_COMPLETE;
+    wake_up(&master->sdo_queue);
+
+    if (master->debug_level)
+        EC_DBG("Finished SDO request for slave %i.\n",
+                fsm->slave->ring_position);
+
+    // check for another SDO request
+    if (ec_fsm_master_action_process_sdo(fsm))
+        return; // processing another request
+
+    fsm->state = ec_fsm_master_state_end;
 }
 
 /*****************************************************************************/