Introduced EEPROM write requests: EEPROM write operations from user
authorFlorian Pose <fp@igh-essen.com>
Thu, 01 Mar 2007 21:34:10 +0000
changeset 601 d6d951b766e3
parent 600 b0660152f710
child 602 0c58446dec3c
Introduced EEPROM write requests: EEPROM write operations from user
space are now queued and block until completion. Also, appropriate error
codes are returned.
NEWS
master/fsm_master.c
master/fsm_master.h
master/master.c
master/master.h
master/slave.c
master/slave.h
--- a/NEWS	Thu Mar 01 21:23:07 2007 +0000
+++ b/NEWS	Thu Mar 01 21:34:10 2007 +0000
@@ -9,16 +9,18 @@
 * Added Intel PRO/100 ethernet driver (e100).
 * Added ethernet driver for NVIDIA nForce chipsets (forcedeth).
 * Removed "ec_eoeif_count" master module parameter.
-* Introduced "device IDs" to tell a master to wait for certain ethernet
-  devices.
-* Added "main" and "backup" parameters to master module. To hand over
+* Introduced "Device IDs" to tell a master to wait for connection of certain
+  ethernet devices.
+* Added "main" and "backup" parameters to master module to hand over
   device ID lists.
 * Changed format of sysconfig file and accordingly adjusted functionality
-  of the init script to handle device IDs.
+  of the init script to handle device ID lists.
 * Device interface changes:
   - Replaced ecdev_register() and ecdev_unregister() with ecdev_offer() and
     ecdev_withdraw(), respectively. The device modules now offer all their
     devices to the master, which decides, which ones to register.
+* All EEPROM write operations from user space are now blocking until
+  completion and returning appropriate error codes.
 * Removed annoying eeprom_write_enable file. EEPROM writing always enabled.
 * Removed EtherCAT line comments from 8139too drivers.
 
--- a/master/fsm_master.c	Thu Mar 01 21:23:07 2007 +0000
+++ b/master/fsm_master.c	Thu Mar 01 21:34:10 2007 +0000
@@ -275,6 +275,51 @@
 /*****************************************************************************/
 
 /**
+ * Check for pending EEPROM write requests and process one.
+ * \return non-zero, if an EEPROM write request is processed.
+ */
+
+int ec_fsm_master_action_process_eeprom(
+        ec_fsm_master_t *fsm /**< master state machine */
+        )
+{
+    ec_master_t *master = fsm->master;
+    ec_eeprom_write_request_t *request;
+    ec_slave_t *slave;
+
+    down(&master->eeprom_sem);
+    list_for_each_entry(request, &master->eeprom_requests, list) {
+        list_del_init(&request->list); // dequeue
+        up(&master->eeprom_sem);
+
+        slave = request->slave;
+        if (!slave->online || slave->error_flag) {
+            EC_ERR("Discarding EEPROM data, slave %i not ready.\n",
+                    slave->ring_position);
+            request->state = EC_EEPROM_REQ_ERROR;
+            wake_up_interruptible(&master->eeprom_queue);
+            down(&master->eeprom_sem);
+            continue;
+        }
+
+        // found pending EEPROM write operation. execute it!
+        EC_INFO("Writing EEPROM of slave %i...\n", slave->ring_position);
+        fsm->eeprom_request = request;
+        fsm->eeprom_index = 0;
+        ec_fsm_sii_write(&fsm->fsm_sii, request->slave, request->offset,
+                request->words, EC_FSM_SII_NODE);
+        fsm->state = ec_fsm_master_state_write_eeprom;
+        fsm->state(fsm); // execute immediately
+        return 1;
+    }
+
+    up(&master->eeprom_sem);
+    return 0;
+}
+
+/*****************************************************************************/
+
+/**
    Master action: PROC_STATES.
    Processes the slave states.
 */
@@ -368,27 +413,8 @@
         }
 
         // check for pending EEPROM write operations.
-        list_for_each_entry(slave, &master->slaves, list) {
-            if (!slave->new_eeprom_data) continue;
-
-            if (!slave->online || slave->error_flag) {
-                kfree(slave->new_eeprom_data);
-                slave->new_eeprom_data = NULL;
-                EC_ERR("Discarding EEPROM data, slave %i not ready.\n",
-                       slave->ring_position);
-                continue;
-            }
-
-            // found pending EEPROM write operation. execute it!
-            EC_INFO("Writing EEPROM of slave %i...\n", slave->ring_position);
-            fsm->slave = slave;
-            fsm->sii_offset = 0x0000;
-            ec_fsm_sii_write(&fsm->fsm_sii, slave, fsm->sii_offset,
-                             slave->new_eeprom_data, EC_FSM_SII_NODE);
-            fsm->state = ec_fsm_master_state_write_eeprom;
-            fsm->state(fsm); // execute immediately
-            return;
-        }
+        if (ec_fsm_master_action_process_eeprom(fsm))
+            return; // EEPROM write request found
     }
 
     fsm->state = ec_fsm_master_state_end;
@@ -751,36 +777,43 @@
 
 void ec_fsm_master_state_write_eeprom(ec_fsm_master_t *fsm /**< master state machine */)
 {
-    ec_slave_t *slave = fsm->slave;
+    ec_master_t *master = fsm->master;
+    ec_eeprom_write_request_t *request = fsm->eeprom_request;
+    ec_slave_t *slave = request->slave;
 
     if (ec_fsm_sii_exec(&fsm->fsm_sii)) return;
 
     if (!ec_fsm_sii_success(&fsm->fsm_sii)) {
-        fsm->slave->error_flag = 1;
+        slave->error_flag = 1;
         EC_ERR("Failed to write EEPROM contents to slave %i.\n",
                slave->ring_position);
-        kfree(slave->new_eeprom_data);
-        slave->new_eeprom_data = NULL;
-        fsm->state = ec_fsm_master_state_error;
-        return;
-    }
-
-    fsm->sii_offset++;
-    if (fsm->sii_offset < slave->new_eeprom_size) {
-        ec_fsm_sii_write(&fsm->fsm_sii, slave, fsm->sii_offset,
-                         slave->new_eeprom_data + fsm->sii_offset,
-                         EC_FSM_SII_NODE);
+        request->state = EC_EEPROM_REQ_ERROR;
+        wake_up_interruptible(&master->eeprom_queue);
+        fsm->state = ec_fsm_master_state_error;
+        return;
+    }
+
+    fsm->eeprom_index++;
+    if (fsm->eeprom_index < request->size) {
+        ec_fsm_sii_write(&fsm->fsm_sii, slave,
+                request->offset + fsm->eeprom_index,
+                request->words + fsm->eeprom_index,
+                EC_FSM_SII_NODE);
         ec_fsm_sii_exec(&fsm->fsm_sii); // execute immediately
         return;
     }
 
     // finished writing EEPROM
     EC_INFO("Finished writing EEPROM of slave %i.\n", slave->ring_position);
-    kfree(slave->new_eeprom_data);
-    slave->new_eeprom_data = NULL;
+    request->state = EC_EEPROM_REQ_COMPLETED;
+    wake_up_interruptible(&master->eeprom_queue);
 
     // TODO: Evaluate new EEPROM contents!
 
+    // check for another EEPROM write request
+    if (ec_fsm_master_action_process_eeprom(fsm))
+        return; // processing another request
+
     // restart master state machine.
     fsm->state = ec_fsm_master_state_start;
     fsm->state(fsm); // execute immediately
--- a/master/fsm_master.h	Thu Mar 01 21:23:07 2007 +0000
+++ b/master/fsm_master.h	Thu Mar 01 21:34:10 2007 +0000
@@ -51,6 +51,37 @@
 
 /*****************************************************************************/
 
+/**
+ * EEPROM request state.
+ */
+
+typedef enum
+{
+    EC_EEPROM_REQ_QUEUED,
+    EC_EEPROM_REQ_COMPLETED,
+    EC_EEPROM_REQ_ERROR
+}
+ec_eeprom_request_state_t;
+
+/*****************************************************************************/
+
+/**
+ * EEPROM write request.
+ */
+
+typedef struct
+{
+    struct list_head list;
+    ec_slave_t *slave;
+    off_t offset;
+    size_t size;
+    const uint16_t *words;
+    ec_eeprom_request_state_t state;
+}
+ec_eeprom_write_request_t;
+
+/*****************************************************************************/
+
 typedef struct ec_fsm_master ec_fsm_master_t; /**< \see ec_fsm_master */
 
 /**
@@ -69,8 +100,9 @@
     ec_slave_state_t slave_states; /**< states of responding slaves */
     unsigned int validate; /**< non-zero, if validation to do */
     ec_slave_t *slave; /**< current slave */
+    ec_eeprom_write_request_t *eeprom_request; /**< EEPROM write request */
+    off_t eeprom_index; /**< index to EEPROM write request data */
     ec_sdo_request_t *sdo_request; /**< SDO request to process */
-    uint16_t sii_offset; 
 
     ec_fsm_slave_t fsm_slave; /**< slave state machine */
     ec_fsm_sii_t fsm_sii; /**< SII state machine */
--- a/master/master.c	Thu Mar 01 21:23:07 2007 +0000
+++ b/master/master.c	Thu Mar 01 21:34:10 2007 +0000
@@ -152,6 +152,10 @@
     master->release_cb = NULL;
     master->cb_data = NULL;
 
+    INIT_LIST_HEAD(&master->eeprom_requests);
+    init_MUTEX(&master->eeprom_sem);
+    init_waitqueue_head(&master->eeprom_queue);
+
     master->sdo_request = NULL;
     master->sdo_seq_user = 0;
     master->sdo_seq_master = 0;
@@ -255,6 +259,9 @@
     ec_eoe_t *eoe, *next_eoe;
     ec_datagram_t *datagram, *next_datagram;
 
+    // list of EEPROM requests is empty,
+    // otherwise master could not be cleared.
+
     // dequeue all datagrams
     list_for_each_entry_safe(datagram, next_datagram,
                              &master->datagram_queue, queue) {
--- a/master/master.h	Thu Mar 01 21:23:07 2007 +0000
+++ b/master/master.h	Thu Mar 01 21:34:10 2007 +0000
@@ -44,6 +44,7 @@
 #include <linux/list.h>
 #include <linux/sysfs.h>
 #include <linux/timer.h>
+#include <linux/wait.h>
 #include <asm/atomic.h>
 #include <asm/semaphore.h>
 
@@ -138,6 +139,12 @@
     void (*release_cb)(void *); /**< lock release callback */
     void *cb_data; /**< data parameter of locking callbacks */
 
+    struct list_head eeprom_requests; /**< EEPROM write requests */
+    struct semaphore eeprom_sem; /**< semaphore protecting the list of
+                                   EEPROM write requests */
+    wait_queue_head_t eeprom_queue; /**< wait queue for EEPROM
+                                      write requests from user space */
+
     ec_sdo_request_t *sdo_request; /**< pointer to the current SDO request */
     unsigned int sdo_seq_user; /**< sequence number for user space */
     unsigned int sdo_seq_master; /**< sequence number for master */
--- a/master/slave.c	Thu Mar 01 21:23:07 2007 +0000
+++ b/master/slave.c	Thu Mar 01 21:34:10 2007 +0000
@@ -128,8 +128,6 @@
 
     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;
@@ -286,7 +284,6 @@
     }
 
     if (slave->eeprom_data) kfree(slave->eeprom_data);
-    if (slave->new_eeprom_data) kfree(slave->new_eeprom_data);
 
     kfree(slave);
 }
@@ -785,63 +782,71 @@
                               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->mode != EC_MASTER_MODE_IDLE) {
+    ec_eeprom_write_request_t request;
+    const uint16_t *cat_header;
+    uint16_t cat_type, cat_size;
+    ec_master_t *master = slave->master;
+
+    if (slave->master->mode != EC_MASTER_MODE_IDLE) { // FIXME
         EC_ERR("Writing EEPROMs only allowed in idle mode!\n");
-        return -EACCES;
-    }
-
-    if (slave->new_eeprom_data) {
-        EC_ERR("Slave %i already has a pending EEPROM write operation!\n",
-               slave->ring_position);
         return -EBUSY;
     }
 
-    // coarse check of the data
-
     if (size % 2) {
-        EC_ERR("EEPROM size is odd! Dropping.\n");
+        EC_ERR("EEPROM data size is odd! Dropping.\n");
         return -EINVAL;
     }
 
-    data_words = (const uint16_t *) data;
-    word_size = size / 2;
-
-    if (word_size < 0x0041) {
+    // 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;
+    request.state = EC_EEPROM_REQ_QUEUED;
+
+    if (request.size < 0x0041) {
         EC_ERR("EEPROM data too short! Dropping.\n");
         return -EINVAL;
     }
 
-    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");
+    cat_header = request.words + 0x0040; // first category header
+    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;
         }
-        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 -ENOMEM;
-    }
-    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 size;
+        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);
+    }
+
+    // data ok: 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
+    while (wait_event_interruptible(master->eeprom_queue,
+                request.state != EC_EEPROM_REQ_QUEUED)) {
+        // interrupted by signal
+        down(&master->eeprom_sem);
+        if (!list_empty(&request.list)) {
+            // still queued: safely dequeue
+            list_del(&request.list);
+            up(&master->eeprom_sem);
+            return -EINTR;
+        }
+        // request processing: interrupt not possible.
+        up(&master->eeprom_sem);
+    }
+
+    return request.state == EC_EEPROM_REQ_COMPLETED ? size : -EIO;
 }
 
 /*****************************************************************************/
--- a/master/slave.h	Thu Mar 01 21:23:07 2007 +0000
+++ b/master/slave.h	Thu Mar 01 21:34:10 2007 +0000
@@ -221,9 +221,7 @@
 
     // EEPROM
     uint8_t *eeprom_data; /**< Complete EEPROM image */
-    uint16_t eeprom_size; /**< size of the EEPROM contents in byte */
-    uint16_t *new_eeprom_data; /**< new EEPROM data to write */
-    uint16_t new_eeprom_size; /**< size of new EEPROM data in words */
+    size_t eeprom_size; /**< size of the EEPROM contents in bytes */
 
     // slave information interface
     uint16_t sii_alias; /**< configured station alias */