Merge.
authorFlorian Pose <fp@igh-essen.com>
Thu, 16 Sep 2010 10:16:12 +0200
changeset 1963 6aaf77798445
parent 1962 df9b00bda8dc (diff)
parent 1961 48f536aefc18 (current diff)
child 1964 9327c261056f
Merge.
TODO
lib/master.c
lib/slave_config.c
master/cdev.c
master/ioctl.h
--- a/TODO	Thu Sep 16 09:08:11 2010 +0200
+++ b/TODO	Thu Sep 16 10:16:12 2010 +0200
@@ -13,41 +13,28 @@
 * Ethernet drivers:
     - Fix link detection in generic driver.
     - Add native drivers from 2.6.24 up to 2.6.31.
-* Change SDO index at runtime for SDO request.
-* Output skipped datagrams again.
-* Output warning when send_ext() is called in illegal context.
-* Implement CompleteAccess for SDO uploads.
-* Check for Enable SDO Complete Access flag.
-* Remove allow_scanning flag.
 * ethercat tool:
     - Output error after usage.
-    - Data type abbreviations.
     - Implement ranges for slaves and domains.
-    - Add -x switch for hex display.
-    - Implement CompleteAccess
-    - Implement --output-file argument in foe_read.
-    - Fix arguments of reg_read.
-    - Number layout for reg_read.
-    - Implement identifier parameter for cstruct command.
-    - Implement sync delimiter for cstruct command.
-    - Implement indent in 'ethercat ma'
-    - Implement 0xXXXX:YY format for specifying SDOs.
-    - Implement reading from stream for soe_write.
-    - Fix AL state display if error bit set.
+* Fix casting away constness during expected WC calculation.
+* Include SoE drive_no in ethercat tool.
+
+Future issues:
+
+* Remove allow_scanning flag.
+* Output skipped datagrams again.
+* Check for Enable SDO Complete Access flag.
+* Do not output 'SDO does not exist' when querying data type.
+* Always write down PDO assignment/mapping in doubt? If not, add an interface
+  for enforcing this.
+* Read AL status code on spontaneous state change.
 * recompile tool/CommandVersion.cpp if revision changes.
 * Log SoE IDNs with real name ([SP]-x-yyyy).
 * Output SoE IDN configurations in 'ethercat config'.
-* Fix casting away constness during expected WC calculation.
-* Read AL status code on spontaneous state change.
-* Fix clearing domains etc. when not activated and releasing.
-* Output -EPROTO if mailbox protocol is not supported.
 * Only output watchdog config if not default.
-* Do not output 'SDO does not exist' when querying data type.
-* Always write down PDO assignment/mapping in doubt? If not, add an interface
-  for enforcing this.
-
-Future issues:
-
+* 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?
@@ -80,6 +67,13 @@
     - Add a -n (numeric) switch.
 	- Check for unwanted options.
     - Fix number of digits in negative integer hex output.
+    - Data type abbreviations.
+    - Add -x switch for hex display.
+    - Implement CompleteAccess
+    - Implement --output-file argument in foe_read.
+    - Implement indent in 'ethercat ma'
+    - Implement 0xXXXX:YY format for specifying SDOs.
+    - Implement reading from stream for soe_write.
 * Simplify master fsm by introducing a common request state to handle external
   requests (replace write_sii, sdo_request, etc).
 * Write PDO mapping/assignment by default?
--- a/include/ecrt.h	Thu Sep 16 09:08:11 2010 +0200
+++ b/include/ecrt.h	Thu Sep 16 10:16:12 2010 +0200
@@ -674,6 +674,8 @@
         uint32_t *abort_code /**< Abort code of the SDO upload. */
         );
 
+#endif /* #ifndef __KERNEL__ */
+
 /** Executes an SoE write request.
  *
  * Starts writing an IDN and blocks until the request was processed, or an
@@ -685,10 +687,11 @@
 int ecrt_master_write_idn(
         ec_master_t *master, /**< EtherCAT master. */
         uint16_t slave_position, /**< Slave position. */
+        uint8_t drive_no, /**< Drive number. */
         uint16_t idn, /**< SoE IDN (see ecrt_slave_config_idn()). */
         uint8_t *data, /**< Pointer to data to write. */
         size_t data_size, /**< Size of data to write. */
-        uint32_t *error_code /**< Pointer to variable, where an SoE error code
+        uint16_t *error_code /**< Pointer to variable, where an SoE error code
                                can be stored. */
         );
 
@@ -703,17 +706,16 @@
 int ecrt_master_read_idn(
         ec_master_t *master, /**< EtherCAT master. */
         uint16_t slave_position, /**< Slave position. */
+        uint8_t drive_no, /**< Drive number. */
         uint16_t idn, /**< SoE IDN (see ecrt_slave_config_idn()). */
         uint8_t *target, /**< Pointer to memory where the read data can be
                            stored. */
         size_t target_size, /**< Size of the memory \a target points to. */
         size_t *result_size, /**< Actual size of the received data. */
-        uint32_t *error_code /**< Pointer to variable, where an SoE error code
+        uint16_t *error_code /**< Pointer to variable, where an SoE error code
                                can be stored. */
         );
 
-#endif /* #ifndef __KERNEL__ */
-
 /** Finishes the configuration phase and prepares for cyclic operation.
  *
  * This function tells the master that the configuration phase is finished and
@@ -1212,6 +1214,7 @@
  */
 int ecrt_slave_config_idn(
         ec_slave_config_t *sc, /**< Slave configuration. */
+        uint8_t drive_no, /**< Drive number. */
         uint16_t idn, /**< SoE IDN. */
         ec_al_state_t state, /**< AL state in which to write the IDN (PREOP or
                                SAFEOP). */
--- a/lib/master.c	Thu Sep 16 09:08:11 2010 +0200
+++ b/lib/master.c	Thu Sep 16 10:16:12 2010 +0200
@@ -384,11 +384,13 @@
 /*****************************************************************************/
 
 int ecrt_master_write_idn(ec_master_t *master, uint16_t slave_position,
-        uint16_t idn, uint8_t *data, size_t data_size, uint32_t *error_code)
+        uint8_t drive_no, uint16_t idn, uint8_t *data, size_t data_size,
+        uint16_t *error_code)
 {
     ec_ioctl_slave_soe_write_t io;
 
     io.slave_position = slave_position;
+    io.drive_no = drive_no;
     io.idn = idn;
     io.data_size = data_size;
     io.data = data;
@@ -407,12 +409,13 @@
 /*****************************************************************************/
 
 int ecrt_master_read_idn(ec_master_t *master, uint16_t slave_position,
-        uint16_t idn, uint8_t *target, size_t target_size,
-        size_t *result_size, uint32_t *error_code)
+        uint8_t drive_no, uint16_t idn, uint8_t *target, size_t target_size,
+        size_t *result_size, uint16_t *error_code)
 {
     ec_ioctl_slave_soe_read_t io;
 
     io.slave_position = slave_position;
+    io.drive_no = drive_no;
     io.idn = idn;
     io.mem_size = target_size;
     io.data = target;
--- a/lib/slave_config.c	Thu Sep 16 09:08:11 2010 +0200
+++ b/lib/slave_config.c	Thu Sep 16 10:16:12 2010 +0200
@@ -533,12 +533,13 @@
 
 /*****************************************************************************/
 
-int ecrt_slave_config_idn(ec_slave_config_t *sc, uint16_t idn,
-        ec_al_state_t al_state, const uint8_t *data, size_t size)
+int ecrt_slave_config_idn(ec_slave_config_t *sc, uint8_t drive_no,
+        uint16_t idn, ec_al_state_t al_state, const uint8_t *data, size_t size)
 {
     ec_ioctl_sc_idn_t io;
 
     io.config_index = sc->index;
+    io.drive_no = drive_no;
     io.idn = idn;
     io.al_state = al_state;
     io.data = data;
--- a/master/cdev.c	Thu Sep 16 09:08:11 2010 +0200
+++ b/master/cdev.c	Thu Sep 16 10:16:12 2010 +0200
@@ -894,7 +894,11 @@
 
     if (request.req.state != EC_INT_REQUEST_SUCCESS) {
         data.data_size = 0;
-        retval = -EIO;
+        if (request.req.errno) {
+            retval = -request.req.errno;
+        } else {
+            retval = -EIO;
+        }
     } else {
         if (request.req.data_size > data.target_size) {
             EC_MASTER_ERR(master, "Buffer too small.\n");
@@ -999,7 +1003,13 @@
 
     data.abort_code = request.req.abort_code;
 
-    retval = request.req.state == EC_INT_REQUEST_SUCCESS ? 0 : -EIO;
+    if (request.req.state == EC_INT_REQUEST_SUCCESS) {
+        retval = 0;
+    } else if (request.req.errno) {
+        retval = -request.req.errno;
+    } else {
+        retval = -EIO;
+    }
 
     if (__copy_to_user((void __user *) arg, &data, sizeof(data))) {
         retval = -EFAULT;
@@ -2477,7 +2487,7 @@
     up(&master->master_sem); // FIXME
 
     ret = ecrt_slave_config_idn(
-            sc, ioctl.idn, ioctl.al_state, data, ioctl.size);
+            sc, ioctl.drive_no, ioctl.idn, ioctl.al_state, data, ioctl.size);
     kfree(data);
     return ret;
 }
@@ -3332,86 +3342,40 @@
         )
 {
     ec_ioctl_slave_soe_read_t ioctl;
-    ec_master_soe_request_t request;
+    u8 *data;
     int retval;
 
     if (copy_from_user(&ioctl, (void __user *) arg, sizeof(ioctl))) {
         return -EFAULT;
     }
 
-    ec_soe_request_init(&request.req);
-    ec_soe_request_set_idn(&request.req, ioctl.idn);
-    ec_soe_request_read(&request.req);
-
-    if (down_interruptible(&master->master_sem))
-        return -EINTR;
-
-    if (!(request.slave = ec_master_find_slave(
-                    master, 0, ioctl.slave_position))) {
-        up(&master->master_sem);
-        ec_soe_request_clear(&request.req);
-        EC_MASTER_ERR(master, "Slave %u does not exist!\n",
-                ioctl.slave_position);
-        return -EINVAL;
-    }
-
-    // schedule request.
-    list_add_tail(&request.list, &request.slave->soe_requests);
-
-    up(&master->master_sem);
-
-    EC_SLAVE_DBG(request.slave, 1, "Scheduled SoE read request.\n");
-
-    // wait for processing through FSM
-    if (wait_event_interruptible(request.slave->soe_queue,
-                request.req.state != EC_INT_REQUEST_QUEUED)) {
-        // interrupted by signal
-        down(&master->master_sem);
-        if (request.req.state == EC_INT_REQUEST_QUEUED) {
-            list_del(&request.list);
-            up(&master->master_sem);
-            ec_soe_request_clear(&request.req);
-            return -EINTR;
-        }
-        // request already processing: interrupt not possible.
-        up(&master->master_sem);
-    }
-
-    // wait until master FSM has finished processing
-    wait_event(request.slave->soe_queue,
-            request.req.state != EC_INT_REQUEST_BUSY);
-
-    ioctl.error_code = request.req.error_code;
-
-    EC_SLAVE_DBG(request.slave, 1, "Read %zd bytes via SoE.\n",
-            request.req.data_size);
-
-    if (request.req.state != EC_INT_REQUEST_SUCCESS) {
-        ioctl.data_size = 0;
-        retval = -EIO;
-    } else {
-        if (request.req.data_size > ioctl.mem_size) {
-            EC_MASTER_ERR(master, "Buffer too small.\n");
-            ec_soe_request_clear(&request.req);
-            return -EOVERFLOW;
-        }
-        ioctl.data_size = request.req.data_size;
-        if (copy_to_user((void __user *) ioctl.data,
-                    request.req.data, ioctl.data_size)) {
-            ec_soe_request_clear(&request.req);
-            return -EFAULT;
-        }
-        retval = 0;
-    }
+    data = kmalloc(ioctl.mem_size, GFP_KERNEL);
+    if (!data) {
+        EC_MASTER_ERR(master, "Failed to allocate %u bytes of IDN data.\n",
+                ioctl.mem_size);
+        return -ENOMEM;
+    }
+
+    retval = ecrt_master_read_idn(master, ioctl.slave_position,
+            ioctl.drive_no, ioctl.idn, data, ioctl.mem_size, &ioctl.data_size,
+            &ioctl.error_code);
+    if (retval) {
+        kfree(data);
+        return retval;
+    }
+
+    if (copy_to_user((void __user *) ioctl.data,
+                data, ioctl.data_size)) {
+        kfree(data);
+        return -EFAULT;
+    }
+    kfree(data);
 
     if (__copy_to_user((void __user *) arg, &ioctl, sizeof(ioctl))) {
         retval = -EFAULT;
     }
 
-    EC_SLAVE_DBG(request.slave, 1, "Finished SoE read request.\n");
-
-    ec_soe_request_clear(&request.req);
-
+    EC_MASTER_DBG(master, 1, "Finished SoE read request.\n");
     return retval;
 }
 
@@ -3425,79 +3389,37 @@
         )
 {
     ec_ioctl_slave_soe_write_t ioctl;
-    ec_master_soe_request_t request;
+    u8 *data;
     int retval;
 
     if (copy_from_user(&ioctl, (void __user *) arg, sizeof(ioctl))) {
         return -EFAULT;
     }
 
-    INIT_LIST_HEAD(&request.list);
-
-    ec_soe_request_init(&request.req);
-    ec_soe_request_set_idn(&request.req, ioctl.idn);
-
-    if (ec_soe_request_alloc(&request.req, ioctl.data_size)) {
-        ec_soe_request_clear(&request.req);
+    data = kmalloc(ioctl.data_size, GFP_KERNEL);
+    if (!data) {
+        EC_MASTER_ERR(master, "Failed to allocate %u bytes of IDN data.\n",
+                ioctl.data_size);
         return -ENOMEM;
     }
-    if (copy_from_user(request.req.data,
-                (void __user *) ioctl.data, ioctl.data_size)) {
-        ec_soe_request_clear(&request.req);
-        return -EFAULT;
-    }
-    request.req.data_size = ioctl.data_size;
-    ec_soe_request_write(&request.req);
-
-    if (down_interruptible(&master->master_sem))
-        return -EINTR;
-
-    if (!(request.slave = ec_master_find_slave(
-                    master, 0, ioctl.slave_position))) {
-        up(&master->master_sem);
-        EC_MASTER_ERR(master, "Slave %u does not exist!\n",
-                ioctl.slave_position);
-        ec_soe_request_clear(&request.req);
-        return -EINVAL;
-    }
-
-    EC_SLAVE_DBG(request.slave, 1, "Scheduling SoE write request.\n");
-
-    // schedule SoE write request.
-    list_add_tail(&request.list, &request.slave->soe_requests);
-
-    up(&master->master_sem);
-
-    // wait for processing through FSM
-    if (wait_event_interruptible(request.slave->soe_queue,
-                request.req.state != EC_INT_REQUEST_QUEUED)) {
-        // interrupted by signal
-        down(&master->master_sem);
-        if (request.req.state == EC_INT_REQUEST_QUEUED) {
-            // abort request
-            list_del(&request.list);
-            up(&master->master_sem);
-            ec_soe_request_clear(&request.req);
-            return -EINTR;
-        }
-        up(&master->master_sem);
-    }
-
-    // wait until master FSM has finished processing
-    wait_event(request.slave->soe_queue,
-            request.req.state != EC_INT_REQUEST_BUSY);
-
-    ioctl.error_code = request.req.error_code;
-    retval = request.req.state == EC_INT_REQUEST_SUCCESS ? 0 : -EIO;
+    if (copy_from_user(data, (void __user *) ioctl.data, ioctl.data_size)) {
+        kfree(data);
+        return -EFAULT;
+    }
+
+    retval = ecrt_master_write_idn(master, ioctl.slave_position,
+            ioctl.drive_no, ioctl.idn, data, ioctl.data_size,
+            &ioctl.error_code);
+    kfree(data);
+    if (retval) {
+        return retval;
+    }
 
     if (__copy_to_user((void __user *) arg, &ioctl, sizeof(ioctl))) {
         retval = -EFAULT;
     }
 
-    ec_soe_request_clear(&request.req);
-
-    EC_SLAVE_DBG(request.slave, 1, "Finished SoE write request.\n");
-
+    EC_MASTER_DBG(master, 1, "Finished SoE write request.\n");
     return retval;
 }
 
--- a/master/device.c	Thu Sep 16 09:08:11 2010 +0200
+++ b/master/device.c	Thu Sep 16 10:16:12 2010 +0200
@@ -315,7 +315,7 @@
         u32 tx_frame_rate =
             (u32) (device->tx_count - device->last_tx_count) * 1000;
         u32 tx_byte_rate =
-            (device->tx_bytes - device->last_tx_bytes) * 1000;
+            (device->tx_bytes - device->last_tx_bytes);
         u64 loss = device->tx_count - device->rx_count;
         s32 loss_rate = (s32) (loss - device->last_loss) * 1000;
         for (i = 0; i < EC_RATE_COUNT; i++) {
--- a/master/fsm_coe.c	Thu Sep 16 09:08:11 2010 +0200
+++ b/master/fsm_coe.c	Thu Sep 16 10:16:12 2010 +0200
@@ -1103,6 +1103,7 @@
 
     if (!(slave->sii.mailbox_protocols & EC_MBOX_COE)) {
         EC_SLAVE_ERR(slave, "Slave does not support CoE!\n");
+        request->errno = EPROTONOSUPPORT;
         fsm->state = ec_fsm_coe_error;
         return;
     }
@@ -1110,6 +1111,7 @@
     if (slave->configured_rx_mailbox_size < 
             EC_MBOX_HEADER_SIZE + EC_COE_DOWN_REQ_HEADER_SIZE) {
         EC_SLAVE_ERR(slave, "Mailbox too small!\n");
+        request->errno = EOVERFLOW;
         fsm->state = ec_fsm_coe_error;
         return;
     }
@@ -1118,6 +1120,7 @@
         data = ec_slave_mbox_prepare_send(slave, datagram, 0x03,
                 EC_COE_DOWN_REQ_HEADER_SIZE);
         if (IS_ERR(data)) {
+            request->errno = PTR_ERR(data);
             fsm->state = ec_fsm_coe_error;
             return;
         }
@@ -1159,6 +1162,7 @@
         data = ec_slave_mbox_prepare_send(slave, datagram, 0x03,
                 data_size);
         if (IS_ERR(data)) {
+            request->errno = PTR_ERR(data);
             fsm->state = ec_fsm_coe_error;
             return;
         }
@@ -1212,6 +1216,7 @@
         return; // FIXME: check for response first?
 
     if (datagram->state != EC_DATAGRAM_RECEIVED) {
+        fsm->request->errno = EIO;
         fsm->state = ec_fsm_coe_error;
         EC_SLAVE_ERR(slave, "Failed to receive CoE download"
                 " request datagram: ");
@@ -1233,6 +1238,7 @@
                 return;
             }
         }
+        fsm->request->errno = EIO;
         fsm->state = ec_fsm_coe_error;
         EC_SLAVE_ERR(slave, "Reception of CoE download request"
                 " for SDO 0x%04x:%x failed with timeout after %lu ms: ",
@@ -1268,6 +1274,7 @@
         return;
 
     if (datagram->state != EC_DATAGRAM_RECEIVED) {
+        fsm->request->errno = EIO;
         fsm->state = ec_fsm_coe_error;
         EC_SLAVE_ERR(slave, "Failed to receive CoE mailbox check"
                 " datagram: ");
@@ -1276,6 +1283,7 @@
     }
 
     if (datagram->working_counter != 1) {
+        fsm->request->errno = EIO;
         fsm->state = ec_fsm_coe_error;
         EC_SLAVE_ERR(slave, "Reception of CoE mailbox check"
                 " datagram failed: ");
@@ -1287,6 +1295,7 @@
         unsigned long diff_ms =
             (datagram->jiffies_received - fsm->jiffies_start) * 1000 / HZ;
         if (diff_ms >= fsm->request->response_timeout) {
+            fsm->request->errno = EIO;
             fsm->state = ec_fsm_coe_error;
             EC_SLAVE_ERR(slave, "Timeout after %lu ms while waiting"
                     " for SDO 0x%04x:%x download response.\n", diff_ms,
@@ -1343,6 +1352,7 @@
     data = ec_slave_mbox_prepare_send(slave, datagram, 0x03,
             data_size);
     if (IS_ERR(data)) {
+        request->errno = PTR_ERR(data);
         fsm->state = ec_fsm_coe_error;
         return;
     }
@@ -1389,6 +1399,7 @@
         return; // FIXME: request again?
 
     if (datagram->state != EC_DATAGRAM_RECEIVED) {
+        request->errno = EIO;
         fsm->state = ec_fsm_coe_error;
         EC_SLAVE_ERR(slave, "Failed to receive CoE download"
                 " response datagram: ");
@@ -1397,6 +1408,7 @@
     }
 
     if (datagram->working_counter != 1) {
+        request->errno = EIO;
         fsm->state = ec_fsm_coe_error;
         EC_SLAVE_ERR(slave, "Reception of CoE download response failed: ");
         ec_datagram_print_wc_error(datagram);
@@ -1405,11 +1417,13 @@
 
     data = ec_slave_mbox_fetch(slave, datagram, &mbox_prot, &rec_size);
     if (IS_ERR(data)) {
+        request->errno = PTR_ERR(data);
         fsm->state = ec_fsm_coe_error;
         return;
     }
 
     if (mbox_prot != 0x03) { // CoE
+        request->errno = EIO;
         fsm->state = ec_fsm_coe_error;
         EC_SLAVE_ERR(slave, "Received mailbox protocol 0x%02X as response.\n",
                 mbox_prot);
@@ -1430,6 +1444,7 @@
     }
 
     if (rec_size < 6) {
+        request->errno = EIO;
         fsm->state = ec_fsm_coe_error;
         EC_SLAVE_ERR(slave, "Received data are too small (%zu bytes):\n",
                 rec_size);
@@ -1440,6 +1455,7 @@
     if (EC_READ_U16(data) >> 12 == 0x2 && // SDO request
         EC_READ_U8 (data + 2) >> 5 == 0x4) { // abort SDO transfer request
         char subidxstr[10];
+        request->errno = EIO;
         fsm->state = ec_fsm_coe_error;
         if (request->complete_access) {
             subidxstr[0] = 0x00;
@@ -1497,6 +1513,7 @@
         return;
 
     if (datagram->state != EC_DATAGRAM_RECEIVED) {
+        fsm->request->errno = EIO;
         fsm->state = ec_fsm_coe_error;
         EC_SLAVE_ERR(slave, "Failed to receive CoE mailbox check datagram: ");
         ec_datagram_print_state(datagram);
@@ -1504,6 +1521,7 @@
     }
 
     if (datagram->working_counter != 1) {
+        fsm->request->errno = EIO;
         fsm->state = ec_fsm_coe_error;
         EC_SLAVE_ERR(slave, "Reception of CoE mailbox segment check"
                 " datagram failed: ");
@@ -1515,6 +1533,7 @@
         unsigned long diff_ms =
             (datagram->jiffies_received - fsm->jiffies_start) * 1000 / HZ;
         if (diff_ms >= fsm->request->response_timeout) {
+            fsm->request->errno = EIO;
             fsm->state = ec_fsm_coe_error;
             EC_SLAVE_ERR(slave, "Timeout while waiting for SDO download"
                     " segment response.\n");
@@ -1553,6 +1572,7 @@
         return; // FIXME: request again?
 
     if (datagram->state != EC_DATAGRAM_RECEIVED) {
+        request->errno = EIO;
         fsm->state = ec_fsm_coe_error;
         EC_SLAVE_ERR(slave, "Failed to receive CoE download response"
                 " datagram: ");
@@ -1561,6 +1581,7 @@
     }
 
     if (datagram->working_counter != 1) {
+        request->errno = EIO;
         fsm->state = ec_fsm_coe_error;
         EC_SLAVE_ERR(slave, "Reception of CoE download response failed: ");
         ec_datagram_print_wc_error(datagram);
@@ -1569,11 +1590,13 @@
 
     data = ec_slave_mbox_fetch(slave, datagram, &mbox_prot, &rec_size);
     if (IS_ERR(data)) {
+        request->errno = PTR_ERR(data);
         fsm->state = ec_fsm_coe_error;
         return;
     }
 
     if (mbox_prot != 0x03) { // CoE
+        request->errno = EIO;
         fsm->state = ec_fsm_coe_error;
         EC_SLAVE_ERR(slave, "Received mailbox protocol 0x%02X as response.\n",
                 mbox_prot);
@@ -1594,6 +1617,7 @@
     }
 
     if (rec_size < 6) {
+        request->errno = EIO;
         fsm->state = ec_fsm_coe_error;
         EC_SLAVE_ERR(slave, "Received data are too small (%zu bytes):\n",
                 rec_size);
@@ -1604,6 +1628,7 @@
     if (EC_READ_U16(data) >> 12 == 0x2 && // SDO request
         EC_READ_U8 (data + 2) >> 5 == 0x4) { // abort SDO transfer request
         char subidxstr[10];
+        request->errno = EIO;
         fsm->state = ec_fsm_coe_error;
         if (request->complete_access) {
             subidxstr[0] = 0x00;
@@ -1640,6 +1665,7 @@
         EC_SLAVE_ERR(slave, "Invalid toggle received during"
                 " segmented download:\n");
         ec_print_data(data, rec_size);
+        request->errno = EIO;
         fsm->state = ec_fsm_coe_error;
         return;
     }
@@ -1671,12 +1697,14 @@
 
     if (!(slave->sii.mailbox_protocols & EC_MBOX_COE)) {
         EC_SLAVE_ERR(slave, "Slave does not support CoE!\n");
+        request->errno = EPROTONOSUPPORT;
         fsm->state = ec_fsm_coe_error;
         return;
     }
 
     data = ec_slave_mbox_prepare_send(slave, datagram, 0x03, 10);
     if (IS_ERR(data)) {
+        request->errno = PTR_ERR(data);
         fsm->state = ec_fsm_coe_error;
         return;
     }
@@ -1714,6 +1742,7 @@
         return; // FIXME: check for response first?
 
     if (datagram->state != EC_DATAGRAM_RECEIVED) {
+        fsm->request->errno = EIO;
         fsm->state = ec_fsm_coe_error;
         EC_SLAVE_ERR(slave, "Failed to receive CoE upload request: ");
         ec_datagram_print_state(datagram);
@@ -1734,6 +1763,7 @@
                 return;
             }
         }
+        fsm->request->errno = EIO;
         fsm->state = ec_fsm_coe_error;
         EC_SLAVE_ERR(slave, "Reception of CoE upload request for"
                 " SDO 0x%04x:%x failed with timeout after %lu ms: ",
@@ -1771,6 +1801,7 @@
         return;
 
     if (datagram->state != EC_DATAGRAM_RECEIVED) {
+        fsm->request->errno = EIO;
         fsm->state = ec_fsm_coe_error;
         EC_SLAVE_ERR(slave, "Failed to receive CoE mailbox check datagram: ");
         ec_datagram_print_state(datagram);
@@ -1778,6 +1809,7 @@
     }
 
     if (datagram->working_counter != 1) {
+        fsm->request->errno = EIO;
         fsm->state = ec_fsm_coe_error;
         EC_SLAVE_ERR(slave, "Reception of CoE mailbox check"
                 " datagram failed: ");
@@ -1789,6 +1821,7 @@
         unsigned long diff_ms =
             (datagram->jiffies_received - fsm->jiffies_start) * 1000 / HZ;
         if (diff_ms >= fsm->request->response_timeout) {
+            fsm->request->errno = EIO;
             fsm->state = ec_fsm_coe_error;
             EC_SLAVE_ERR(slave, "Timeout after %lu ms while waiting for"
                     " SDO 0x%04x:%x upload response.\n", diff_ms,
@@ -1818,6 +1851,7 @@
     uint8_t *data =
         ec_slave_mbox_prepare_send(fsm->slave, fsm->datagram, 0x03, 10);
     if (IS_ERR(data)) {
+        fsm->request->errno = PTR_ERR(data);
         fsm->state = ec_fsm_coe_error;
         return;
     }
@@ -1850,11 +1884,13 @@
     size_t rec_size, data_size;
     ec_sdo_request_t *request = fsm->request;
     unsigned int expedited, size_specified;
+    int ret;
 
     if (datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--)
         return; // FIXME: request again?
 
     if (datagram->state != EC_DATAGRAM_RECEIVED) {
+        request->errno = EIO;
         fsm->state = ec_fsm_coe_error;
         EC_SLAVE_ERR(slave, "Failed to receive CoE upload response"
                 " datagram: ");
@@ -1863,6 +1899,7 @@
     }
 
     if (datagram->working_counter != 1) {
+        request->errno = EIO;
         fsm->state = ec_fsm_coe_error;
         EC_SLAVE_ERR(slave, "Reception of CoE upload response failed: ");
         ec_datagram_print_wc_error(datagram);
@@ -1871,6 +1908,7 @@
 
     data = ec_slave_mbox_fetch(slave, datagram, &mbox_prot, &rec_size);
     if (IS_ERR(data)) {
+        request->errno = PTR_ERR(data);
         fsm->state = ec_fsm_coe_error;
         return;
     }
@@ -1881,6 +1919,7 @@
     }
 
     if (mbox_prot != 0x03) { // CoE
+        request->errno = EIO;
         fsm->state = ec_fsm_coe_error;
         EC_SLAVE_WARN(slave, "Received mailbox protocol 0x%02X"
                 " as response.\n", mbox_prot);
@@ -1896,6 +1935,7 @@
     }
 
     if (rec_size < 6) {
+        request->errno = EIO;
         fsm->state = ec_fsm_coe_error;
         EC_SLAVE_ERR(slave, "Received currupted SDO upload response"
                 " (%zu bytes)!\n", rec_size);
@@ -1913,6 +1953,7 @@
         } else {
             EC_SLAVE_ERR(slave, "No abort message.\n");
         }
+        request->errno = EIO;
         fsm->state = ec_fsm_coe_error;
         return;
     }
@@ -1923,6 +1964,7 @@
                 " uploading SDO 0x%04X:%02X.\n",
                 request->index, request->subindex);
         ec_print_data(data, rec_size);
+        request->errno = EIO;
         fsm->state = ec_fsm_coe_error;
         return;
     }
@@ -1955,6 +1997,7 @@
         }
 
         if (rec_size < 6 + fsm->complete_size) {
+            request->errno = EIO;
             fsm->state = ec_fsm_coe_error;
             EC_SLAVE_ERR(slave, "Received corrupted SDO expedited upload"
                     " response (only %zu bytes)!\n", rec_size);
@@ -1962,12 +2005,15 @@
             return;
         }
 
-        if (ec_sdo_request_copy_data(request, data + 6, fsm->complete_size)) {
+        ret = ec_sdo_request_copy_data(request, data + 6, fsm->complete_size);
+        if (ret) {
+            request->errno = -ret;
             fsm->state = ec_fsm_coe_error;
             return;
         }
     } else { // normal
         if (rec_size < 10) {
+            request->errno = EIO;
             fsm->state = ec_fsm_coe_error;
             EC_SLAVE_ERR(slave, "Received currupted SDO normal upload"
                     " response (only %zu bytes)!\n", rec_size);
@@ -1979,18 +2025,23 @@
         fsm->complete_size = EC_READ_U32(data + 6);
 
         if (!fsm->complete_size) {
+            request->errno = EIO;
             fsm->state = ec_fsm_coe_error;
             EC_SLAVE_ERR(slave, "No complete size supplied!\n");
             ec_print_data(data, rec_size);
             return;
         }
 
-        if (ec_sdo_request_alloc(request, fsm->complete_size)) {
+        ret = ec_sdo_request_alloc(request, fsm->complete_size);
+        if (ret) {
+            request->errno = -ret;
             fsm->state = ec_fsm_coe_error;
             return;
         }
 
-        if (ec_sdo_request_copy_data(request, data + 10, data_size)) {
+        ret = ec_sdo_request_copy_data(request, data + 10, data_size);
+        if (ret) {
+            request->errno = -ret;
             fsm->state = ec_fsm_coe_error;
             return;
         }
@@ -2031,6 +2082,7 @@
         return; // FIXME: check for response first?
 
     if (datagram->state != EC_DATAGRAM_RECEIVED) {
+        fsm->request->errno = EIO;
         fsm->state = ec_fsm_coe_error;
         EC_SLAVE_ERR(slave, "Failed to receive CoE upload segment"
                 " request datagram: ");
@@ -2039,6 +2091,7 @@
     }
 
     if (datagram->working_counter != 1) {
+        fsm->request->errno = EIO;
         fsm->state = ec_fsm_coe_error;
         EC_SLAVE_ERR(slave, "Reception of CoE upload segment"
                 " request failed: ");
@@ -2068,6 +2121,7 @@
         return;
 
     if (datagram->state != EC_DATAGRAM_RECEIVED) {
+        fsm->request->errno = EIO;
         fsm->state = ec_fsm_coe_error;
         EC_SLAVE_ERR(slave, "Failed to receive CoE mailbox check"
                 " datagram: ");
@@ -2076,6 +2130,7 @@
     }
 
     if (datagram->working_counter != 1) {
+        fsm->request->errno = EIO;
         fsm->state = ec_fsm_coe_error;
         EC_SLAVE_ERR(slave, "Reception of CoE mailbox check datagram"
                 " failed: ");
@@ -2087,6 +2142,7 @@
         unsigned long diff_ms =
             (datagram->jiffies_received - fsm->jiffies_start) * 1000 / HZ;
         if (diff_ms >= fsm->request->response_timeout) {
+            fsm->request->errno = EIO;
             fsm->state = ec_fsm_coe_error;
             EC_SLAVE_ERR(slave, "Timeout while waiting for SDO upload"
                     " segment response.\n");
@@ -2125,6 +2181,7 @@
         return; // FIXME: request again?
 
     if (datagram->state != EC_DATAGRAM_RECEIVED) {
+        request->errno = EIO;
         fsm->state = ec_fsm_coe_error;
         EC_SLAVE_ERR(slave, "Failed to receive CoE upload segment"
                 " response datagram: ");
@@ -2133,6 +2190,7 @@
     }
 
     if (datagram->working_counter != 1) {
+        request->errno = EIO;
         fsm->state = ec_fsm_coe_error;
         EC_SLAVE_ERR(slave, "Reception of CoE upload segment"
                 " response failed: ");
@@ -2142,6 +2200,7 @@
 
     data = ec_slave_mbox_fetch(slave, datagram, &mbox_prot, &rec_size);
     if (IS_ERR(data)) {
+        request->errno = PTR_ERR(data);
         fsm->state = ec_fsm_coe_error;
         return;
     }
@@ -2154,6 +2213,7 @@
     if (mbox_prot != 0x03) { // CoE
         EC_SLAVE_ERR(slave, "Received mailbox protocol 0x%02X as response.\n",
                 mbox_prot);
+        request->errno = EIO;
         fsm->state = ec_fsm_coe_error;
         return;
     }
@@ -2170,6 +2230,7 @@
         EC_SLAVE_ERR(slave, "Received currupted SDO upload"
                 " segment response!\n");
         ec_print_data(data, rec_size);
+        request->errno = EIO;
         fsm->state = ec_fsm_coe_error;
         return;
     }
@@ -2180,6 +2241,7 @@
                request->index, request->subindex);
         request->abort_code = EC_READ_U32(data + 6);
         ec_canopen_abort_msg(slave, request->abort_code);
+        request->errno = EIO;
         fsm->state = ec_fsm_coe_error;
         return;
     }
@@ -2208,6 +2270,7 @@
         EC_SLAVE_ERR(slave, "SDO upload 0x%04X:%02X failed: Fragment"
                 " exceeding complete size!\n",
                 request->index, request->subindex);
+        request->errno = EOVERFLOW;
         fsm->state = ec_fsm_coe_error;
         return;
     }
--- a/master/fsm_sii.c	Thu Sep 16 09:08:11 2010 +0200
+++ b/master/fsm_sii.c	Thu Sep 16 10:16:12 2010 +0200
@@ -277,7 +277,8 @@
 #endif
 
     if (EC_READ_U8(datagram->data + 1) & 0x20) {
-        EC_SLAVE_ERR(fsm->slave, "SII: Error on last SII command!\n");
+        EC_SLAVE_ERR(fsm->slave, "Error on last command while"
+                " reading from SII word 0x%04x.\n", fsm->word_offset);
         fsm->state = ec_fsm_sii_state_error;
         return;
     }
--- a/master/fsm_slave_scan.c	Thu Sep 16 09:08:11 2010 +0200
+++ b/master/fsm_slave_scan.c	Thu Sep 16 10:16:12 2010 +0200
@@ -498,12 +498,14 @@
     ec_slave_t *slave = fsm->slave;
     uint16_t cat_type, cat_size;
 
-    if (ec_fsm_sii_exec(&fsm->fsm_sii)) return;
+    if (ec_fsm_sii_exec(&fsm->fsm_sii))
+        return;
 
     if (!ec_fsm_sii_success(&fsm->fsm_sii)) {
         fsm->slave->error_flag = 1;
         fsm->state = ec_fsm_slave_scan_state_error;
-        EC_SLAVE_ERR(slave, "Failed to read SII size.\n");
+        EC_SLAVE_ERR(slave, "Failed to determine SII content size:"
+                " Reading word offset 0x%04x failed.\n", fsm->sii_offset);
         return;
     }
 
--- a/master/fsm_soe.c	Thu Sep 16 09:08:11 2010 +0200
+++ b/master/fsm_soe.c	Thu Sep 16 10:16:12 2010 +0200
@@ -201,7 +201,8 @@
     ec_soe_request_t *request = fsm->request;
     uint8_t *data;
 
-    EC_SLAVE_DBG(slave, 1, "Reading IDN 0x%04X.\n", request->idn);
+    EC_SLAVE_DBG(slave, 1, "Reading IDN 0x%04X of drive %u.\n", request->idn,
+            request->drive_no);
 
     if (!(slave->sii.mailbox_protocols & EC_MBOX_SOE)) {
         EC_SLAVE_ERR(slave, "Slave does not support SoE!\n");
@@ -218,7 +219,7 @@
         return;
     }
 
-    EC_WRITE_U8(data, OPCODE_READ_REQUEST);
+    EC_WRITE_U8(data, OPCODE_READ_REQUEST | (request->drive_no & 0x07) << 5);
     EC_WRITE_U8(data + 1, 1 << 6); // request value
     EC_WRITE_U16(data + 2, request->idn);
 
@@ -492,7 +493,8 @@
         return;
     }
 
-    EC_WRITE_U8(data, OPCODE_WRITE_REQUEST | incomplete << 3);
+    EC_WRITE_U8(data, OPCODE_WRITE_REQUEST | incomplete << 3 |
+            (req->drive_no & 0x07) << 5);
     EC_WRITE_U8(data + 1, 1 << 6); // only value included
     EC_WRITE_U16(data + 2, incomplete ? fragments_left : req->idn);
     memcpy(data + 4, req->data + fsm->offset, fragment_size);
@@ -517,8 +519,8 @@
     ec_slave_t *slave = fsm->slave;
     ec_soe_request_t *req = fsm->request;
 
-    EC_SLAVE_DBG(slave, 1, "Writing IDN 0x%04X (%zu byte).\n",
-            req->idn, req->data_size);
+    EC_SLAVE_DBG(slave, 1, "Writing IDN 0x%04X of drive %u (%zu byte).\n",
+            req->idn, req->drive_no, req->data_size);
 
     if (!(slave->sii.mailbox_protocols & EC_MBOX_SOE)) {
         EC_SLAVE_ERR(slave, "Slave does not support SoE!\n");
--- a/master/ioctl.h	Thu Sep 16 09:08:11 2010 +0200
+++ b/master/ioctl.h	Thu Sep 16 10:16:12 2010 +0200
@@ -56,7 +56,7 @@
  *
  * Increment this when changing the ioctl interface!
  */
-#define EC_IOCTL_VERSION_MAGIC 7
+#define EC_IOCTL_VERSION_MAGIC 8
 
 // Command-line tool
 #define EC_IOCTL_MODULE                EC_IOR(0x00, ec_ioctl_module_t)
@@ -414,6 +414,7 @@
 typedef struct {
     // inputs
     uint16_t slave_position;
+    uint8_t drive_no;
     uint16_t idn;
     uint32_t mem_size;
     uint8_t *data;
@@ -428,6 +429,7 @@
 typedef struct {
     // inputs
     uint16_t slave_position;
+    uint8_t drive_no;
     uint16_t idn;
     uint32_t data_size;
     uint8_t *data;
@@ -583,6 +585,7 @@
 typedef struct {
     // inputs
     uint32_t config_index;
+    uint8_t drive_no;
     uint16_t idn;
     ec_al_state_t al_state;
     const uint8_t *data;
--- a/master/master.c	Thu Sep 16 09:08:11 2010 +0200
+++ b/master/master.c	Thu Sep 16 10:16:12 2010 +0200
@@ -2344,6 +2344,163 @@
 
 /*****************************************************************************/
 
+int ecrt_master_write_idn(ec_master_t *master, uint16_t slave_position,
+        uint8_t drive_no, uint16_t idn, uint8_t *data, size_t data_size,
+        uint16_t *error_code)
+{
+    ec_master_soe_request_t request;
+    int retval;
+
+    if (drive_no > 7) {
+        EC_MASTER_ERR(master, "Invalid drive number!\n");
+        return -EINVAL;
+    }
+
+    INIT_LIST_HEAD(&request.list);
+    ec_soe_request_init(&request.req);
+    ec_soe_request_set_drive_no(&request.req, drive_no);
+    ec_soe_request_set_idn(&request.req, idn);
+
+    if (ec_soe_request_alloc(&request.req, data_size)) {
+        ec_soe_request_clear(&request.req);
+        return -ENOMEM;
+    }
+
+    memcpy(request.req.data, data, data_size);
+    request.req.data_size = data_size;
+    ec_soe_request_write(&request.req);
+
+    if (down_interruptible(&master->master_sem))
+        return -EINTR;
+
+    if (!(request.slave = ec_master_find_slave(
+                    master, 0, slave_position))) {
+        up(&master->master_sem);
+        EC_MASTER_ERR(master, "Slave %u does not exist!\n",
+                slave_position);
+        ec_soe_request_clear(&request.req);
+        return -EINVAL;
+    }
+
+    EC_SLAVE_DBG(request.slave, 1, "Scheduling SoE write request.\n");
+
+    // schedule SoE write request.
+    list_add_tail(&request.list, &request.slave->soe_requests);
+
+    up(&master->master_sem);
+
+    // wait for processing through FSM
+    if (wait_event_interruptible(request.slave->soe_queue,
+                request.req.state != EC_INT_REQUEST_QUEUED)) {
+        // interrupted by signal
+        down(&master->master_sem);
+        if (request.req.state == EC_INT_REQUEST_QUEUED) {
+            // abort request
+            list_del(&request.list);
+            up(&master->master_sem);
+            ec_soe_request_clear(&request.req);
+            return -EINTR;
+        }
+        up(&master->master_sem);
+    }
+
+    // wait until master FSM has finished processing
+    wait_event(request.slave->soe_queue,
+            request.req.state != EC_INT_REQUEST_BUSY);
+
+    if (error_code) {
+        *error_code = request.req.error_code;
+    }
+    retval = request.req.state == EC_INT_REQUEST_SUCCESS ? 0 : -EIO;
+    ec_soe_request_clear(&request.req);
+
+    return retval;
+}
+
+/*****************************************************************************/
+
+int ecrt_master_read_idn(ec_master_t *master, uint16_t slave_position,
+        uint8_t drive_no, uint16_t idn, uint8_t *target, size_t target_size,
+        size_t *result_size, uint16_t *error_code)
+{
+    ec_master_soe_request_t request;
+
+    if (drive_no > 7) {
+        EC_MASTER_ERR(master, "Invalid drive number!\n");
+        return -EINVAL;
+    }
+
+    INIT_LIST_HEAD(&request.list);
+    ec_soe_request_init(&request.req);
+    ec_soe_request_set_drive_no(&request.req, drive_no);
+    ec_soe_request_set_idn(&request.req, idn);
+    ec_soe_request_read(&request.req);
+
+    if (down_interruptible(&master->master_sem))
+        return -EINTR;
+
+    if (!(request.slave = ec_master_find_slave(master, 0, slave_position))) {
+        up(&master->master_sem);
+        ec_soe_request_clear(&request.req);
+        EC_MASTER_ERR(master, "Slave %u does not exist!\n", slave_position);
+        return -EINVAL;
+    }
+
+    // schedule request.
+    list_add_tail(&request.list, &request.slave->soe_requests);
+
+    up(&master->master_sem);
+
+    EC_SLAVE_DBG(request.slave, 1, "Scheduled SoE read request.\n");
+
+    // wait for processing through FSM
+    if (wait_event_interruptible(request.slave->soe_queue,
+                request.req.state != EC_INT_REQUEST_QUEUED)) {
+        // interrupted by signal
+        down(&master->master_sem);
+        if (request.req.state == EC_INT_REQUEST_QUEUED) {
+            list_del(&request.list);
+            up(&master->master_sem);
+            ec_soe_request_clear(&request.req);
+            return -EINTR;
+        }
+        // request already processing: interrupt not possible.
+        up(&master->master_sem);
+    }
+
+    // wait until master FSM has finished processing
+    wait_event(request.slave->soe_queue,
+            request.req.state != EC_INT_REQUEST_BUSY);
+
+    if (error_code) {
+        *error_code = request.req.error_code;
+    }
+
+    EC_SLAVE_DBG(request.slave, 1, "Read %zd bytes via SoE.\n",
+            request.req.data_size);
+
+    if (request.req.state != EC_INT_REQUEST_SUCCESS) {
+        if (result_size) {
+            *result_size = 0;
+        }
+        ec_soe_request_clear(&request.req);
+        return -EIO;
+    } else {
+        if (request.req.data_size > target_size) {
+            EC_MASTER_ERR(master, "Buffer too small.\n");
+            ec_soe_request_clear(&request.req);
+            return -EOVERFLOW;
+        }
+        if (result_size) {
+            *result_size = request.req.data_size;
+        }
+        memcpy(target, request.req.data, request.req.data_size);
+        return 0;
+    }
+}
+
+/*****************************************************************************/
+
 /** \cond */
 
 EXPORT_SYMBOL(ecrt_master_create_domain);
@@ -2362,6 +2519,8 @@
 EXPORT_SYMBOL(ecrt_master_sync_slave_clocks);
 EXPORT_SYMBOL(ecrt_master_sync_monitor_queue);
 EXPORT_SYMBOL(ecrt_master_sync_monitor_process);
+EXPORT_SYMBOL(ecrt_master_write_idn);
+EXPORT_SYMBOL(ecrt_master_read_idn);
 
 /** \endcond */
 
--- a/master/sdo_request.c	Thu Sep 16 09:08:11 2010 +0200
+++ b/master/sdo_request.c	Thu Sep 16 10:16:12 2010 +0200
@@ -64,6 +64,7 @@
     req->issue_timeout = 0; // no timeout
     req->response_timeout = EC_SDO_REQUEST_RESPONSE_TIMEOUT;
     req->state = EC_INT_REQUEST_INIT;
+    req->errno = 0;
     req->abort_code = 0x00000000;
 }
 
@@ -226,6 +227,7 @@
 {
     req->dir = EC_DIR_INPUT;
     req->state = EC_INT_REQUEST_QUEUED;
+    req->errno = 0;
     req->abort_code = 0x00000000;
     req->jiffies_start = jiffies;
 }
@@ -236,6 +238,7 @@
 {
     req->dir = EC_DIR_OUTPUT;
     req->state = EC_INT_REQUEST_QUEUED;
+    req->errno = 0;
     req->abort_code = 0x00000000;
     req->jiffies_start = jiffies;
 }
--- a/master/sdo_request.h	Thu Sep 16 09:08:11 2010 +0200
+++ b/master/sdo_request.h	Thu Sep 16 10:16:12 2010 +0200
@@ -64,6 +64,7 @@
     unsigned long jiffies_start; /**< Jiffies, when the request was issued. */
     unsigned long jiffies_sent; /**< Jiffies, when the upload/download
                                      request was sent. */
+    int errno; /**< Error number. */
     uint32_t abort_code; /**< SDO request abort code. Zero on success. */
 };
 
--- a/master/slave_config.c	Thu Sep 16 09:08:11 2010 +0200
+++ b/master/slave_config.c	Thu Sep 16 10:16:12 2010 +0200
@@ -938,16 +938,22 @@
 
 /*****************************************************************************/
 
-int ecrt_slave_config_idn(ec_slave_config_t *sc, uint16_t idn,
-        ec_al_state_t state, const uint8_t *data, size_t size)
+int ecrt_slave_config_idn(ec_slave_config_t *sc, uint8_t drive_no, 
+        uint16_t idn, ec_al_state_t state, const uint8_t *data,
+        size_t size)
 {
     ec_slave_t *slave = sc->slave;
     ec_soe_request_t *req;
     int ret;
 
-    EC_CONFIG_DBG(sc, 1, "%s(sc = 0x%p, idn = 0x%04X, state = %u, "
-            "data = 0x%p, size = %zu)\n",
-            __func__, sc, idn, state, data, size);
+    EC_CONFIG_DBG(sc, 1, "%s(sc = 0x%p, drive_no = %u, idn = 0x%04X, "
+            "state = %u, data = 0x%p, size = %zu)\n",
+            __func__, sc, drive_no, idn, state, data, size);
+
+    if (drive_no > 7) {
+        EC_CONFIG_ERR(sc, "Invalid drive number!\n");
+        return -EINVAL;
+    }
 
     if (state != EC_AL_STATE_PREOP && state != EC_AL_STATE_SAFEOP) {
         EC_CONFIG_ERR(sc, "AL state for IDN config"
@@ -967,6 +973,7 @@
     }
 
     ec_soe_request_init(req);
+    ec_soe_request_set_drive_no(req, drive_no);
     ec_soe_request_set_idn(req, idn);
     req->al_state = state;
 
--- a/master/soe_request.c	Thu Sep 16 09:08:11 2010 +0200
+++ b/master/soe_request.c	Thu Sep 16 10:16:12 2010 +0200
@@ -56,6 +56,8 @@
         ec_soe_request_t *req /**< SoE request. */
         )
 {
+    req->drive_no = 0x00;
+    req->idn = 0x0000;
     req->al_state = EC_AL_STATE_INIT;
     req->data = NULL;
     req->mem_size = 0;
@@ -86,6 +88,7 @@
         const ec_soe_request_t *other /**< Other SoE request to copy from. */
         )
 {
+    req->drive_no = other->drive_no;
     req->idn = other->idn;
     req->al_state = other->al_state;
     return ec_soe_request_copy_data(req, other->data, other->data_size);
@@ -93,6 +96,18 @@
 
 /*****************************************************************************/
 
+/** Set drive number.
+ */
+void ec_soe_request_set_drive_no(
+        ec_soe_request_t *req, /**< SoE request. */
+        uint8_t drive_no /** Drive Number. */
+        )
+{
+    req->drive_no = drive_no;
+}
+
+/*****************************************************************************/
+
 /** Set IDN.
  */
 void ec_soe_request_set_idn(
--- a/master/soe_request.h	Thu Sep 16 09:08:11 2010 +0200
+++ b/master/soe_request.h	Thu Sep 16 10:16:12 2010 +0200
@@ -47,6 +47,7 @@
  */
 typedef struct {
     struct list_head list; /**< List item. */
+    uint8_t drive_no; /**< Drive number. */
     uint16_t idn; /**< Sercos ID-Number. */
     ec_al_state_t al_state; /**< AL state (only valid for IDN config). */
     uint8_t *data; /**< Pointer to SDO data. */
@@ -66,6 +67,7 @@
 void ec_soe_request_clear(ec_soe_request_t *);
 
 int ec_soe_request_copy(ec_soe_request_t *, const ec_soe_request_t *);
+void ec_soe_request_set_drive_no(ec_soe_request_t *, uint8_t);
 void ec_soe_request_set_idn(ec_soe_request_t *, uint16_t);
 int ec_soe_request_alloc(ec_soe_request_t *, size_t);
 int ec_soe_request_copy_data(ec_soe_request_t *, const uint8_t *, size_t);
--- a/tool/CommandCStruct.cpp	Thu Sep 16 09:08:11 2010 +0200
+++ b/tool/CommandCStruct.cpp	Thu Sep 16 10:16:12 2010 +0200
@@ -117,7 +117,7 @@
     for (i = 0; i < slave.sync_count; i++) {
         m.getSync(&sync, slave.position, i);
 
-        syncs << "   {" << dec << sync.sync_index
+        syncs << "    {" << dec << sync.sync_index
             << ", " << (EC_READ_BIT(&sync.control_register, 2) ?
                     "EC_DIR_OUTPUT" : "EC_DIR_INPUT")
             << ", " << dec << (unsigned int) sync.pdo_count
@@ -136,7 +136,7 @@
         for (j = 0; j < sync.pdo_count; j++) {
             m.getPdo(&pdo, slave.position, i, j);
 
-            pdos << "   {0x" << hex << setfill('0')
+            pdos << "    {0x" << hex << setfill('0')
                 << setw(4) << pdo.index
                 << ", " << dec << (unsigned int) pdo.entry_count
                 << ", ";
@@ -155,7 +155,7 @@
             for (k = 0; k < pdo.entry_count; k++) {
                 m.getPdoEntry(&entry, slave.position, i, j, k);
 
-                entries << "   {0x" << hex << setfill('0')
+                entries << "    {0x" << hex << setfill('0')
                     << setw(4) << entry.index
                     << ", 0x" << setw(2) << (unsigned int) entry.subindex
                     << ", " << dec << (unsigned int) entry.bit_length
@@ -201,6 +201,7 @@
 
     cout << "ec_sync_info_t " << id.str() << "syncs[] = {" << endl
         << syncs.str()
+        << "    {0xff}" << endl
         << "};" << endl
         << endl;
 }
--- a/tool/CommandDomains.cpp	Thu Sep 16 09:08:11 2010 +0200
+++ b/tool/CommandDomains.cpp	Thu Sep 16 10:16:12 2010 +0200
@@ -71,7 +71,7 @@
         << endl
         << "  SlaveConfig 1001:0, SM3 ( Input), LogAddr 0x00000006, Size 6"
         << endl
-        << "    0x00 0x00 0x00 0x00 0x00 0x00" << endl
+        << "    00 00 00 00 00 00" << endl
         << endl
         << "The process data are displayed as hexadecimal bytes." << endl
         << endl
@@ -185,7 +185,7 @@
         for (j = 0; j < fmmu.data_size; j++) {
             if (j && !(j % BreakAfterBytes))
                 cout << endl << indent << "    ";
-            cout << "0x" << setw(2)
+            cout << setw(2)
                 << (unsigned int) *(processData + dataOffset + j) << " ";
         }
         cout << endl;
--- a/tool/CommandMaster.cpp	Thu Sep 16 09:08:11 2010 +0200
+++ b/tool/CommandMaster.cpp	Thu Sep 16 10:16:12 2010 +0200
@@ -149,8 +149,8 @@
                     << "      Tx frame rate [1/s]: "
                     << setfill(' ') << setprecision(0) << fixed;
                 for (j = 0; j < EC_RATE_COUNT; j++) {
-                    cout <<
-                        setw(5) << data.devices[i].tx_frame_rates[j] / 1000.0;
+                    cout << setw(ColWidth)
+                        << data.devices[i].tx_frame_rates[j] / 1000.0;
                     if (j < EC_RATE_COUNT - 1) {
                         cout << " ";
                     }
@@ -159,8 +159,8 @@
                     << "      Tx rate [KByte/s]:   "
                     << setprecision(1) << fixed;
                 for (j = 0; j < EC_RATE_COUNT; j++) {
-                    cout << setw(5)
-                        << data.devices[i].tx_byte_rates[j] / 1024000.0;
+                    cout << setw(ColWidth)
+                        << data.devices[i].tx_byte_rates[j] / 1024.0;
                     if (j < EC_RATE_COUNT - 1) {
                         cout << " ";
                     }
@@ -169,7 +169,8 @@
                     << "      Loss rate [1/s]:     "
                     << setprecision(0) << fixed;
                 for (j = 0; j < EC_RATE_COUNT; j++) {
-                    cout << setw(5) << data.devices[i].loss_rates[j] / 1000.0;
+                    cout << setw(ColWidth)
+                        << data.devices[i].loss_rates[j] / 1000.0;
                     if (j < EC_RATE_COUNT - 1) {
                         cout << " ";
                     }
@@ -183,7 +184,7 @@
                         perc = 100.0 * data.devices[i].loss_rates[j] /
                             data.devices[i].tx_frame_rates[j];
                     }
-                    cout << setw(5) << perc;
+                    cout << setw(ColWidth) << perc;
                     if (j < EC_RATE_COUNT - 1) {
                         cout << " ";
                     }
--- a/tool/CommandMaster.h	Thu Sep 16 09:08:11 2010 +0200
+++ b/tool/CommandMaster.h	Thu Sep 16 10:16:12 2010 +0200
@@ -42,6 +42,9 @@
 
         string helpString() const;
         void execute(const StringVector &);
+
+    private:
+        enum {ColWidth = 6};
 };
 
 /****************************************************************************/
--- a/tool/CommandSoeRead.cpp	Thu Sep 16 09:08:11 2010 +0200
+++ b/tool/CommandSoeRead.cpp	Thu Sep 16 10:16:12 2010 +0200
@@ -90,6 +90,8 @@
         throwInvalidUsageException(err);
     }
 
+    ioctl.drive_no = 0; // FIXME
+
     try {
         ioctl.idn = parseIdn(args[0]);
     } catch (runtime_error &e) {
--- a/tool/CommandSoeWrite.cpp	Thu Sep 16 09:08:11 2010 +0200
+++ b/tool/CommandSoeWrite.cpp	Thu Sep 16 10:16:12 2010 +0200
@@ -92,6 +92,8 @@
         throwInvalidUsageException(err);
     }
 
+    ioctl.drive_no = 0; // FIXME
+
     try {
         ioctl.idn = parseIdn(args[0]);
     } catch (runtime_error &e) {