master/slave.c
branchstable-1.3
changeset 1744 7bc131b92039
parent 1739 5fcbd29151d2
child 1746 72e7507b3f1b
--- a/master/slave.c	Fri Aug 10 15:08:44 2007 +0000
+++ b/master/slave.c	Fri Aug 10 15:27:08 2007 +0000
@@ -57,6 +57,7 @@
 ssize_t ec_show_slave_attribute(struct kobject *, struct attribute *, char *);
 ssize_t ec_store_slave_attribute(struct kobject *, struct attribute *,
                                  const char *, size_t);
+char *ec_slave_sii_string(ec_slave_t *, unsigned int);
 
 /*****************************************************************************/
 
@@ -65,11 +66,13 @@
 EC_SYSFS_READ_ATTR(info);
 EC_SYSFS_READ_WRITE_ATTR(state);
 EC_SYSFS_READ_WRITE_ATTR(eeprom);
+EC_SYSFS_READ_WRITE_ATTR(alias);
 
 static struct attribute *def_attrs[] = {
     &attr_info,
     &attr_state,
     &attr_eeprom,
+    &attr_alias,
     NULL,
 };
 
@@ -110,26 +113,21 @@
 
     slave->master = master;
 
-    slave->requested_state = EC_SLAVE_STATE_UNKNOWN;
+    slave->requested_state = EC_SLAVE_STATE_PREOP;
     slave->current_state = EC_SLAVE_STATE_UNKNOWN;
     slave->self_configured = 0;
     slave->error_flag = 0;
-    slave->online = 1;
+    slave->online_state = EC_SLAVE_ONLINE;
     slave->fmmu_count = 0;
-
-    slave->coupler_index = 0;
-    slave->coupler_subindex = 0xFFFF;
+    slave->pdos_registered = 0;
 
     slave->base_type = 0;
     slave->base_revision = 0;
     slave->base_build = 0;
     slave->base_fmmu_count = 0;
-    slave->base_sync_count = 0;
 
     slave->eeprom_data = NULL;
     slave->eeprom_size = 0;
-    slave->new_eeprom_data = NULL;
-    slave->new_eeprom_size = 0;
 
     slave->sii_alias = 0;
     slave->sii_vendor_id = 0;
@@ -147,8 +145,10 @@
     slave->sii_name = NULL;
     slave->sii_current_on_ebus = 0;
 
-    INIT_LIST_HEAD(&slave->sii_strings);
-    INIT_LIST_HEAD(&slave->sii_syncs);
+    slave->sii_strings = NULL;
+    slave->sii_string_count = 0;
+    slave->sii_syncs = NULL;
+    slave->sii_sync_count = 0;
     INIT_LIST_HEAD(&slave->sii_pdos);
     INIT_LIST_HEAD(&slave->sdo_dictionary);
     INIT_LIST_HEAD(&slave->sdo_confs);
@@ -238,46 +238,34 @@
 void ec_slave_clear(struct kobject *kobj /**< kobject of the slave */)
 {
     ec_slave_t *slave;
-    ec_sii_string_t *string, *next_str;
-    ec_sii_sync_t *sync, *next_sync;
-    ec_sii_pdo_t *pdo, *next_pdo;
-    ec_sii_pdo_entry_t *entry, *next_ent;
+    ec_pdo_t *pdo, *next_pdo;
     ec_sdo_data_t *sdodata, *next_sdodata;
+    unsigned int i;
 
     slave = container_of(kobj, ec_slave_t, kobj);
 
-    // free all string objects
-    list_for_each_entry_safe(string, next_str, &slave->sii_strings, list) {
-        list_del(&string->list);
-        kfree(string);
+    // free all strings
+    if (slave->sii_strings) {
+        for (i = 0; i < slave->sii_string_count; i++)
+            kfree(slave->sii_strings[i]);
+        kfree(slave->sii_strings);
     }
 
     // free all sync managers
-    list_for_each_entry_safe(sync, next_sync, &slave->sii_syncs, list) {
-        list_del(&sync->list);
-        kfree(sync);
-    }
-
-    // free all PDOs
+    if (slave->sii_syncs) {
+        for (i = 0; i < slave->sii_sync_count; i++) {
+            ec_sync_clear(&slave->sii_syncs[i]);
+        }
+        kfree(slave->sii_syncs);
+    }
+
+    // free all SII PDOs
     list_for_each_entry_safe(pdo, next_pdo, &slave->sii_pdos, list) {
         list_del(&pdo->list);
-        if (pdo->name) kfree(pdo->name);
-
-        // free all PDO entries
-        list_for_each_entry_safe(entry, next_ent, &pdo->entries, list) {
-            list_del(&entry->list);
-            if (entry->name) kfree(entry->name);
-            kfree(entry);
-        }
-
+        ec_pdo_clear(pdo);
         kfree(pdo);
     }
 
-    if (slave->sii_group) kfree(slave->sii_group);
-    if (slave->sii_image) kfree(slave->sii_image);
-    if (slave->sii_order) kfree(slave->sii_order);
-    if (slave->sii_name) kfree(slave->sii_name);
-
     // free all SDO configurations
     list_for_each_entry_safe(sdodata, next_sdodata, &slave->sdo_confs, list) {
         list_del(&sdodata->list);
@@ -286,7 +274,6 @@
     }
 
     if (slave->eeprom_data) kfree(slave->eeprom_data);
-    if (slave->new_eeprom_data) kfree(slave->new_eeprom_data);
 
     kfree(slave);
 }
@@ -309,10 +296,10 @@
 void ec_slave_reset(ec_slave_t *slave /**< EtherCAT slave */)
 {
     ec_sdo_data_t *sdodata, *next_sdodata;
-    ec_sii_sync_t *sync;
-
-    // remove FMMU configurations
+    unsigned int i;
+
     slave->fmmu_count = 0;
+    slave->pdos_registered = 0;
 
     // free all SDO configurations
     list_for_each_entry_safe(sdodata, next_sdodata, &slave->sdo_confs, list) {
@@ -322,17 +309,73 @@
     }
 
     // remove estimated sync manager sizes
-    list_for_each_entry(sync, &slave->sii_syncs, list) {
-        sync->est_length = 0;
-    }
-}
-
-/*****************************************************************************/
-
-/**
+    for (i = 0; i < slave->sii_sync_count; i++) {
+        slave->sii_syncs[i].est_length = 0;
+    }
+}
+
+/*****************************************************************************/
+
+/**
+ * Sets the application state of a slave.
  */
 
-void ec_slave_request_state(ec_slave_t *slave, /**< ETherCAT slave */
+void ec_slave_set_state(ec_slave_t *slave, /**< EtherCAT slave */
+        ec_slave_state_t new_state /**< new application state */
+        )
+{
+    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;
+    }
+}
+
+/*****************************************************************************/
+
+/**
+ * Sets the online state of a slave.
+ */
+
+void ec_slave_set_online_state(ec_slave_t *slave, /**< EtherCAT slave */
+        ec_slave_online_state_t new_state /**< new online state */
+        )
+{
+    if (new_state == EC_SLAVE_OFFLINE &&
+            slave->online_state == EC_SLAVE_ONLINE) {
+        if (slave->pdos_registered)
+            slave->master->pdo_slaves_offline++;
+        if (slave->master->debug_level)
+            EC_DBG("Slave %i: offline.\n", slave->ring_position);
+    }
+    else if (new_state == EC_SLAVE_ONLINE &&
+            slave->online_state == EC_SLAVE_OFFLINE) {
+        slave->error_flag = 0; // clear error flag
+        if (slave->pdos_registered)
+            slave->master->pdo_slaves_offline--;
+        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);
+        }
+    }
+
+    slave->online_state = new_state;
+}
+
+/*****************************************************************************/
+
+/**
+ */
+
+void ec_slave_request_state(ec_slave_t *slave, /**< EtherCAT slave */
                             ec_slave_state_t state /**< new state */
                             )
 {
@@ -347,35 +390,50 @@
    \return 0 in case of success, else < 0
 */
 
-int ec_slave_fetch_strings(ec_slave_t *slave, /**< EtherCAT slave */
-                           const uint8_t *data /**< category data */
-                           )
-{
-    unsigned int string_count, i;
+int ec_slave_fetch_sii_strings(
+        ec_slave_t *slave, /**< EtherCAT slave */
+        const uint8_t *data /**< category data */
+        )
+{
+    int i;
     size_t size;
     off_t offset;
-    ec_sii_string_t *string;
-
-    string_count = data[0];
+
+    slave->sii_string_count = data[0];
+
+    if (!slave->sii_string_count)
+        return 0;
+
+    if (!(slave->sii_strings =
+                kmalloc(sizeof(char *) * slave->sii_string_count,
+                    GFP_KERNEL))) {
+        EC_ERR("Failed to allocate string array memory.\n");
+        goto out_zero;
+    }
+
     offset = 1;
-    for (i = 0; i < string_count; i++) {
+    for (i = 0; i < slave->sii_string_count; i++) {
         size = data[offset];
         // allocate memory for string structure and data at a single blow
-        if (!(string = (ec_sii_string_t *)
-              kmalloc(sizeof(ec_sii_string_t) + size + 1, GFP_ATOMIC))) {
+        if (!(slave->sii_strings[i] =
+                    kmalloc(sizeof(char) * size + 1, GFP_KERNEL))) {
             EC_ERR("Failed to allocate string memory.\n");
-            return -1;
-        }
-        string->size = size;
-        // string memory appended to string structure
-        string->data = (char *) string + sizeof(ec_sii_string_t);
-        memcpy(string->data, data + offset + 1, size);
-        string->data[size] = 0x00;
-        list_add_tail(&string->list, &slave->sii_strings);
+            goto out_free;
+        }
+        memcpy(slave->sii_strings[i], data + offset + 1, size);
+        slave->sii_strings[i][size] = 0x00; // append binary zero
         offset += 1 + size;
     }
 
     return 0;
+
+out_free:
+    for (i--; i >= 0; i--) kfree(slave->sii_strings[i]);
+    kfree(slave->sii_strings);
+    slave->sii_strings = NULL;
+out_zero:
+    slave->sii_string_count = 0;
+    return -1;
 }
 
 /*****************************************************************************/
@@ -385,16 +443,17 @@
    \return 0 in case of success, else < 0
 */
 
-void ec_slave_fetch_general(ec_slave_t *slave, /**< EtherCAT slave */
-                            const uint8_t *data /**< category data */
-                            )
+void ec_slave_fetch_sii_general(
+        ec_slave_t *slave, /**< EtherCAT slave */
+        const uint8_t *data /**< category data */
+        )
 {
     unsigned int i;
 
-    ec_slave_locate_string(slave, data[0], &slave->sii_group);
-    ec_slave_locate_string(slave, data[1], &slave->sii_image);
-    ec_slave_locate_string(slave, data[2], &slave->sii_order);
-    ec_slave_locate_string(slave, data[3], &slave->sii_name);
+    slave->sii_group = ec_slave_sii_string(slave, data[0]);
+    slave->sii_image = ec_slave_sii_string(slave, data[1]);
+    slave->sii_order = ec_slave_sii_string(slave, data[2]);
+    slave->sii_name = ec_slave_sii_string(slave, data[3]);
 
     for (i = 0; i < 4; i++)
         slave->sii_physical_layer[i] =
@@ -410,32 +469,34 @@
    \return 0 in case of success, else < 0
 */
 
-int ec_slave_fetch_sync(ec_slave_t *slave, /**< EtherCAT slave */
-                        const uint8_t *data, /**< category data */
-                        size_t word_count /**< number of words */
-                        )
-{
-    unsigned int sync_count, i;
-    ec_sii_sync_t *sync;
-
-    sync_count = word_count / 4; // sync manager struct is 4 words long
-
-    for (i = 0; i < sync_count; i++, data += 8) {
-        if (!(sync = (ec_sii_sync_t *)
-              kmalloc(sizeof(ec_sii_sync_t), GFP_ATOMIC))) {
-            EC_ERR("Failed to allocate Sync-Manager memory.\n");
-            return -1;
-        }
-
-        sync->index = i;
+int ec_slave_fetch_sii_syncs(
+        ec_slave_t *slave, /**< EtherCAT slave */
+        const uint8_t *data, /**< category data */
+        size_t word_count /**< number of words */
+        )
+{
+    unsigned int i;
+    ec_sync_t *sync;
+
+    // sync manager struct is 4 words long
+    slave->sii_sync_count = word_count / 4;
+
+    if (!(slave->sii_syncs =
+                kmalloc(sizeof(ec_sync_t) * slave->sii_sync_count,
+                    GFP_KERNEL))) {
+        EC_ERR("Failed to allocate memory for sync managers.\n");
+        slave->sii_sync_count = 0;
+        return -1;
+    }
+    
+    for (i = 0; i < slave->sii_sync_count; i++, data += 8) {
+        sync = &slave->sii_syncs[i];
+
+        ec_sync_init(sync, slave, i);
         sync->physical_start_address = EC_READ_U16(data);
-        sync->length                 = EC_READ_U16(data + 2);
-        sync->control_register       = EC_READ_U8 (data + 4);
-        sync->enable                 = EC_READ_U8 (data + 6);
-
-        sync->est_length = 0;
-
-        list_add_tail(&sync->list, &slave->sii_syncs);
+        sync->length = EC_READ_U16(data + 2);
+        sync->control_register = EC_READ_U8 (data + 4);
+        sync->enable = EC_READ_U8 (data + 6);
     }
 
     return 0;
@@ -448,55 +509,74 @@
    \return 0 in case of success, else < 0
 */
 
-int ec_slave_fetch_pdo(ec_slave_t *slave, /**< EtherCAT slave */
-                       const uint8_t *data, /**< category data */
-                       size_t word_count, /**< number of words */
-                       ec_sii_pdo_type_t pdo_type /**< PDO type */
-                       )
-{
-    ec_sii_pdo_t *pdo;
-    ec_sii_pdo_entry_t *entry;
+int ec_slave_fetch_sii_pdos(
+        ec_slave_t *slave, /**< EtherCAT slave */
+        const uint8_t *data, /**< category data */
+        size_t word_count, /**< number of words */
+        ec_pdo_type_t pdo_type /**< PDO type */
+        )
+{
+    ec_pdo_t *pdo;
+    ec_pdo_entry_t *entry;
     unsigned int entry_count, i;
 
     while (word_count >= 4) {
-        if (!(pdo = (ec_sii_pdo_t *)
-              kmalloc(sizeof(ec_sii_pdo_t), GFP_ATOMIC))) {
+        if (!(pdo = kmalloc(sizeof(ec_pdo_t), GFP_KERNEL))) {
             EC_ERR("Failed to allocate PDO memory.\n");
             return -1;
         }
 
-        INIT_LIST_HEAD(&pdo->entries);
+        ec_pdo_init(pdo);
         pdo->type = pdo_type;
-
         pdo->index = EC_READ_U16(data);
         entry_count = EC_READ_U8(data + 2);
         pdo->sync_index = EC_READ_U8(data + 3);
-        pdo->name = NULL;
-        ec_slave_locate_string(slave, EC_READ_U8(data + 5), &pdo->name);
-
+        pdo->name = ec_slave_sii_string(slave, EC_READ_U8(data + 5));
         list_add_tail(&pdo->list, &slave->sii_pdos);
 
         word_count -= 4;
         data += 8;
 
         for (i = 0; i < entry_count; i++) {
-            if (!(entry = (ec_sii_pdo_entry_t *)
-                  kmalloc(sizeof(ec_sii_pdo_entry_t), GFP_ATOMIC))) {
+            if (!(entry = kmalloc(sizeof(ec_pdo_entry_t), GFP_KERNEL))) {
                 EC_ERR("Failed to allocate PDO entry memory.\n");
                 return -1;
             }
 
             entry->index = EC_READ_U16(data);
             entry->subindex = EC_READ_U8(data + 2);
-            entry->name = NULL;
-            ec_slave_locate_string(slave, EC_READ_U8(data + 3), &entry->name);
+            entry->name = ec_slave_sii_string(slave, EC_READ_U8(data + 3));
             entry->bit_length = EC_READ_U8(data + 5);
-
             list_add_tail(&entry->list, &pdo->entries);
 
             word_count -= 4;
             data += 8;
         }
+
+        // if sync manager index is positive, the PDO is mapped by default
+        if (pdo->sync_index >= 0) {
+            ec_pdo_t *mapped_pdo;
+
+            if (pdo->sync_index >= slave->sii_sync_count) {
+                EC_ERR("Invalid SM index %i for PDO 0x%04X in slave %u.",
+                        pdo->sync_index, pdo->index, slave->ring_position);
+                return -1;
+            }
+
+            if (!(mapped_pdo = kmalloc(sizeof(ec_pdo_t), GFP_KERNEL))) {
+                EC_ERR("Failed to allocate PDO memory.\n");
+                return -1;
+            }
+
+            if (ec_pdo_copy(mapped_pdo, pdo)) {
+                EC_ERR("Failed to copy PDO.\n");
+                kfree(mapped_pdo);
+                return -1;
+            }
+
+            list_add_tail(&mapped_pdo->list,
+                    &slave->sii_syncs[pdo->sync_index].pdos);
+        }
     }
 
     return 0;
@@ -510,68 +590,43 @@
    \todo documentation
 */
 
-int ec_slave_locate_string(ec_slave_t *slave, /**< EtherCAT slave */
-                           unsigned int index, /**< string index */
-                           char **ptr /**< Address of the string pointer */
-                           )
-{
-    ec_sii_string_t *string;
-    char *err_string;
-
-    // Erst alten Speicher freigeben
-    if (*ptr) {
-        kfree(*ptr);
-        *ptr = NULL;
-    }
-
-    // Index 0 bedeutet "nicht belegt"
-    if (!index) return 0;
-
-    // EEPROM-String mit Index finden und kopieren
-    list_for_each_entry(string, &slave->sii_strings, list) {
-        if (--index) continue;
-
-        if (!(*ptr = (char *) kmalloc(string->size + 1, GFP_ATOMIC))) {
-            EC_ERR("Unable to allocate string memory.\n");
-            return -1;
-        }
-        memcpy(*ptr, string->data, string->size + 1);
-        return 0;
-    }
-
-    if (slave->master->debug_level)
-        EC_WARN("String %i not found in slave %i.\n",
-                index, slave->ring_position);
-
-    err_string = "(string not found)";
-
-    if (!(*ptr = (char *) kmalloc(strlen(err_string) + 1, GFP_ATOMIC))) {
-        EC_WARN("Unable to allocate string memory.\n");
-        return -1;
-    }
-
-    memcpy(*ptr, err_string, strlen(err_string) + 1);
-    return 0;
-}
-
-/*****************************************************************************/
-
-/**
-   Prepares an FMMU configuration.
-   Configuration data for the FMMU is saved in the slave structure and is
-   written to the slave in ecrt_master_activate().
-   The FMMU configuration is done in a way, that the complete data range
-   of the corresponding sync manager is covered. Seperate FMMUs are configured
-   for each domain.
-   If the FMMU configuration is already prepared, the function returns with
-   success.
-   \return 0 in case of success, else < 0
-*/
-
-int ec_slave_prepare_fmmu(ec_slave_t *slave, /**< EtherCAT slave */
-                          const ec_domain_t *domain, /**< domain */
-                          const ec_sii_sync_t *sync  /**< sync manager */
-                          )
+char *ec_slave_sii_string(
+        ec_slave_t *slave, /**< EtherCAT slave */
+        unsigned int index /**< string index */
+        )
+{
+    if (!index--) 
+        return NULL;
+
+    if (index >= slave->sii_string_count) {
+        if (slave->master->debug_level)
+            EC_WARN("String %i not found in slave %i.\n",
+                    index, slave->ring_position);
+        return NULL;
+    }
+
+    return slave->sii_strings[index];
+}
+
+/*****************************************************************************/
+
+/**
+ * Prepares an FMMU configuration.
+ * Configuration data for the FMMU is saved in the slave structure and is
+ * written to the slave in ecrt_master_activate().
+ * The FMMU configuration is done in a way, that the complete data range
+ * of the corresponding sync manager is covered. Seperate FMMUs are configured
+ * for each domain.
+ * If the FMMU configuration is already prepared, the function returns with
+ * success.
+ * \return 0 in case of success, else < 0
+ */
+
+int ec_slave_prepare_fmmu(
+        ec_slave_t *slave, /**< EtherCAT slave */
+        const ec_domain_t *domain, /**< domain */
+        const ec_sync_t *sync  /**< sync manager */
+        )
 {
     unsigned int i;
     ec_fmmu_t *fmmu;
@@ -592,12 +647,14 @@
 
     fmmu = &slave->fmmus[slave->fmmu_count];
 
-    fmmu->index = slave->fmmu_count;
+    ec_fmmu_init(fmmu, slave, slave->fmmu_count++);
     fmmu->domain = domain;
     fmmu->sync = sync;
     fmmu->logical_start_address = 0;
 
-    slave->fmmu_count++;
+    slave->pdos_registered = 1;
+    
+    ec_slave_request_state(slave, EC_SLAVE_STATE_OP);
 
     return 0;
 }
@@ -613,9 +670,9 @@
                      )
 {
     off_t off = 0;
-    ec_sii_sync_t *sync;
-    ec_sii_pdo_t *pdo;
-    ec_sii_pdo_entry_t *pdo_entry;
+    ec_sync_t *sync;
+    ec_pdo_t *pdo;
+    ec_pdo_entry_t *pdo_entry;
     int first, i;
     ec_sdo_data_t *sdodata;
     char str[20];
@@ -635,14 +692,10 @@
     off += sprintf(buffer + off, " (");
     off += ec_state_string(slave->requested_state, buffer + off);
     off += sprintf(buffer + off, ")\nFlags: %s, %s\n",
-                   slave->online ? "online" : "OFFLINE",
-                   slave->error_flag ? "ERROR" : "ok");
+            slave->online_state == EC_SLAVE_ONLINE ? "online" : "OFFLINE",
+            slave->error_flag ? "ERROR" : "ok");
     off += sprintf(buffer + off, "Ring position: %i\n",
                    slave->ring_position);
-    off += sprintf(buffer + off, "Advanced position: %i:%i\n",
-                   slave->coupler_index, slave->coupler_subindex);
-    off += sprintf(buffer + off, "Coupler: %s\n",
-                   ec_slave_is_coupler(slave) ? "yes" : "no");
     off += sprintf(buffer + off, "Current consumption: %i mA\n\n",
                    slave->sii_current_on_ebus);
 
@@ -725,31 +778,52 @@
     if (slave->sii_order)
         off += sprintf(buffer + off, "  Order number: %s\n", slave->sii_order);
 
-    if (!list_empty(&slave->sii_syncs))
-        off += sprintf(buffer + off, "\nSync-Managers:\n");
-
-    list_for_each_entry(sync, &slave->sii_syncs, list) {
-        off += sprintf(buffer + off, "  %i: 0x%04X, length %i,"
-                       " control 0x%02X, %s\n",
-                       sync->index, sync->physical_start_address,
-                       sync->length, sync->control_register,
-                       sync->enable ? "enable" : "disable");
+    if (slave->sii_sync_count)
+        off += sprintf(buffer + off, "\nSync managers / PDO mapping:\n");
+
+    for (i = 0; i < slave->sii_sync_count; i++) {
+        sync = &slave->sii_syncs[i];
+        off += sprintf(buffer + off,
+                "  SM%u: addr 0x%04X, size %i, control 0x%02X, %s\n",
+                sync->index, sync->physical_start_address,
+                ec_sync_size(sync), sync->control_register,
+                sync->enable ? "enable" : "disable");
+
+        if (list_empty(&sync->pdos))
+            off += sprintf(buffer + off, "    No PDOs mapped.\n");
+
+        list_for_each_entry(pdo, &sync->pdos, list) {
+            off += sprintf(buffer + off, "    %s 0x%04X \"%s\"\n",
+                    pdo->type == EC_RX_PDO ? "RXPDO" : "TXPDO",
+                    pdo->index, pdo->name ? pdo->name : "???");
+
+            list_for_each_entry(pdo_entry, &pdo->entries, list) {
+                off += sprintf(buffer + off,
+                        "      0x%04X:%X \"%s\", %i bit\n",
+                        pdo_entry->index, pdo_entry->subindex,
+                        pdo_entry->name ? pdo_entry->name : "???",
+                        pdo_entry->bit_length);
+            }
+        }
     }
 
     if (!list_empty(&slave->sii_pdos))
-        off += sprintf(buffer + off, "\nPDOs:\n");
+        off += sprintf(buffer + off, "\nAvailable PDOs:\n");
 
     list_for_each_entry(pdo, &slave->sii_pdos, list) {
-        off += sprintf(buffer + off,
-                       "  %s \"%s\" (0x%04X), Sync-Manager %i\n",
+        off += sprintf(buffer + off, "  %s 0x%04X \"%s\"",
                        pdo->type == EC_RX_PDO ? "RXPDO" : "TXPDO",
-                       pdo->name ? pdo->name : "???",
-                       pdo->index, pdo->sync_index);
+                       pdo->index, pdo->name ? pdo->name : "???");
+        if (pdo->sync_index >= 0)
+            off += sprintf(buffer + off, ", default mapping: SM%u.\n",
+                    pdo->sync_index);
+        else
+            off += sprintf(buffer + off, ", no default mapping.\n");
 
         list_for_each_entry(pdo_entry, &pdo->entries, list) {
-            off += sprintf(buffer + off, "    \"%s\" 0x%04X:%X, %i bit\n",
+            off += sprintf(buffer + off, "    0x%04X:%X \"%s\", %i bit\n",
+                           pdo_entry->index, pdo_entry->subindex,
                            pdo_entry->name ? pdo_entry->name : "???",
-                           pdo_entry->index, pdo_entry->subindex,
                            pdo_entry->bit_length);
         }
     }
@@ -776,79 +850,150 @@
 /*****************************************************************************/
 
 /**
-   Schedules an EEPROM write operation.
-   \return 0 in case of success, else < 0
-*/
+ * Schedules an EEPROM write request.
+ * \return 0 case of success, otherwise error code.
+ */
+
+int ec_slave_schedule_eeprom_writing(ec_eeprom_write_request_t *request)
+{
+    ec_master_t *master = request->slave->master;
+
+    request->state = EC_REQUEST_QUEUED;
+
+    // schedule EEPROM write request.
+    down(&master->eeprom_sem);
+    list_add_tail(&request->list, &master->eeprom_requests);
+    up(&master->eeprom_sem);
+
+    // wait for processing through FSM
+    if (wait_event_interruptible(master->eeprom_queue,
+                request->state != EC_REQUEST_QUEUED)) {
+        // interrupted by signal
+        down(&master->eeprom_sem);
+        if (request->state == EC_REQUEST_QUEUED) {
+            list_del(&request->list);
+            up(&master->eeprom_sem);
+            return -EINTR;
+        }
+        // request already processing: interrupt not possible.
+        up(&master->eeprom_sem);
+    }
+
+    // wait until master FSM has finished processing
+    wait_event(master->eeprom_queue,
+            request->state != EC_REQUEST_IN_PROGRESS);
+
+    return request->state == EC_REQUEST_COMPLETE ? 0 : -EIO;
+}
+
+/*****************************************************************************/
+
+/**
+ * Writes complete EEPROM contents to a slave.
+ * \return data size written in case of success, otherwise error code.
+ */
 
 ssize_t ec_slave_write_eeprom(ec_slave_t *slave, /**< EtherCAT slave */
-                              const uint8_t *data, /**< new EEPROM data */
-                              size_t size /**< size of data in bytes */
-                              )
-{
-    uint16_t word_size, cat_type, cat_size;
-    const uint16_t *data_words, *next_header;
-    uint16_t *new_data;
-
-    if (!slave->master->eeprom_write_enable) {
-        EC_ERR("Writing EEPROMs not allowed! Enable via"
-               " eeprom_write_enable SysFS entry.\n");
-        return -1;
-    }
-
-    if (slave->master->mode != EC_MASTER_MODE_IDLE) {
+        const uint8_t *data, /**< new EEPROM data */
+        size_t size /**< size of data in bytes */
+        )
+{
+    ec_eeprom_write_request_t request;
+    const uint16_t *cat_header;
+    uint16_t cat_type, cat_size;
+    int ret;
+
+    if (slave->master->mode != EC_MASTER_MODE_IDLE) { // FIXME
         EC_ERR("Writing EEPROMs only allowed in idle mode!\n");
-        return -1;
-    }
-
-    if (slave->new_eeprom_data) {
-        EC_ERR("Slave %i already has a pending EEPROM write operation!\n",
-               slave->ring_position);
-        return -1;
-    }
-
-    // coarse check of the data
+        return -EBUSY;
+    }
 
     if (size % 2) {
-        EC_ERR("EEPROM size is odd! Dropping.\n");
-        return -1;
-    }
-
-    data_words = (const uint16_t *) data;
-    word_size = size / 2;
-
-    if (word_size < 0x0041) {
+        EC_ERR("EEPROM data size is odd! Dropping.\n");
+        return -EINVAL;
+    }
+
+    // init EEPROM write request
+    INIT_LIST_HEAD(&request.list);
+    request.slave = slave;
+    request.words = (const uint16_t *) data;
+    request.offset = 0;
+    request.size = size / 2;
+
+    if (request.size < 0x0041) {
         EC_ERR("EEPROM data too short! Dropping.\n");
-        return -1;
-    }
-
-    next_header = data_words + 0x0040;
-    cat_type = EC_READ_U16(next_header);
-    while (cat_type != 0xFFFF) {
-        cat_type = EC_READ_U16(next_header);
-        cat_size = EC_READ_U16(next_header + 1);
-        if ((next_header + cat_size + 2) - data_words >= word_size) {
-            EC_ERR("EEPROM data seems to be corrupted! Dropping.\n");
-            return -1;
-        }
-        next_header += cat_size + 2;
-        cat_type = EC_READ_U16(next_header);
-    }
-
-    // data ok!
-
-    if (!(new_data = (uint16_t *) kmalloc(word_size * 2, GFP_KERNEL))) {
-        EC_ERR("Unable to allocate memory for new EEPROM data!\n");
-        return -1;
-    }
-    memcpy(new_data, data, size);
-
-    slave->new_eeprom_size = word_size;
-    slave->new_eeprom_data = new_data;
-
-    EC_INFO("EEPROM writing scheduled for slave %i, %i words.\n",
-            slave->ring_position, word_size);
-    return 0;
-}
+        return -EINVAL;
+    }
+
+    cat_header = request.words + EC_FIRST_EEPROM_CATEGORY_OFFSET;
+    cat_type = EC_READ_U16(cat_header);
+    while (cat_type != 0xFFFF) { // cycle through categories
+        if (cat_header + 1 > request.words + request.size) {
+            EC_ERR("EEPROM data corrupted! Dropping.\n");
+            return -EINVAL;
+        }
+        cat_size = EC_READ_U16(cat_header + 1);
+        if (cat_header + cat_size + 2 > request.words + request.size) {
+            EC_ERR("EEPROM data corrupted! Dropping.\n");
+            return -EINVAL;
+        }
+        cat_header += cat_size + 2;
+        cat_type = EC_READ_U16(cat_header);
+    }
+
+    // EEPROM data ok. schedule writing.
+    if ((ret = ec_slave_schedule_eeprom_writing(&request)))
+        return ret; // error code
+
+    return size; // success
+}
+
+/*****************************************************************************/
+
+/**
+ * Writes the Secondary slave address (alias) to the slave's EEPROM.
+ * \return data size written in case of success, otherwise error code.
+ */
+
+ssize_t ec_slave_write_alias(ec_slave_t *slave, /**< EtherCAT slave */
+        const uint8_t *data, /**< alias string */
+        size_t size /**< size of data in bytes */
+        )
+{
+    ec_eeprom_write_request_t request;
+    char *remainder;
+    uint16_t alias, word;
+    int ret;
+
+    if (slave->master->mode != EC_MASTER_MODE_IDLE) { // FIXME
+        EC_ERR("Writing EEPROMs only allowed in idle mode!\n");
+        return -EBUSY;
+    }
+
+    alias = simple_strtoul(data, &remainder, 0);
+    if (remainder == (char *) data || (*remainder && *remainder != '\n')) {
+        EC_ERR("Invalid alias value! Dropping.\n");
+        return -EINVAL;
+    }
+    
+    // correct endianess
+    EC_WRITE_U16(&word, alias);
+
+    // init EEPROM write request
+    INIT_LIST_HEAD(&request.list);
+    request.slave = slave;
+    request.words = &word;
+    request.offset = 0x0004;
+    request.size = 1;
+
+    if ((ret = ec_slave_schedule_eeprom_writing(&request)))
+        return ret; // error code
+
+    slave->sii_alias = alias; // FIXME: do this in state machine
+
+    return size; // success
+}
+
 
 /*****************************************************************************/
 
@@ -894,6 +1039,9 @@
             }
         }
     }
+    else if (attr == &attr_alias) {
+        return sprintf(buffer, "%u\n", slave->sii_alias);
+    }
 
     return 0;
 }
@@ -934,138 +1082,39 @@
         return size;
     }
     else if (attr == &attr_eeprom) {
-        if (!ec_slave_write_eeprom(slave, buffer, size))
-            return size;
-    }
-
-    return -EINVAL;
-}
-
-/*****************************************************************************/
-
-/**
-   Calculates the size of a sync manager by evaluating PDO sizes.
-   \return sync manager size
-*/
-
-uint16_t ec_slave_calc_sync_size(const ec_slave_t *slave,
-                                 /**< EtherCAT slave */
-                                 const ec_sii_sync_t *sync
-                                 /**< sync manager */
-                                 )
-{
-    ec_sii_pdo_t *pdo;
-    ec_sii_pdo_entry_t *pdo_entry;
-    unsigned int bit_size, byte_size;
-
-    if (sync->length) return sync->length;
-    if (sync->est_length) return sync->est_length;
-
-    bit_size = 0;
-    list_for_each_entry(pdo, &slave->sii_pdos, list) {
-        if (pdo->sync_index != sync->index) continue;
-
-        list_for_each_entry(pdo_entry, &pdo->entries, list) {
-            bit_size += pdo_entry->bit_length;
-        }
-    }
-
-    if (bit_size % 8) // round up to full bytes
-        byte_size = bit_size / 8 + 1;
-    else
-        byte_size = bit_size / 8;
-
-    return byte_size;
-}
-
-/*****************************************************************************/
-
-/**
-   Initializes a sync manager configuration page with EEPROM data.
-   The referenced memory (\a data) must be at least EC_SYNC_SIZE bytes.
-*/
-
-void ec_slave_sync_config(const ec_slave_t *slave, /**< EtherCAT slave */
-        const ec_sii_sync_t *sync, /**< sync manager */
-        uint8_t *data /**> configuration memory */
+        return ec_slave_write_eeprom(slave, buffer, size);
+    }
+    else if (attr == &attr_alias) {
+        return ec_slave_write_alias(slave, buffer, size);
+    }
+
+    return -EIO;
+}
+
+/*****************************************************************************/
+
+/**
+ */
+
+ec_sync_t *ec_slave_get_pdo_sync(
+        ec_slave_t *slave, /**< EtherCAT slave */
+        ec_direction_t dir /**< input or output */
         )
 {
-    size_t sync_size;
-
-    sync_size = ec_slave_calc_sync_size(slave, sync);
-
-    if (slave->master->debug_level) {
-        EC_DBG("Slave %3i, SM %i: Addr 0x%04X, Size %3i, Ctrl 0x%02X, En %i\n",
-               slave->ring_position, sync->index, sync->physical_start_address,
-               sync_size, sync->control_register, sync->enable);
-    }
-
-    EC_WRITE_U16(data,     sync->physical_start_address);
-    EC_WRITE_U16(data + 2, sync_size);
-    EC_WRITE_U8 (data + 4, sync->control_register);
-    EC_WRITE_U8 (data + 5, 0x00); // status byte (read only)
-    EC_WRITE_U16(data + 6, sync->enable ? 0x0001 : 0x0000); // enable
-}
-
-/*****************************************************************************/
-
-/**
-   Initializes an FMMU configuration page.
-   The referenced memory (\a data) must be at least EC_FMMU_SIZE bytes.
-*/
-
-void ec_slave_fmmu_config(const ec_slave_t *slave, /**< EtherCAT slave */
-        const ec_fmmu_t *fmmu, /**< FMMU */
-        uint8_t *data /**> configuration memory */
-        )
-{
-    size_t sync_size;
-
-    sync_size = ec_slave_calc_sync_size(slave, fmmu->sync);
-
-    if (slave->master->debug_level) {
-        EC_DBG("Slave %3i, FMMU %2i:"
-               " LogAddr 0x%08X, Size %3i, PhysAddr 0x%04X, Dir %s\n",
-               slave->ring_position, fmmu->index, fmmu->logical_start_address,
-               sync_size, fmmu->sync->physical_start_address,
-               ((fmmu->sync->control_register & 0x04) ? "out" : "in"));
-    }
-
-    EC_WRITE_U32(data,      fmmu->logical_start_address);
-    EC_WRITE_U16(data + 4,  sync_size); // size of fmmu
-    EC_WRITE_U8 (data + 6,  0x00); // logical start bit
-    EC_WRITE_U8 (data + 7,  0x07); // logical end bit
-    EC_WRITE_U16(data + 8,  fmmu->sync->physical_start_address);
-    EC_WRITE_U8 (data + 10, 0x00); // physical start bit
-    EC_WRITE_U8 (data + 11, ((fmmu->sync->control_register & 0x04)
-                             ? 0x02 : 0x01));
-    EC_WRITE_U16(data + 12, 0x0001); // enable
-    EC_WRITE_U16(data + 14, 0x0000); // reserved
-}
-
-/*****************************************************************************/
-
-/**
-   \return non-zero if slave is a bus coupler
-*/
-
-int ec_slave_is_coupler(const ec_slave_t *slave /**< EtherCAT slave */)
-{
-    // TODO: Better bus coupler criterion
-    return slave->sii_vendor_id == 0x00000002
-        && slave->sii_product_code == 0x044C2C52;
-}
-
-/*****************************************************************************/
-
-/**
-   \return non-zero if slave is a bus coupler
-*/
-
-int ec_slave_has_subbus(const ec_slave_t *slave /**< EtherCAT slave */)
-{
-    return slave->sii_vendor_id == 0x00000002
-        && slave->sii_product_code == 0x04602c22;
+    unsigned int sync_index;
+
+    if (dir != EC_DIR_INPUT && dir != EC_DIR_OUTPUT) {
+        EC_ERR("Invalid direction!\n");
+        return NULL;
+    }
+
+    sync_index = (unsigned int) dir;
+    if (slave->sii_mailbox_protocols) sync_index += 2;
+
+    if (sync_index >= slave->sii_sync_count)
+        return NULL;
+
+    return &slave->sii_syncs[sync_index];
 }
 
 /*****************************************************************************/
@@ -1122,9 +1171,10 @@
 {
     if (vendor_id != slave->sii_vendor_id ||
         product_code != slave->sii_product_code) {
-        EC_ERR("Invalid slave type at position %i - Requested: 0x%08X 0x%08X,"
-               " found: 0x%08X 0x%08X\".\n", slave->ring_position, vendor_id,
-               product_code, slave->sii_vendor_id, slave->sii_product_code);
+        EC_ERR("Invalid slave type at position %i:\n", slave->ring_position);
+        EC_ERR("  Requested: 0x%08X 0x%08X\n", vendor_id, product_code);
+        EC_ERR("      Found: 0x%08X 0x%08X\n",
+                slave->sii_vendor_id, slave->sii_product_code);
         return -1;
     }
     return 0;
@@ -1215,73 +1265,96 @@
 
 /*****************************************************************************/
 
-/**
-   \return 0 in case of success, else < 0
-   \ingroup RealtimeInterface
-*/
-
-int ecrt_slave_pdo_size(ec_slave_t *slave, /**< EtherCAT slave */
-                        uint16_t pdo_index, /**< PDO index */
-                        uint8_t pdo_subindex, /**< PDO subindex */
-                        size_t size /**< new PDO size */
-                        )
-{
-    EC_WARN("ecrt_slave_pdo_size() currently not available.\n");
-    return -1;
-
-#if 0
-    unsigned int i, j, field_counter;
-    const ec_sii_sync_t *sync;
-    const ec_pdo_t *pdo;
-    ec_varsize_t *var;
-
-    if (!slave->type) {
-        EC_ERR("Slave %i has no type information!\n", slave->ring_position);
+void ecrt_slave_pdo_mapping_clear(
+        ec_slave_t *slave, /**< EtherCAT slave */
+        ec_direction_t dir /**< output/input */
+        )
+{
+    ec_sync_t *sync;
+
+    if (!(slave->sii_mailbox_protocols & EC_MBOX_COE)) {
+        EC_ERR("Slave %i does not support CoE!\n", slave->ring_position);
+        return;
+    }
+
+    if (!(sync = ec_slave_get_pdo_sync(slave, dir)))
+        return;
+
+    ec_sync_clear_pdos(sync);
+}
+
+/*****************************************************************************/
+
+int ecrt_slave_pdo_mapping_add(
+        ec_slave_t *slave, /**< EtherCAT slave */
+        ec_direction_t dir, /**< input/output */
+        uint16_t pdo_index /**< Index of PDO mapping list */)
+{
+    ec_pdo_t *pdo;
+    ec_sync_t *sync;
+    unsigned int not_found = 1;
+
+    if (!(slave->sii_mailbox_protocols & EC_MBOX_COE)) {
+        EC_ERR("Slave %u does not support CoE!\n", slave->ring_position);
         return -1;
     }
 
-    field_counter = 0;
-    for (i = 0; (sync = slave->type->sync_managers[i]); i++) {
-        for (j = 0; (field = sync->fields[j]); j++) {
-            if (!strcmp(field->name, field_name)) {
-                if (field_counter++ == field_index) {
-                    // is the size of this field variable?
-                    if (field->size) {
-                        EC_ERR("Field \"%s\"[%i] of slave %i has no variable"
-                               " size!\n", field->name, field_index,
-                               slave->ring_position);
-                        return -1;
-                    }
-                    // does a size specification already exist?
-                    list_for_each_entry(var, &slave->varsize_fields, list) {
-                        if (var->field == field) {
-                            EC_WARN("Resizing field \"%s\"[%i] of slave %i.\n",
-                                    field->name, field_index,
-                                    slave->ring_position);
-                            var->size = size;
-                            return 0;
-                        }
-                    }
-                    // create a new size specification...
-                    if (!(var = kmalloc(sizeof(ec_varsize_t), GFP_KERNEL))) {
-                        EC_ERR("Failed to allocate memory for varsize_t!\n");
-                        return -1;
-                    }
-                    var->field = field;
-                    var->size = size;
-                    list_add_tail(&var->list, &slave->varsize_fields);
-                    return 0;
-                }
-            }
-        }
-    }
-
-    EC_ERR("Slave %i (\"%s %s\") has no field \"%s\"[%i]!\n",
-           slave->ring_position, slave->type->vendor_name,
-           slave->type->product_name, field_name, field_index);
-    return -1;
-#endif
-}
+    // does the slave provide the PDO list?
+    list_for_each_entry(pdo, &slave->sii_pdos, list) {
+        if (pdo->index == pdo_index) {
+            not_found = 0;
+            break;
+        }
+    }
+
+    if (not_found) {
+        EC_ERR("Slave %u does not provide PDO 0x%04X!\n",
+                slave->ring_position, pdo_index);
+        return -1;
+    }
+
+    // check direction
+    if ((pdo->type == EC_TX_PDO && dir == EC_DIR_OUTPUT) ||
+            (pdo->type == EC_RX_PDO && dir == EC_DIR_INPUT)) {
+        EC_ERR("Invalid direction for PDO 0x%04X.\n", pdo_index);
+        return -1;
+    }
+
+
+    if (!(sync = ec_slave_get_pdo_sync(slave, dir))) {
+        EC_ERR("Failed to obtain sync manager for PDO mapping of slave %u!\n",
+                slave->ring_position);
+        return -1;
+    }
+
+    return ec_sync_add_pdo(sync, pdo);
+}
+
+/*****************************************************************************/
+
+int ecrt_slave_pdo_mapping(ec_slave_t *slave, /**< EtherCAT slave */
+        ec_direction_t dir, /**< input/output */
+        unsigned int num_args, /**< Number of following arguments */
+        ... /**< PDO indices to map */
+        )
+{
+    va_list ap;
+
+    ecrt_slave_pdo_mapping_clear(slave, dir);
+
+    va_start(ap, num_args);
+
+    for (; num_args; num_args--) {
+        if (ecrt_slave_pdo_mapping_add(
+                    slave, dir, (uint16_t) va_arg(ap, int))) {
+            return -1;
+        }
+    }
+
+    va_end(ap);
+    return 0;
+}
+
 
 /*****************************************************************************/
 
@@ -1290,7 +1363,9 @@
 EXPORT_SYMBOL(ecrt_slave_conf_sdo8);
 EXPORT_SYMBOL(ecrt_slave_conf_sdo16);
 EXPORT_SYMBOL(ecrt_slave_conf_sdo32);
-EXPORT_SYMBOL(ecrt_slave_pdo_size);
+EXPORT_SYMBOL(ecrt_slave_pdo_mapping_clear);
+EXPORT_SYMBOL(ecrt_slave_pdo_mapping_add);
+EXPORT_SYMBOL(ecrt_slave_pdo_mapping);
 
 /** \endcond */