changeset 3397:4cce65f46b89

io/nand: Add mechanism for devices to provide partition geometry later than device init time
author Ross Younger <wry@ecoscentric.com>
date Sat, 06 Dec 2014 13:13:54 +1300
parents ca49a7404897
children aecb165864c8
files packages/io/nand/current/ChangeLog packages/io/nand/current/include/nand_device.h packages/io/nand/current/src/nand.c
diffstat 3 files changed, 54 insertions(+), 11 deletions(-) [+]
line wrap: on
line diff
--- a/packages/io/nand/current/ChangeLog
+++ b/packages/io/nand/current/ChangeLog
@@ -1,6 +1,8 @@
 2014-12-06  Ross Younger  <wry@ecoscentric.com>
 
 	* src/nandinit.cxx: Another tweak to initialiser priority
+	* include/nand_device.h src/nand.c: Add mechanism for devices to
+	provide partition geometry later than device init time
 
 2014-12-02  Ross Younger  <wry@ecoscentric.com>
 
--- a/packages/io/nand/current/include/nand_device.h
+++ b/packages/io/nand/current/include/nand_device.h
@@ -230,9 +230,35 @@ struct _cyg_nand_device_t {
     /* Data about the device need not be defined statically. Indeed
      * it is usually preferable to autodetect parameters when the chip
      * is initialised. The details here need only be set at some point
-     * before fns->devinit() returns. */
+     * before fns->devinit() returns.
+     * Some devices may wish to store a partition table on the device
+     * itself, which is harder as it leads to a chicken-and-egg problem;
+     * in that case, fns->devinit() should set dev->plf_update_partitions,
+     * which the infrastructure will call later.
+     */
+    cyg_nand_partition partition[CYGNUM_NAND_MAX_PARTITIONS];
 
-    cyg_nand_partition partition[CYGNUM_NAND_MAX_PARTITIONS];
+    /* If present (set up by dev->devinit()) this function will be
+     * called on the first call to cyg_nand_get_partition().
+     * On exit it is expected to have updated dev->partition[].
+     *
+     * Return 0 if OK, or a negative error code if something went wrong.
+     *
+     * NOTE: This function will be called WITHOUT the device lock held
+     * and is expected to assert that lock only as far as necessary
+     * (in particular, when updating the partition table contents).
+     *
+     * NOTE: If something happens that makes it desirable to update
+     * the in-RAM partition table at runtime, you can call this
+     * function directly. If you do this, BEWARE: any existing
+     * clients will start using the updated geometry immediately.
+     * If a partition has ceased to be valid then its clients will
+     * immediately start returning errors!
+     */
+    int (*plf_update_partitions)(cyg_nand_device *dev);
+
+    /* Used by the infrastructure. */
+    cyg_bool plf_update_partitions_called;
 
     size_t page_bits; /* log2 of no of regular bytes per page */
     size_t spare_per_page; /* OOB area size in bytes */
--- a/packages/io/nand/current/src/nand.c
+++ b/packages/io/nand/current/src/nand.c
@@ -166,11 +166,33 @@ static cyg_nand_printf nand_default_pf;
     nand_default_pf = pf;
 }
 
+static void announce_partitions(cyg_nand_device *dev)
+{
+    int i, live_partitions = 0;
+    for (i=0; i<CYGNUM_NAND_MAX_PARTITIONS; i++)
+        if (dev->partition[i].dev) ++live_partitions;
+    if (live_partitions)
+        NAND_CHATTER(1,dev, "%s: %u partition%c configured\n", dev->devname, live_partitions, live_partitions==1 ? ' ' : 's' );
+    else
+        NAND_CHATTER(1,dev, "%s: NO partitions configured!\n", dev->devname); // hope they know what they're doing.
+}
+
 __externC
 cyg_nand_partition* cyg_nand_get_partition(cyg_nand_device *dev, unsigned partno)
 {
     if ((partno < 0) || (partno >= CYGNUM_NAND_MAX_PARTITIONS))
         return NULL;
+    if (!dev->plf_update_partitions_called) {
+        dev->plf_update_partitions_called = true; // Set this before the actual call, to kill a chicken-and-egg recursion where partition 0 needs to be read in order to determine the geometry of the other partitions.
+        if (dev->plf_update_partitions != NULL) {
+            int st = dev->plf_update_partitions(dev);
+            if (st != 0) {
+                NAND_ERROR(dev, "%s: Partition table update failed (code %d)\n", dev->devname, st);
+                // Well, we tried.
+            }
+        }
+        announce_partitions(dev);
+    }
     LOCK_DEV(dev);
     cyg_nand_partition *rv = &(dev->partition[partno]);
     UNLOCK_DEV(dev);
@@ -279,17 +301,10 @@ int cyg_nand_lookup(const char *devname,
                 goto done;
             }
 #endif
-
             cyg_drv_mutex_init(&dev->devlock);
+            dev->plf_update_partitions_called = false;
             dev->is_inited = 1;
-
-            int live_partitions = 0;
-            for (i=0; i<CYGNUM_NAND_MAX_PARTITIONS; i++)
-                if (dev->partition[i].dev) ++live_partitions;
-            if (live_partitions)
-                NAND_CHATTER(1,dev, "%s devinit complete, %u partition%c configured\n", devname, live_partitions, live_partitions==1 ? ' ' : 's' );
-            else
-                NAND_CHATTER(1,dev, "%s devinit complete, NO partitions configured!\n", devname); // hope they know what they're doing.
+            announce_partitions(dev);
         }
         if (dev_o) *dev_o = dev;
     }