EEPROM writing via SysFS.
authorFlorian Pose <fp@igh-essen.com>
Fri, 02 Jun 2006 14:25:45 +0000
changeset 269 a03be9a6fed6
parent 268 4f9c149fb71f
child 270 f6ba0b288657
EEPROM writing via SysFS.
master/fsm.c
master/fsm.h
master/slave.c
master/slave.h
--- a/master/fsm.c	Fri Jun 02 12:01:47 2006 +0000
+++ b/master/fsm.c	Fri Jun 02 14:25:45 2006 +0000
@@ -58,6 +58,7 @@
 void ec_fsm_master_reconfigure(ec_fsm_t *);
 void ec_fsm_master_address(ec_fsm_t *);
 void ec_fsm_master_conf(ec_fsm_t *);
+void ec_fsm_master_eeprom(ec_fsm_t *);
 
 void ec_fsm_slave_start_reading(ec_fsm_t *);
 void ec_fsm_slave_read_status(ec_fsm_t *);
@@ -77,8 +78,11 @@
 void ec_fsm_slave_op2(ec_fsm_t *);
 
 void ec_fsm_sii_start_reading(ec_fsm_t *);
-void ec_fsm_sii_check(ec_fsm_t *);
-void ec_fsm_sii_fetch(ec_fsm_t *);
+void ec_fsm_sii_read_check(ec_fsm_t *);
+void ec_fsm_sii_read_fetch(ec_fsm_t *);
+void ec_fsm_sii_start_writing(ec_fsm_t *);
+void ec_fsm_sii_write_check(ec_fsm_t *);
+void ec_fsm_sii_write_check2(ec_fsm_t *);
 void ec_fsm_sii_end(ec_fsm_t *);
 void ec_fsm_sii_error(ec_fsm_t *);
 
@@ -406,7 +410,25 @@
         return;
     }
 
-    // nothing to configure. restart master state machine.
+    if (master->mode == EC_MASTER_MODE_FREERUN) {
+        // nothing to configure. check for pending EEPROM write operations.
+        list_for_each_entry(slave, &master->slaves, list) {
+            if (!slave->new_eeprom_data) continue;
+
+            // found pending EEPROM write operation. execute it!
+            EC_INFO("Writing EEPROM of slave %i...\n", slave->ring_position);
+            fsm->sii_offset = 0x0000;
+            memcpy(fsm->sii_value, slave->new_eeprom_data, 2);
+            fsm->sii_mode = 1;
+            fsm->sii_state = ec_fsm_sii_start_writing;
+            fsm->slave = slave;
+            fsm->master_state = ec_fsm_master_eeprom;
+            fsm->master_state(fsm); // execute immediately
+            return;
+        }
+    }
+
+    // nothing to do. restart master state machine.
     fsm->master_state = ec_fsm_master_start;
     fsm->master_state(fsm); // execute immediately
 }
@@ -434,7 +456,7 @@
 
     if (fsm->sii_state != ec_fsm_sii_end) return;
 
-    if (EC_READ_U32(fsm->sii_result) != slave->sii_vendor_id) {
+    if (EC_READ_U32(fsm->sii_value) != slave->sii_vendor_id) {
         EC_ERR("Slave %i: invalid vendor ID!\n", slave->ring_position);
         fsm->master_state = ec_fsm_master_start;
         fsm->master_state(fsm); // execute immediately
@@ -472,10 +494,10 @@
 
     if (fsm->sii_state != ec_fsm_sii_end) return;
 
-    if (EC_READ_U32(fsm->sii_result) != slave->sii_product_code) {
+    if (EC_READ_U32(fsm->sii_value) != slave->sii_product_code) {
         EC_ERR("Slave %i: invalid product code!\n", slave->ring_position);
         EC_ERR("expected 0x%08X, got 0x%08X.\n", slave->sii_product_code,
-               EC_READ_U32(fsm->sii_result));
+               EC_READ_U32(fsm->sii_value));
         fsm->master_state = ec_fsm_master_start;
         fsm->master_state(fsm); // execute immediately
         return;
@@ -666,6 +688,49 @@
     fsm->master_state(fsm); // execute immediately
 }
 
+/*****************************************************************************/
+
+/**
+   Master state: EEPROM.
+*/
+
+void ec_fsm_master_eeprom(ec_fsm_t *fsm /**< finite state machine */)
+{
+    ec_slave_t *slave = fsm->slave;
+
+    fsm->sii_state(fsm); // execute SII state machine
+
+    if (fsm->sii_state == ec_fsm_sii_error) {
+        EC_ERR("Failed to write EEPROM contents to slave %i.\n",
+               slave->ring_position);
+        kfree(slave->new_eeprom_data);
+        slave->new_eeprom_data = NULL;
+        fsm->master_state = ec_fsm_master_start;
+        fsm->master_state(fsm); // execute immediately
+        return;
+    }
+
+    if (fsm->sii_state != ec_fsm_sii_end) return;
+
+    fsm->sii_offset++;
+    if (fsm->sii_offset < slave->new_eeprom_size) {
+        memcpy(fsm->sii_value, slave->new_eeprom_data + fsm->sii_offset, 2);
+        fsm->sii_state = ec_fsm_sii_start_writing;
+        fsm->sii_state(fsm); // execute immediately
+        return;
+    }
+
+    // finished writing EEPROM
+    EC_INFO("Finished writing EEPROM of slave %i.\n", slave->ring_position);
+    kfree(slave->new_eeprom_data);
+    slave->new_eeprom_data = NULL;
+
+    // restart master state machine.
+    fsm->master_state = ec_fsm_master_start;
+    fsm->master_state(fsm); // execute immediately
+    return;
+}
+
 /******************************************************************************
  *  slave state machine
  *****************************************************************************/
@@ -834,8 +899,8 @@
 
     if (fsm->sii_state != ec_fsm_sii_end) return;
 
-    cat_type = EC_READ_U16(fsm->sii_result);
-    cat_size = EC_READ_U16(fsm->sii_result + 2);
+    cat_type = EC_READ_U16(fsm->sii_value);
+    cat_size = EC_READ_U16(fsm->sii_value + 2);
 
     if (cat_type != 0xFFFF) { // not the last category
         fsm->sii_offset += cat_size + 2;
@@ -895,10 +960,10 @@
     // 2 words fetched
 
     if (fsm->sii_offset + 2 <= slave->eeprom_size / 2) { // 2 words fit
-        memcpy(slave->eeprom_data + fsm->sii_offset * 2, fsm->sii_result, 4);
+        memcpy(slave->eeprom_data + fsm->sii_offset * 2, fsm->sii_value, 4);
     }
     else { // copy the last word
-        memcpy(slave->eeprom_data + fsm->sii_offset * 2, fsm->sii_result, 2);
+        memcpy(slave->eeprom_data + fsm->sii_offset * 2, fsm->sii_value, 2);
     }
 
     if (fsm->sii_offset + 2 < slave->eeprom_size / 2) {
@@ -1305,17 +1370,17 @@
     EC_WRITE_U8 (command->data + 1, 0x01); // request read operation
     EC_WRITE_U16(command->data + 2, fsm->sii_offset);
     ec_master_queue_command(fsm->master, command);
-    fsm->sii_state = ec_fsm_sii_check;
-}
-
-/*****************************************************************************/
-
-/**
-   SII state: CHECK.
+    fsm->sii_state = ec_fsm_sii_read_check;
+}
+
+/*****************************************************************************/
+
+/**
+   SII state: READ_CHECK.
    Checks, if the SII-read-command has been sent and issues a fetch command.
 */
 
-void ec_fsm_sii_check(ec_fsm_t *fsm /**< finite state machine */)
+void ec_fsm_sii_read_check(ec_fsm_t *fsm /**< finite state machine */)
 {
     ec_command_t *command = &fsm->command;
 
@@ -1336,17 +1401,17 @@
     }
 
     ec_master_queue_command(fsm->master, command);
-    fsm->sii_state = ec_fsm_sii_fetch;
-}
-
-/*****************************************************************************/
-
-/**
-   SII state: FETCH.
+    fsm->sii_state = ec_fsm_sii_read_fetch;
+}
+
+/*****************************************************************************/
+
+/**
+   SII state: READ_FETCH.
    Fetches the result of an SII-read command.
 */
 
-void ec_fsm_sii_fetch(ec_fsm_t *fsm /**< finite state machine */)
+void ec_fsm_sii_read_fetch(ec_fsm_t *fsm /**< finite state machine */)
 {
     ec_command_t *command = &fsm->command;
 
@@ -1391,13 +1456,93 @@
 #endif
 
     // SII value received.
-    memcpy(fsm->sii_result, command->data + 6, 4);
+    memcpy(fsm->sii_value, command->data + 6, 4);
     fsm->sii_state = ec_fsm_sii_end;
 }
 
 /*****************************************************************************/
 
 /**
+   SII state: START_WRITING.
+   Starts reading the slave information interface.
+*/
+
+void ec_fsm_sii_start_writing(ec_fsm_t *fsm /**< finite state machine */)
+{
+    ec_command_t *command = &fsm->command;
+
+    // initiate write operation
+    ec_command_npwr(command, fsm->slave->station_address, 0x502, 8);
+    EC_WRITE_U8 (command->data,     0x01); // enable write access
+    EC_WRITE_U8 (command->data + 1, 0x02); // request write operation
+    EC_WRITE_U32(command->data + 2, fsm->sii_offset);
+    memcpy(command->data + 6, fsm->sii_value, 2);
+    ec_master_queue_command(fsm->master, command);
+    fsm->sii_state = ec_fsm_sii_write_check;
+}
+
+/*****************************************************************************/
+
+/**
+   SII state: WRITE_CHECK.
+*/
+
+void ec_fsm_sii_write_check(ec_fsm_t *fsm /**< finite state machine */)
+{
+    ec_command_t *command = &fsm->command;
+
+    if (command->state != EC_CMD_RECEIVED || command->working_counter != 1) {
+        EC_ERR("SII: Reception of write command failed.\n");
+        fsm->sii_state = ec_fsm_sii_error;
+        return;
+    }
+
+    fsm->sii_start = get_cycles();
+
+    // issue check/fetch command
+    ec_command_nprd(command, fsm->slave->station_address, 0x502, 2);
+    ec_master_queue_command(fsm->master, command);
+    fsm->sii_state = ec_fsm_sii_write_check2;
+}
+
+/*****************************************************************************/
+
+/**
+   SII state: WRITE_CHECK2.
+*/
+
+void ec_fsm_sii_write_check2(ec_fsm_t *fsm /**< finite state machine */)
+{
+    ec_command_t *command = &fsm->command;
+
+    if (command->state != EC_CMD_RECEIVED || command->working_counter != 1) {
+        EC_ERR("SII: Reception of write check command failed.\n");
+        fsm->sii_state = ec_fsm_sii_error;
+        return;
+    }
+
+    if (EC_READ_U8(command->data + 1) & 0x82) {
+        // still busy... timeout?
+        if (get_cycles() - fsm->sii_start >= (cycles_t) 10 * cpu_khz) {
+            EC_ERR("SII: Write timeout.\n");
+            fsm->sii_state = ec_fsm_sii_error;
+        }
+
+        // issue check/fetch command again
+        ec_master_queue_command(fsm->master, command);
+    }
+    else if (EC_READ_U8(command->data + 1) & 0x40) {
+        EC_ERR("SII: Write operation failed!\n");
+        fsm->sii_state = ec_fsm_sii_error;
+    }
+    else { // success
+        fsm->sii_state = ec_fsm_sii_end;
+    }
+}
+
+/*****************************************************************************/
+
+/**
    SII state: END.
    End state of the slave SII state machine.
 */
--- a/master/fsm.h	Fri Jun 02 12:01:47 2006 +0000
+++ b/master/fsm.h	Fri Jun 02 14:25:45 2006 +0000
@@ -71,7 +71,7 @@
     void (*sii_state)(ec_fsm_t *); /**< SII state function */
     uint16_t sii_offset; /**< input: offset in SII */
     unsigned int sii_mode; /**< SII reading done by APRD (0) or NPRD (1) */
-    uint8_t sii_result[4]; /**< output: raw SII value (32bit) */
+    uint8_t sii_value[4]; /**< raw SII value (32bit) */
     cycles_t sii_start; /**< sii start */
 
     void (*change_state)(ec_fsm_t *); /**< slave state change state function */
--- a/master/slave.c	Fri Jun 02 12:01:47 2006 +0000
+++ b/master/slave.c	Fri Jun 02 14:25:45 2006 +0000
@@ -69,7 +69,7 @@
 EC_SYSFS_READ_ATTR(sii_name);
 EC_SYSFS_READ_ATTR(type);
 EC_SYSFS_READ_WRITE_ATTR(state);
-EC_SYSFS_READ_ATTR(eeprom);
+EC_SYSFS_READ_WRITE_ATTR(eeprom);
 
 static struct attribute *def_attrs[] = {
     &attr_ring_position,
@@ -157,6 +157,8 @@
     slave->current_state = EC_SLAVE_STATE_UNKNOWN;
     slave->state_error = 0;
     slave->online = 1;
+    slave->new_eeprom_data = NULL;
+    slave->new_eeprom_size = 0;
 
     ec_command_init(&slave->mbox_command);
 
@@ -239,6 +241,7 @@
     }
 
     if (slave->eeprom_data) kfree(slave->eeprom_data);
+    if (slave->new_eeprom_data) kfree(slave->new_eeprom_data);
 
     ec_command_clear(&slave->mbox_command);
 }
@@ -1247,6 +1250,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
 */
@@ -1359,6 +1439,10 @@
 
         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;
 }
--- a/master/slave.h	Fri Jun 02 12:01:47 2006 +0000
+++ b/master/slave.h	Fri Jun 02 14:25:45 2006 +0000
@@ -271,6 +271,9 @@
     char *eeprom_order; /**< slave order number acc. to EEPROM */
     char *eeprom_name; /**< slave name acc. to EEPROM */
 
+    uint16_t *new_eeprom_data; /**< new EEPROM data to write */
+    size_t new_eeprom_size; /**< size of new EEPROM data in words */
+
     struct list_head sdo_dictionary; /**< SDO directory list */
 
     ec_command_t mbox_command; /**< mailbox command */