master/slave.c
branchstable-1.0
changeset 1621 4bbe090553f7
parent 1619 0d4119024f55
child 1624 9dc190591c0f
--- a/master/slave.c	Mon May 29 09:54:18 2006 +0000
+++ b/master/slave.c	Mon Jun 26 15:04:06 2006 +0000
@@ -69,6 +69,7 @@
 EC_SYSFS_READ_ATTR(sii_name);
 EC_SYSFS_READ_ATTR(type);
 EC_SYSFS_READ_WRITE_ATTR(state);
+EC_SYSFS_READ_WRITE_ATTR(eeprom);
 
 static struct attribute *def_attrs[] = {
     &attr_ring_position,
@@ -79,6 +80,7 @@
     &attr_sii_name,
     &attr_type,
     &attr_state,
+    &attr_eeprom,
     NULL,
 };
 
@@ -145,6 +147,8 @@
     slave->type = NULL;
     slave->registered = 0;
     slave->fmmu_count = 0;
+    slave->eeprom_data = NULL;
+    slave->eeprom_size = 0;
     slave->eeprom_group = NULL;
     slave->eeprom_image = NULL;
     slave->eeprom_order = NULL;
@@ -153,13 +157,14 @@
     slave->current_state = EC_SLAVE_STATE_UNKNOWN;
     slave->state_error = 0;
     slave->online = 1;
-
-    ec_command_init(&slave->mbox_command);
+    slave->new_eeprom_data = NULL;
+    slave->new_eeprom_size = 0;
 
     INIT_LIST_HEAD(&slave->eeprom_strings);
     INIT_LIST_HEAD(&slave->eeprom_syncs);
     INIT_LIST_HEAD(&slave->eeprom_pdos);
     INIT_LIST_HEAD(&slave->sdo_dictionary);
+    INIT_LIST_HEAD(&slave->varsize_fields);
 
     for (i = 0; i < 4; i++) {
         slave->dl_link[i] = 0;
@@ -186,6 +191,7 @@
     ec_eeprom_pdo_entry_t *entry, *next_ent;
     ec_sdo_t *sdo, *next_sdo;
     ec_sdo_entry_t *en, *next_en;
+    ec_varsize_t *var, *next_var;
 
     slave = container_of(kobj, ec_slave_t, kobj);
 
@@ -234,7 +240,14 @@
         kfree(sdo);
     }
 
-    ec_command_clear(&slave->mbox_command);
+    // free information about variable sized data fields
+    list_for_each_entry_safe(var, next_var, &slave->varsize_fields, list) {
+        list_del(&var->list);
+        kfree(var);
+    }
+
+    if (slave->eeprom_data) kfree(slave->eeprom_data);
+    if (slave->new_eeprom_data) kfree(slave->new_eeprom_data);
 }
 
 /*****************************************************************************/
@@ -1121,6 +1134,8 @@
 
     EC_INFO("  EEPROM data:\n");
 
+    EC_INFO("    EEPROM content size: %i Bytes\n", slave->eeprom_size);
+
     if (slave->sii_alias)
         EC_INFO("    Configured station alias: 0x%04X (%i)\n",
                 slave->sii_alias, slave->sii_alias);
@@ -1239,6 +1254,83 @@
 /*****************************************************************************/
 
 /**
+   Schedules an EEPROM write operation.
+   \return 0 in case of success, else < 0
+*/
+
+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_FREERUN) {
+        EC_ERR("Writing EEPROMs only allowed in freerun 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
+
+    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 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;
+}
+
+/*****************************************************************************/
+
+/**
    Formats attribute data for SysFS read access.
    \return number of bytes to read
 */
@@ -1295,6 +1387,19 @@
                 return sprintf(buffer, "UNKNOWN\n");
         }
     }
+    else if (attr == &attr_eeprom) {
+        if (slave->eeprom_data) {
+            if (slave->eeprom_size > PAGE_SIZE) {
+                EC_ERR("EEPROM contents of slave %i exceed 1 page (%i/%i).\n",
+                       slave->ring_position, slave->eeprom_size,
+                       (int) PAGE_SIZE);
+            }
+            else {
+                memcpy(buffer, slave->eeprom_data, slave->eeprom_size);
+                return slave->eeprom_size;
+            }
+        }
+    }
 
     return 0;
 }
@@ -1338,10 +1443,51 @@
 
         EC_ERR("Failed to set slave state!\n");
     }
+    else if (attr == &attr_eeprom) {
+        if (!ec_slave_write_eeprom(slave, buffer, size))
+            return size;
+    }
 
     return -EINVAL;
 }
 
+/*****************************************************************************/
+
+/**
+   \return size of sync manager contents
+*/
+
+size_t ec_slave_calc_sync_size(const ec_slave_t *slave, /**< EtherCAT slave */
+                               const ec_sync_t *sync /**< sync manager */
+                               )
+{
+    unsigned int i, found;
+    const ec_field_t *field;
+    const ec_varsize_t *var;
+    size_t size;
+
+    // if size is specified, return size
+    if (sync->size) return sync->size;
+
+    // sync manager has variable size (size == 0).
+
+    size = 0;
+    for (i = 0; (field = sync->fields[i]); i++) {
+        found = 0;
+        list_for_each_entry(var, &slave->varsize_fields, list) {
+            if (var->field != field) continue;
+            size += var->size;
+            found = 1;
+        }
+
+        if (!found) {
+            EC_WARN("Variable data field \"%s\" of slave %i has no size"
+                    " information!\n", field->name, slave->ring_position);
+        }
+    }
+    return size;
+}
+
 /******************************************************************************
  *  Realtime interface
  *****************************************************************************/
@@ -1361,9 +1507,75 @@
 
 /*****************************************************************************/
 
+/**
+   \return 0 in case of success, else < 0
+   \ingroup RealtimeInterface
+*/
+
+int ecrt_slave_field_size(ec_slave_t *slave, /**< EtherCAT slave */
+                          const char *field_name, /**< data field name */
+                          unsigned int field_index, /**< data field index */
+                          size_t size /**< new data field size */
+                          )
+{
+    unsigned int i, j, field_counter;
+    const ec_sync_t *sync;
+    const ec_field_t *field;
+    ec_varsize_t *var;
+
+    if (!slave->type) {
+        EC_ERR("Slave %i has no type information!\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;
+}
+
+/*****************************************************************************/
+
 /**< \cond */
 
 EXPORT_SYMBOL(ecrt_slave_write_alias);
+EXPORT_SYMBOL(ecrt_slave_field_size);
 
 /**< \endcond */