]> git.sur5r.net Git - freertos/commitdiff
Update version of Reliance Edge.
authorrtel <rtel@1d2547de-c912-0410-9cb9-b8ca96c0e9e2>
Mon, 21 Nov 2016 04:30:49 +0000 (04:30 +0000)
committerrtel <rtel@1d2547de-c912-0410-9cb9-b8ca96c0e9e2>
Mon, 21 Nov 2016 04:30:49 +0000 (04:30 +0000)
git-svn-id: https://svn.code.sf.net/p/freertos/code/trunk@2472 1d2547de-c912-0410-9cb9-b8ca96c0e9e2

17 files changed:
FreeRTOS-Plus/Demo/FreeRTOS_Plus_Reliance_Edge_and_CLI_Windows_Simulator/ConfigurationFiles/redconf.c
FreeRTOS-Plus/Demo/FreeRTOS_Plus_Reliance_Edge_and_CLI_Windows_Simulator/WIN32.vcxproj
FreeRTOS-Plus/Demo/FreeRTOS_Plus_Reliance_Edge_and_CLI_Windows_Simulator/WIN32.vcxproj.filters
FreeRTOS-Plus/Source/Reliance-Edge/core/driver/blockio.c
FreeRTOS-Plus/Source/Reliance-Edge/core/driver/buffer.c
FreeRTOS-Plus/Source/Reliance-Edge/doc/release_notes.md
FreeRTOS-Plus/Source/Reliance-Edge/doc/release_notes.txt
FreeRTOS-Plus/Source/Reliance-Edge/include/redpath.h [new file with mode: 0644]
FreeRTOS-Plus/Source/Reliance-Edge/include/redtests.h
FreeRTOS-Plus/Source/Reliance-Edge/include/redver.h
FreeRTOS-Plus/Source/Reliance-Edge/include/redvolume.h
FreeRTOS-Plus/Source/Reliance-Edge/os/freertos/include/redosdeviations.h
FreeRTOS-Plus/Source/Reliance-Edge/os/freertos/services/osassert.c
FreeRTOS-Plus/Source/Reliance-Edge/os/freertos/services/osbdev.c
FreeRTOS-Plus/Source/Reliance-Edge/os/freertos/services/osmutex.c
FreeRTOS-Plus/Source/Reliance-Edge/posix/redpath.h [deleted file]
FreeRTOS-Plus/Source/Reliance-Edge/tests/util/printf.c

index 1a6a6e19d6c26f8537c342a73913d534162ca044..65feb5546701fd388578df511238dab061f478b0 100644 (file)
@@ -11,5 +11,5 @@
 \r
 const VOLCONF gaRedVolConf[REDCONF_VOLUME_COUNT] =\r
 {\r
-    { 512U, 65536U, false, 256U, "" }\r
+    { 512U, 65536U, false, 256U, 0, "" }\r
 };\r
index dbbae63cdd350d8e6ac1dfe292d5b2bdabd12ee0..6d377dc35f4d17bbb87e7520a91bbe4036ae36f8 100644 (file)
     <ClInclude Include="..\..\..\FreeRTOS\Source\include\task.h" />\r
     <ClInclude Include="..\..\..\FreeRTOS\Source\include\timers.h" />\r
     <ClInclude Include="..\..\Source\FreeRTOS-Plus-CLI\FreeRTOS_CLI.h" />\r
-    <ClInclude Include="..\..\Source\Reliance-Edge\os\freertos\include\ostypes.h" />\r
     <ClInclude Include="ConfigurationFiles\FreeRTOSConfig.h" />\r
     <ClInclude Include="ConfigurationFiles\redconf.h" />\r
     <ClInclude Include="ConfigurationFiles\redtypes.h" />\r
index fd68b27c1b7fc9df385617562cf575eb4fc57124..7d355d1e2f0956d925b2a7705e3b4d59238f68d4 100644 (file)
     <ClInclude Include="ConfigurationFiles\FreeRTOSConfig.h">\r
       <Filter>Configuration Files</Filter>\r
     </ClInclude>\r
-    <ClInclude Include="..\..\Source\Reliance-Edge\os\freertos\include\ostypes.h">\r
-      <Filter>FreeRTOS+\FreeRTOS+Reliance Edge\port</Filter>\r
-    </ClInclude>\r
     <ClInclude Include="ConfigurationFiles\redconf.h">\r
       <Filter>Configuration Files</Filter>\r
     </ClInclude>\r
index d815ca824faad3e1a74398c8caa2396334c7dbf4..897143de024ec9bd08d6968aafaa3f0fd064daf5 100644 (file)
-/*             ----> DO NOT REMOVE THE FOLLOWING NOTICE <----\r
-\r
-                   Copyright (c) 2014-2015 Datalight, Inc.\r
-                       All Rights Reserved Worldwide.\r
-\r
-    This program is free software; you can redistribute it and/or modify\r
-    it under the terms of the GNU General Public License as published by\r
-    the Free Software Foundation; use version 2 of the License.\r
-\r
-    This program is distributed in the hope that it will be useful,\r
-    but "AS-IS," WITHOUT ANY WARRANTY; without even the implied warranty\r
-    of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
-    GNU General Public License for more details.\r
-\r
-    You should have received a copy of the GNU General Public License along\r
-    with this program; if not, write to the Free Software Foundation, Inc.,\r
-    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
-*/\r
-/*  Businesses and individuals that for commercial or other reasons cannot\r
-    comply with the terms of the GPLv2 license may obtain a commercial license\r
-    before incorporating Reliance Edge into proprietary software for\r
-    distribution in any form.  Visit http://www.datalight.com/reliance-edge for\r
-    more information.\r
-*/\r
-/** @file\r
-    @brief Implements block device I/O using logical blocks as the units.\r
-\r
-    The OS block device implementations operate on sectors.  The core does I/O\r
-    in terms of logical blocks: this module translates from logical blocks to\r
-    sectors.\r
-*/\r
-#include <redfs.h>\r
-#include <redcore.h>\r
-\r
-\r
-/** @brief Read a range of logical blocks.\r
-\r
-    @param bVolNum      The volume whose block device is being read from.\r
-    @param ulBlockStart The first block to read.\r
-    @param ulBlockCount The number of blocks to read.\r
-    @param pBuffer      The buffer to populate with the data read.\r
-\r
-    @return A negated ::REDSTATUS code indicating the operation result.\r
-\r
-    @retval 0           Operation was successful.\r
-    @retval -RED_EIO    A disk I/O error occurred.\r
-    @retval -RED_EINVAL Invalid parameters.\r
-*/\r
-REDSTATUS RedIoRead(\r
-    uint8_t     bVolNum,\r
-    uint32_t    ulBlockStart,\r
-    uint32_t    ulBlockCount,\r
-    void       *pBuffer)\r
-{\r
-    REDSTATUS   ret;\r
-\r
-    if(    (bVolNum >= REDCONF_VOLUME_COUNT)\r
-        || (ulBlockStart >= gaRedVolume[bVolNum].ulBlockCount)\r
-        || ((gaRedVolume[bVolNum].ulBlockCount - ulBlockStart) < ulBlockCount)\r
-        || (ulBlockCount == 0U)\r
-        || (pBuffer == NULL))\r
-    {\r
-        REDERROR();\r
-        ret = -RED_EINVAL;\r
-    }\r
-    else\r
-    {\r
-        uint8_t  bSectorShift = gaRedVolume[bVolNum].bBlockSectorShift;\r
-        uint64_t ullSectorStart = (uint64_t)ulBlockStart << bSectorShift;\r
-        uint32_t ulSectorCount = ulBlockCount << bSectorShift;\r
-\r
-        REDASSERT(bSectorShift < 32U);\r
-        REDASSERT((ulSectorCount >> bSectorShift) == ulBlockCount);\r
-\r
-        ret = RedOsBDevRead(bVolNum, ullSectorStart, ulSectorCount, pBuffer);\r
-    }\r
-\r
-    CRITICAL_ASSERT(ret == 0);\r
-\r
-    return ret;\r
-}\r
-\r
-\r
-#if REDCONF_READ_ONLY == 0\r
-/** @brief Write a range of logical blocks.\r
-\r
-    @param bVolNum      The volume whose block device is being written to.\r
-    @param ulBlockStart The first block to write.\r
-    @param ulBlockCount The number of blocks to write.\r
-    @param pBuffer      The buffer containing the data to write.\r
-\r
-    @return A negated ::REDSTATUS code indicating the operation result.\r
-\r
-    @retval 0           Operation was successful.\r
-    @retval -RED_EIO    A disk I/O error occurred.\r
-    @retval -RED_EINVAL Invalid parameters.\r
-*/\r
-REDSTATUS RedIoWrite(\r
-    uint8_t     bVolNum,\r
-    uint32_t    ulBlockStart,\r
-    uint32_t    ulBlockCount,\r
-    const void *pBuffer)\r
-{\r
-    REDSTATUS   ret;\r
-\r
-    if(    (bVolNum >= REDCONF_VOLUME_COUNT)\r
-        || (ulBlockStart >= gaRedVolume[bVolNum].ulBlockCount)\r
-        || ((gaRedVolume[bVolNum].ulBlockCount - ulBlockStart) < ulBlockCount)\r
-        || (ulBlockCount == 0U)\r
-        || (pBuffer == NULL))\r
-    {\r
-        REDERROR();\r
-        ret = -RED_EINVAL;\r
-    }\r
-    else\r
-    {\r
-        uint8_t  bSectorShift = gaRedVolume[bVolNum].bBlockSectorShift;\r
-        uint64_t ullSectorStart = (uint64_t)ulBlockStart << bSectorShift;\r
-        uint32_t ulSectorCount = ulBlockCount << bSectorShift;\r
-\r
-        REDASSERT(bSectorShift < 32U);\r
-        REDASSERT((ulSectorCount >> bSectorShift) == ulBlockCount);\r
-\r
-        ret = RedOsBDevWrite(bVolNum, ullSectorStart, ulSectorCount, pBuffer);\r
-    }\r
-\r
-    CRITICAL_ASSERT(ret == 0);\r
-\r
-    return ret;\r
-}\r
-\r
-\r
-/** @brief Flush any caches beneath the file system.\r
-\r
-    @param bVolNum  The volume number of the volume whose block device is being\r
-                    flushed.\r
-\r
-    @return A negated ::REDSTATUS code indicating the operation result.\r
-\r
-    @retval 0           Operation was successful.\r
-    @retval -RED_EINVAL @p bVolNum is an invalid volume number.\r
-    @retval -RED_EIO    A disk I/O error occurred.\r
-*/\r
-REDSTATUS RedIoFlush(\r
-    uint8_t     bVolNum)\r
-{\r
-    REDSTATUS   ret;\r
-\r
-    if(bVolNum >= REDCONF_VOLUME_COUNT)\r
-    {\r
-        REDERROR();\r
-        ret = -RED_EINVAL;\r
-    }\r
-    else\r
-    {\r
-        ret = RedOsBDevFlush(bVolNum);\r
-    }\r
-\r
-    CRITICAL_ASSERT(ret == 0);\r
-\r
-    return ret;\r
-}\r
-#endif /* REDCONF_READ_ONLY == 0 */\r
-\r
+/*             ----> DO NOT REMOVE THE FOLLOWING NOTICE <----
+
+                   Copyright (c) 2014-2015 Datalight, Inc.
+                       All Rights Reserved Worldwide.
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; use version 2 of the License.
+
+    This program is distributed in the hope that it will be useful,
+    but "AS-IS," WITHOUT ANY WARRANTY; without even the implied warranty
+    of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+/*  Businesses and individuals that for commercial or other reasons cannot
+    comply with the terms of the GPLv2 license may obtain a commercial license
+    before incorporating Reliance Edge into proprietary software for
+    distribution in any form.  Visit http://www.datalight.com/reliance-edge for
+    more information.
+*/
+/** @file
+    @brief Implements block device I/O using logical blocks as the units.
+
+    The OS block device implementations operate on sectors.  The core does I/O
+    in terms of logical blocks: this module translates from logical blocks to
+    sectors.
+
+    If bBlockIoRetries is greater than 0 for the current volume, then this
+    module will retry block device calls on failure up to the configured number
+    of times.  This behavior caters to the type of unreliable hardware and
+    drivers that are sometimes found in the IoT world, where one operation may
+    fail but the next may still succeed.
+*/
+#include <redfs.h>
+#include <redcore.h>
+
+
+/** @brief Read a range of logical blocks.
+
+    @param bVolNum      The volume whose block device is being read from.
+    @param ulBlockStart The first block to read.
+    @param ulBlockCount The number of blocks to read.
+    @param pBuffer      The buffer to populate with the data read.
+
+    @return A negated ::REDSTATUS code indicating the operation result.
+
+    @retval 0           Operation was successful.
+    @retval -RED_EIO    A disk I/O error occurred.
+    @retval -RED_EINVAL Invalid parameters.
+*/
+REDSTATUS RedIoRead(
+    uint8_t     bVolNum,
+    uint32_t    ulBlockStart,
+    uint32_t    ulBlockCount,
+    void       *pBuffer)
+{
+    REDSTATUS   ret = 0;
+
+    if(    (bVolNum >= REDCONF_VOLUME_COUNT)
+        || (ulBlockStart >= gaRedVolume[bVolNum].ulBlockCount)
+        || ((gaRedVolume[bVolNum].ulBlockCount - ulBlockStart) < ulBlockCount)
+        || (ulBlockCount == 0U)
+        || (pBuffer == NULL))
+    {
+        REDERROR();
+        ret = -RED_EINVAL;
+    }
+    else
+    {
+        uint8_t  bSectorShift = gaRedVolume[bVolNum].bBlockSectorShift;
+        uint64_t ullSectorStart = (uint64_t)ulBlockStart << bSectorShift;
+        uint32_t ulSectorCount = ulBlockCount << bSectorShift;
+        uint8_t  bRetryIdx;
+
+        REDASSERT(bSectorShift < 32U);
+        REDASSERT((ulSectorCount >> bSectorShift) == ulBlockCount);
+
+        for(bRetryIdx = 0U; bRetryIdx <= gpRedVolConf->bBlockIoRetries; bRetryIdx++)
+        {
+            ret = RedOsBDevRead(bVolNum, ullSectorStart, ulSectorCount, pBuffer);
+
+            if(ret == 0)
+            {
+                break;
+            }
+        }
+    }
+
+    CRITICAL_ASSERT(ret == 0);
+
+    return ret;
+}
+
+
+#if REDCONF_READ_ONLY == 0
+/** @brief Write a range of logical blocks.
+
+    @param bVolNum      The volume whose block device is being written to.
+    @param ulBlockStart The first block to write.
+    @param ulBlockCount The number of blocks to write.
+    @param pBuffer      The buffer containing the data to write.
+
+    @return A negated ::REDSTATUS code indicating the operation result.
+
+    @retval 0           Operation was successful.
+    @retval -RED_EIO    A disk I/O error occurred.
+    @retval -RED_EINVAL Invalid parameters.
+*/
+REDSTATUS RedIoWrite(
+    uint8_t     bVolNum,
+    uint32_t    ulBlockStart,
+    uint32_t    ulBlockCount,
+    const void *pBuffer)
+{
+    REDSTATUS   ret = 0;
+
+    if(    (bVolNum >= REDCONF_VOLUME_COUNT)
+        || (ulBlockStart >= gaRedVolume[bVolNum].ulBlockCount)
+        || ((gaRedVolume[bVolNum].ulBlockCount - ulBlockStart) < ulBlockCount)
+        || (ulBlockCount == 0U)
+        || (pBuffer == NULL))
+    {
+        REDERROR();
+        ret = -RED_EINVAL;
+    }
+    else
+    {
+        uint8_t  bSectorShift = gaRedVolume[bVolNum].bBlockSectorShift;
+        uint64_t ullSectorStart = (uint64_t)ulBlockStart << bSectorShift;
+        uint32_t ulSectorCount = ulBlockCount << bSectorShift;
+        uint8_t  bRetryIdx;
+
+        REDASSERT(bSectorShift < 32U);
+        REDASSERT((ulSectorCount >> bSectorShift) == ulBlockCount);
+
+        for(bRetryIdx = 0U; bRetryIdx <= gpRedVolConf->bBlockIoRetries; bRetryIdx++)
+        {
+            ret = RedOsBDevWrite(bVolNum, ullSectorStart, ulSectorCount, pBuffer);
+
+            if(ret == 0)
+            {
+                break;
+            }
+        }
+    }
+
+    CRITICAL_ASSERT(ret == 0);
+
+    return ret;
+}
+
+
+/** @brief Flush any caches beneath the file system.
+
+    @param bVolNum  The volume number of the volume whose block device is being
+                    flushed.
+
+    @return A negated ::REDSTATUS code indicating the operation result.
+
+    @retval 0           Operation was successful.
+    @retval -RED_EINVAL @p bVolNum is an invalid volume number.
+    @retval -RED_EIO    A disk I/O error occurred.
+*/
+REDSTATUS RedIoFlush(
+    uint8_t     bVolNum)
+{
+    REDSTATUS   ret = 0;
+
+    if(bVolNum >= REDCONF_VOLUME_COUNT)
+    {
+        REDERROR();
+        ret = -RED_EINVAL;
+    }
+    else
+    {
+        uint8_t  bRetryIdx;
+
+        for(bRetryIdx = 0U; bRetryIdx <= gpRedVolConf->bBlockIoRetries; bRetryIdx++)
+        {
+            ret = RedOsBDevFlush(bVolNum);
+
+            if(ret == 0)
+            {
+                break;
+            }
+        }
+    }
+
+    CRITICAL_ASSERT(ret == 0);
+
+    return ret;
+}
+#endif /* REDCONF_READ_ONLY == 0 */
+
index b1cfea2811e81a1856a7dfccf0df2a4aaba4ccc3..17717e0f4bb8cbb5c144e04a61d612e66d3f34e1 100644 (file)
-/*             ----> DO NOT REMOVE THE FOLLOWING NOTICE <----\r
-\r
-                   Copyright (c) 2014-2015 Datalight, Inc.\r
-                       All Rights Reserved Worldwide.\r
-\r
-    This program is free software; you can redistribute it and/or modify\r
-    it under the terms of the GNU General Public License as published by\r
-    the Free Software Foundation; use version 2 of the License.\r
-\r
-    This program is distributed in the hope that it will be useful,\r
-    but "AS-IS," WITHOUT ANY WARRANTY; without even the implied warranty\r
-    of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
-    GNU General Public License for more details.\r
-\r
-    You should have received a copy of the GNU General Public License along\r
-    with this program; if not, write to the Free Software Foundation, Inc.,\r
-    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
-*/\r
-/*  Businesses and individuals that for commercial or other reasons cannot\r
-    comply with the terms of the GPLv2 license may obtain a commercial license\r
-    before incorporating Reliance Edge into proprietary software for\r
-    distribution in any form.  Visit http://www.datalight.com/reliance-edge for\r
-    more information.\r
-*/\r
-/** @file\r
-    @brief Implements the block device buffering system.\r
-\r
-    This module implements the block buffer cache.  It has a number of block\r
-    sized buffers which are used to store data from a given block (identified\r
-    by both block number and volume number: this cache is shared among all\r
-    volumes).  Block buffers may be either dirty or clean.  Most I/O passes\r
-    through this module.  When a buffer is needed for a block which is not in\r
-    the cache, a "victim" is selected via a simple LRU scheme.\r
-*/\r
-#include <redfs.h>\r
-#include <redcore.h>\r
-\r
-\r
-#if DINDIR_POINTERS > 0U\r
-  #define INODE_META_BUFFERS 3U /* Inode, double indirect, indirect */\r
-#elif REDCONF_INDIRECT_POINTERS > 0U\r
-  #define INODE_META_BUFFERS 2U /* Inode, indirect */\r
-#elif REDCONF_DIRECT_POINTERS == INODE_ENTRIES\r
-  #define INODE_META_BUFFERS 1U /* Inode only */\r
-#endif\r
-\r
-#define INODE_BUFFERS (INODE_META_BUFFERS + 1U) /* Add data buffer */\r
-\r
-#if REDCONF_IMAP_EXTERNAL == 1\r
-  #define IMAP_BUFFERS 1U\r
-#else\r
-  #define IMAP_BUFFERS 0U\r
-#endif\r
-\r
-#if (REDCONF_READ_ONLY == 1) || (REDCONF_API_FSE == 1)\r
-  /*  Read, write, truncate, lookup: One inode all the way down, plus imap.\r
-  */\r
-  #define MINIMUM_BUFFER_COUNT (INODE_BUFFERS + IMAP_BUFFERS)\r
-#elif REDCONF_API_POSIX == 1\r
-  #if REDCONF_API_POSIX_RENAME == 1\r
-    #if REDCONF_RENAME_ATOMIC == 1\r
-      /*  Two parent directories all the way down.  Source and destination inode\r
-          buffer.  One inode buffer for cyclic rename detection.  Imap.  The\r
-          parent inode buffers are released before deleting the destination\r
-          inode, so that does not increase the minimum.\r
-      */\r
-      #define MINIMUM_BUFFER_COUNT (INODE_BUFFERS + INODE_BUFFERS + 3U + IMAP_BUFFERS)\r
-    #else\r
-      /*  Two parent directories all the way down.  Source inode buffer.  One\r
-          inode buffer for cyclic rename detection.  Imap.\r
-      */\r
-      #define MINIMUM_BUFFER_COUNT (INODE_BUFFERS + INODE_BUFFERS + 2U + IMAP_BUFFERS)\r
-    #endif\r
-  #else\r
-    /*  Link/create: Needs a parent inode all the way down, an extra inode\r
-        buffer, and an imap buffer.\r
-\r
-        Unlink is the same, since the parent inode buffers are released before\r
-        the inode is deleted.\r
-    */\r
-    #define MINIMUM_BUFFER_COUNT (INODE_BUFFERS + 1U + IMAP_BUFFERS)\r
-  #endif\r
-#endif\r
-\r
-#if REDCONF_BUFFER_COUNT < MINIMUM_BUFFER_COUNT\r
-#error "REDCONF_BUFFER_COUNT is too low for the configuration"\r
-#endif\r
-\r
-\r
-/*  A note on the typecasts in the below macros: Operands to bitwise operators\r
-    are subject to the "usual arithmetic conversions".  This means that the\r
-    flags, which have uint16_t values, are promoted to int.  MISRA-C:2012 R10.1\r
-    forbids using signed integers in bitwise operations, so we cast to uint32_t\r
-    to avoid the integer promotion, then back to uint16_t to reflect the actual\r
-    type.\r
-*/\r
-#define BFLAG_META_MASK (uint16_t)((uint32_t)BFLAG_META_MASTER | BFLAG_META_IMAP | BFLAG_META_INODE | BFLAG_META_INDIR | BFLAG_META_DINDIR)\r
-#define BFLAG_MASK (uint16_t)((uint32_t)BFLAG_DIRTY | BFLAG_NEW | BFLAG_META_MASK)\r
-\r
-\r
-/*  An invalid block number.  Used to indicate buffers which are not currently\r
-    in use.\r
-*/\r
-#define BBLK_INVALID UINT32_MAX\r
-\r
-\r
-/** @brief Metadata stored for each block buffer.\r
-\r
-    To make better use of CPU caching when searching the BUFFERHEAD array, this\r
-    structure should be kept small.\r
-*/\r
-typedef struct\r
-{\r
-    uint32_t    ulBlock;    /**< Block number the buffer is associated with; BBLK_INVALID if unused. */\r
-    uint8_t     bVolNum;    /**< Volume the block resides on. */\r
-    uint8_t     bRefCount;  /**< Number of references. */\r
-    uint16_t    uFlags;     /**< Buffer flags: mask of BFLAG_* values. */\r
-} BUFFERHEAD;\r
-\r
-\r
-/** @brief State information for the block buffer module.\r
-*/\r
-typedef struct\r
-{\r
-    /** Number of buffers which are referenced (have a bRefCount > 0).\r
-    */\r
-    uint16_t    uNumUsed;\r
-\r
-    /** MRU array.  Each element of the array stores a buffer index; each buffer\r
-        index appears in the array once and only once.  The first element of the\r
-        array is the most-recently-used (MRU) buffer, followed by the next most\r
-        recently used, and so on, till the last element, which is the least-\r
-        recently-used (LRU) buffer.\r
-    */\r
-    uint8_t     abMRU[REDCONF_BUFFER_COUNT];\r
-\r
-    /** Buffer heads, storing metadata for each buffer.\r
-    */\r
-    BUFFERHEAD  aHead[REDCONF_BUFFER_COUNT];\r
-\r
-    /** Array of memory for the block buffers themselves.\r
-\r
-        Force 64-bit alignment of the aabBuffer array to ensure that it is safe\r
-        to cast buffer pointers to node structure pointers.\r
-    */\r
-    ALIGNED_2D_BYTE_ARRAY(b, aabBuffer, REDCONF_BUFFER_COUNT, REDCONF_BLOCK_SIZE);\r
-} BUFFERCTX;\r
-\r
-\r
-static bool BufferIsValid(const uint8_t  *pbBuffer, uint16_t uFlags);\r
-static bool BufferToIdx(const void *pBuffer, uint8_t *pbIdx);\r
-#if REDCONF_READ_ONLY == 0\r
-static REDSTATUS BufferWrite(uint8_t bIdx);\r
-static REDSTATUS BufferFinalize(uint8_t *pbBuffer, uint16_t uFlags);\r
-#endif\r
-static void BufferMakeLRU(uint8_t bIdx);\r
-static void BufferMakeMRU(uint8_t bIdx);\r
-static bool BufferFind(uint32_t ulBlock, uint8_t *pbIdx);\r
-\r
-#ifdef REDCONF_ENDIAN_SWAP\r
-static void BufferEndianSwap(const void *pBuffer, uint16_t uFlags);\r
-static void BufferEndianSwapHeader(NODEHEADER *pHeader);\r
-static void BufferEndianSwapMaster(MASTERBLOCK *pMaster);\r
-static void BufferEndianSwapMetaRoot(METAROOT *pMetaRoot);\r
-static void BufferEndianSwapInode(INODE *pInode);\r
-static void BufferEndianSwapIndir(INDIR *pIndir);\r
-#endif\r
-\r
-\r
-static BUFFERCTX gBufCtx;\r
-\r
-\r
-/** @brief Initialize the buffers.\r
-*/\r
-void RedBufferInit(void)\r
-{\r
-    uint8_t bIdx;\r
-\r
-    RedMemSet(&gBufCtx, 0U, sizeof(gBufCtx));\r
-\r
-    for(bIdx = 0U; bIdx < REDCONF_BUFFER_COUNT; bIdx++)\r
-    {\r
-        /*  When the buffers have been freshly initialized, acquire the buffers\r
-            in the order in which they appear in the array.\r
-        */\r
-        gBufCtx.abMRU[bIdx] = (uint8_t)((REDCONF_BUFFER_COUNT - bIdx) - 1U);\r
-        gBufCtx.aHead[bIdx].ulBlock = BBLK_INVALID;\r
-    }\r
-}\r
-\r
-\r
-/** @brief Acquire a buffer.\r
-\r
-    @param ulBlock  Block number to acquire.\r
-    @param uFlags   BFLAG_ values for the operation.\r
-    @param ppBuffer On success, populated with the acquired buffer.\r
-\r
-    @return A negated ::REDSTATUS code indicating the operation result.\r
-\r
-    @retval 0           Operation was successful.\r
-    @retval -RED_EIO    A disk I/O error occurred.\r
-    @retval -RED_EINVAL Invalid parameters.\r
-    @retval -RED_EBUSY  All buffers are referenced.\r
-*/\r
-REDSTATUS RedBufferGet(\r
-    uint32_t    ulBlock,\r
-    uint16_t    uFlags,\r
-    void      **ppBuffer)\r
-{\r
-    REDSTATUS   ret = 0;\r
-    uint8_t     bIdx;\r
-\r
-    if((ulBlock >= gpRedVolume->ulBlockCount) || ((uFlags & BFLAG_MASK) != uFlags) || (ppBuffer == NULL))\r
-    {\r
-        REDERROR();\r
-        ret = -RED_EINVAL;\r
-    }\r
-    else\r
-    {\r
-        if(BufferFind(ulBlock, &bIdx))\r
-        {\r
-            /*  Error if the buffer exists and BFLAG_NEW was specified, since\r
-                the new flag is used when a block is newly allocated/created, so\r
-                the block was previously free and and there should never be an\r
-                existing buffer for a free block.\r
-\r
-                Error if the buffer exists but does not have the same type as\r
-                was requested.\r
-            */\r
-            if(    ((uFlags & BFLAG_NEW) != 0U)\r
-                || ((uFlags & BFLAG_META_MASK) != (gBufCtx.aHead[bIdx].uFlags & BFLAG_META_MASK)))\r
-            {\r
-                CRITICAL_ERROR();\r
-                ret = -RED_EFUBAR;\r
-            }\r
-        }\r
-        else if(gBufCtx.uNumUsed == REDCONF_BUFFER_COUNT)\r
-        {\r
-            /*  The MINIMUM_BUFFER_COUNT is supposed to ensure that no operation\r
-                ever runs out of buffers, so this should never happen.\r
-            */\r
-            CRITICAL_ERROR();\r
-            ret = -RED_EBUSY;\r
-        }\r
-        else\r
-        {\r
-            BUFFERHEAD *pHead;\r
-\r
-            /*  Search for the least recently used buffer which is not\r
-                referenced.\r
-            */\r
-            for(bIdx = (uint8_t)(REDCONF_BUFFER_COUNT - 1U); bIdx > 0U; bIdx--)\r
-            {\r
-                if(gBufCtx.aHead[gBufCtx.abMRU[bIdx]].bRefCount == 0U)\r
-                {\r
-                    break;\r
-                }\r
-            }\r
-\r
-            bIdx = gBufCtx.abMRU[bIdx];\r
-            pHead = &gBufCtx.aHead[bIdx];\r
-\r
-            if(pHead->bRefCount == 0U)\r
-            {\r
-                /*  If the LRU buffer is valid and dirty, write it out before\r
-                    repurposing it.\r
-                */\r
-                if(((pHead->uFlags & BFLAG_DIRTY) != 0U) && (pHead->ulBlock != BBLK_INVALID))\r
-                {\r
-                  #if REDCONF_READ_ONLY == 1\r
-                    CRITICAL_ERROR();\r
-                    ret = -RED_EFUBAR;\r
-                  #else\r
-                    ret = BufferWrite(bIdx);\r
-                  #endif\r
-                }\r
-            }\r
-            else\r
-            {\r
-                /*  All the buffers are used, which should have been caught by\r
-                    checking gBufCtx.uNumUsed.\r
-                */\r
-                CRITICAL_ERROR();\r
-                ret = -RED_EBUSY;\r
-            }\r
-\r
-            if(ret == 0)\r
-            {\r
-                if((uFlags & BFLAG_NEW) == 0U)\r
-                {\r
-                    /*  Invalidate the LRU buffer.  If the read fails, we do not\r
-                        want the buffer head to continue to refer to the old\r
-                        block number, since the read, even if it fails, may have\r
-                        partially overwritten the buffer data (consider the case\r
-                        where block size exceeds sector size, and some but not\r
-                        all of the sectors are read successfully), and if the\r
-                        buffer were to be used subsequently with its partially\r
-                        erroneous contents, bad things could happen.\r
-                    */\r
-                    pHead->ulBlock = BBLK_INVALID;\r
-\r
-                    ret = RedIoRead(gbRedVolNum, ulBlock, 1U, gBufCtx.b.aabBuffer[bIdx]);\r
-\r
-                    if((ret == 0) && ((uFlags & BFLAG_META) != 0U))\r
-                    {\r
-                        if(!BufferIsValid(gBufCtx.b.aabBuffer[bIdx], uFlags))\r
-                        {\r
-                            /*  A corrupt metadata node is usually a critical\r
-                                error.  The master block is an exception since\r
-                                it might be invalid because the volume is not\r
-                                mounted; that condition is expected and should\r
-                                not result in an assertion.\r
-                            */\r
-                            CRITICAL_ASSERT((uFlags & BFLAG_META_MASTER) != 0U);\r
-                            ret = -RED_EIO;\r
-                        }\r
-                    }\r
-\r
-                  #ifdef REDCONF_ENDIAN_SWAP\r
-                    if(ret == 0)\r
-                    {\r
-                        BufferEndianSwap(gBufCtx.b.aabBuffer[bIdx], uFlags);\r
-                    }\r
-                  #endif\r
-                }\r
-                else\r
-                {\r
-                    RedMemSet(gBufCtx.b.aabBuffer[bIdx], 0U, REDCONF_BLOCK_SIZE);\r
-                }\r
-            }\r
-\r
-            if(ret == 0)\r
-            {\r
-                pHead->bVolNum = gbRedVolNum;\r
-                pHead->ulBlock = ulBlock;\r
-                pHead->uFlags = 0U;\r
-            }\r
-        }\r
-\r
-        /*  Reference the buffer, update its flags, and promote it to MRU.  This\r
-            happens both when BufferFind() found an existing buffer for the\r
-            block and when the LRU buffer was repurposed to create a buffer for\r
-            the block.\r
-        */\r
-        if(ret == 0)\r
-        {\r
-            BUFFERHEAD *pHead = &gBufCtx.aHead[bIdx];\r
-\r
-            pHead->bRefCount++;\r
-\r
-            if(pHead->bRefCount == 1U)\r
-            {\r
-                gBufCtx.uNumUsed++;\r
-            }\r
-\r
-            /*  BFLAG_NEW tells this function to zero the buffer instead of\r
-                reading it from disk; it has no meaning later on, and thus is\r
-                not saved.\r
-            */\r
-            pHead->uFlags |= (uFlags & (~BFLAG_NEW));\r
-\r
-            BufferMakeMRU(bIdx);\r
-\r
-            *ppBuffer = gBufCtx.b.aabBuffer[bIdx];\r
-        }\r
-    }\r
-\r
-    return ret;\r
-}\r
-\r
-\r
-/** @brief Release a buffer.\r
-\r
-    @param pBuffer  The buffer to release.\r
- */\r
-void RedBufferPut(\r
-    const void *pBuffer)\r
-{\r
-    uint8_t     bIdx;\r
-\r
-    if(!BufferToIdx(pBuffer, &bIdx))\r
-    {\r
-        REDERROR();\r
-    }\r
-    else\r
-    {\r
-        REDASSERT(gBufCtx.aHead[bIdx].bRefCount > 0U);\r
-        gBufCtx.aHead[bIdx].bRefCount--;\r
-\r
-        if(gBufCtx.aHead[bIdx].bRefCount == 0U)\r
-        {\r
-            REDASSERT(gBufCtx.uNumUsed > 0U);\r
-            gBufCtx.uNumUsed--;\r
-        }\r
-    }\r
-}\r
-\r
-\r
-#if REDCONF_READ_ONLY == 0\r
-/** @brief Flush all buffers for the active volume in the given range of blocks.\r
-\r
-    @param ulBlockStart Starting block number to flush.\r
-    @param ulBlockCount Count of blocks, starting at @p ulBlockStart, to flush.\r
-                        Must not be zero.\r
-\r
-    @return A negated ::REDSTATUS code indicating the operation result.\r
-\r
-    @retval 0           Operation was successful.\r
-    @retval -RED_EIO    A disk I/O error occurred.\r
-    @retval -RED_EINVAL Invalid parameters.\r
-*/\r
-REDSTATUS RedBufferFlush(\r
-    uint32_t    ulBlockStart,\r
-    uint32_t    ulBlockCount)\r
-{\r
-    REDSTATUS   ret = 0;\r
-\r
-    if(    (ulBlockStart >= gpRedVolume->ulBlockCount)\r
-        || ((gpRedVolume->ulBlockCount - ulBlockStart) < ulBlockCount)\r
-        || (ulBlockCount == 0U))\r
-    {\r
-        REDERROR();\r
-        ret = -RED_EINVAL;\r
-    }\r
-    else\r
-    {\r
-        uint8_t bIdx;\r
-\r
-        for(bIdx = 0U; bIdx < REDCONF_BUFFER_COUNT; bIdx++)\r
-        {\r
-            BUFFERHEAD *pHead = &gBufCtx.aHead[bIdx];\r
-\r
-            if(    (pHead->bVolNum == gbRedVolNum)\r
-                && (pHead->ulBlock != BBLK_INVALID)\r
-                && ((pHead->uFlags & BFLAG_DIRTY) != 0U)\r
-                && (pHead->ulBlock >= ulBlockStart)\r
-                && (pHead->ulBlock < (ulBlockStart + ulBlockCount)))\r
-            {\r
-                ret = BufferWrite(bIdx);\r
-\r
-                if(ret == 0)\r
-                {\r
-                    pHead->uFlags &= (~BFLAG_DIRTY);\r
-                }\r
-                else\r
-                {\r
-                    break;\r
-                }\r
-            }\r
-        }\r
-    }\r
-\r
-    return ret;\r
-}\r
-\r
-\r
-/** @brief Mark a buffer dirty\r
-\r
-    @param pBuffer  The buffer to mark dirty.\r
-*/\r
-void RedBufferDirty(\r
-    const void *pBuffer)\r
-{\r
-    uint8_t     bIdx;\r
-\r
-    if(!BufferToIdx(pBuffer, &bIdx))\r
-    {\r
-        REDERROR();\r
-    }\r
-    else\r
-    {\r
-        REDASSERT(gBufCtx.aHead[bIdx].bRefCount > 0U);\r
-\r
-        gBufCtx.aHead[bIdx].uFlags |= BFLAG_DIRTY;\r
-    }\r
-}\r
-\r
-\r
-/** @brief Branch a buffer, marking it dirty and assigning a new block number.\r
-\r
-    @param pBuffer      The buffer to branch.\r
-    @param ulBlockNew   The new block number for the buffer.\r
-*/\r
-void RedBufferBranch(\r
-    const void *pBuffer,\r
-    uint32_t    ulBlockNew)\r
-{\r
-    uint8_t     bIdx;\r
-\r
-    if(    !BufferToIdx(pBuffer, &bIdx)\r
-        || (ulBlockNew >= gpRedVolume->ulBlockCount))\r
-    {\r
-        REDERROR();\r
-    }\r
-    else\r
-    {\r
-        BUFFERHEAD *pHead = &gBufCtx.aHead[bIdx];\r
-\r
-        REDASSERT(pHead->bRefCount > 0U);\r
-        REDASSERT((pHead->uFlags & BFLAG_DIRTY) == 0U);\r
-\r
-        pHead->uFlags |= BFLAG_DIRTY;\r
-        pHead->ulBlock = ulBlockNew;\r
-    }\r
-}\r
-\r
-\r
-#if (REDCONF_API_POSIX == 1) || FORMAT_SUPPORTED\r
-/** @brief Discard a buffer, releasing it and marking it invalid.\r
-\r
-    @param pBuffer  The buffer to discard.\r
-*/\r
-void RedBufferDiscard(\r
-    const void *pBuffer)\r
-{\r
-    uint8_t     bIdx;\r
-\r
-    if(!BufferToIdx(pBuffer, &bIdx))\r
-    {\r
-        REDERROR();\r
-    }\r
-    else\r
-    {\r
-        REDASSERT(gBufCtx.aHead[bIdx].bRefCount == 1U);\r
-        REDASSERT(gBufCtx.uNumUsed > 0U);\r
-\r
-        gBufCtx.aHead[bIdx].bRefCount = 0U;\r
-        gBufCtx.aHead[bIdx].ulBlock = BBLK_INVALID;\r
-\r
-        gBufCtx.uNumUsed--;\r
-\r
-        BufferMakeLRU(bIdx);\r
-    }\r
-}\r
-#endif\r
-#endif /* REDCONF_READ_ONLY == 0 */\r
-\r
-\r
-/** @brief Discard a range of buffers, marking them invalid.\r
-\r
-    @param ulBlockStart The starting block number to discard\r
-    @param ulBlockCount The number of blocks, starting at @p ulBlockStart, to\r
-                        discard.  Must not be zero.\r
-\r
-    @return A negated ::REDSTATUS code indicating the operation result.\r
-\r
-    @retval 0           Operation was successful.\r
-    @retval -RED_EINVAL Invalid parameters.\r
-    @retval -RED_EBUSY  A block in the desired range is referenced.\r
-*/\r
-REDSTATUS RedBufferDiscardRange(\r
-    uint32_t    ulBlockStart,\r
-    uint32_t    ulBlockCount)\r
-{\r
-    REDSTATUS   ret = 0;\r
-\r
-    if(    (ulBlockStart >= gpRedVolume->ulBlockCount)\r
-        || ((gpRedVolume->ulBlockCount - ulBlockStart) < ulBlockCount)\r
-        || (ulBlockCount == 0U))\r
-    {\r
-        REDERROR();\r
-        ret = -RED_EINVAL;\r
-    }\r
-    else\r
-    {\r
-        uint8_t bIdx;\r
-\r
-        for(bIdx = 0U; bIdx < REDCONF_BUFFER_COUNT; bIdx++)\r
-        {\r
-            BUFFERHEAD *pHead = &gBufCtx.aHead[bIdx];\r
-\r
-            if(    (pHead->bVolNum == gbRedVolNum)\r
-                && (pHead->ulBlock != BBLK_INVALID)\r
-                && (pHead->ulBlock >= ulBlockStart)\r
-                && (pHead->ulBlock < (ulBlockStart + ulBlockCount)))\r
-            {\r
-                if(pHead->bRefCount == 0U)\r
-                {\r
-                    pHead->ulBlock = BBLK_INVALID;\r
-\r
-                    BufferMakeLRU(bIdx);\r
-                }\r
-                else\r
-                {\r
-                    /*  This should never happen.  There are three general cases\r
-                        when this function is used:\r
-\r
-                        1) Discarding every block, as happens during unmount\r
-                           and at the end of format.  There should no longer be\r
-                           any referenced buffers at those points.\r
-                        2) Discarding a block which has become free.  All\r
-                           buffers for such blocks should be put or branched\r
-                           beforehand.\r
-                        3) Discarding of blocks that were just written straight\r
-                           to disk, leaving stale data in the buffer.  The write\r
-                           code should never reference buffers for these blocks,\r
-                           since they would not be needed or used.\r
-                    */\r
-                    CRITICAL_ERROR();\r
-                    ret = -RED_EBUSY;\r
-                    break;\r
-                }\r
-            }\r
-        }\r
-    }\r
-\r
-    return ret;\r
-}\r
-\r
-\r
-/** Determine whether a metadata buffer is valid.\r
-\r
-    This includes checking its signature, CRC, and sequence number.\r
-\r
-    @param pbBuffer Pointer to the metadata buffer to validate.\r
-    @param uFlags   The buffer flags provided by the caller.  Used to determine\r
-                    the expected signature.\r
-\r
-    @return Whether the metadata buffer is valid.\r
-\r
-    @retval true    The metadata buffer is valid.\r
-    @retval false   The metadata buffer is invalid.\r
-*/\r
-static bool BufferIsValid(\r
-    const uint8_t  *pbBuffer,\r
-    uint16_t        uFlags)\r
-{\r
-    bool            fValid;\r
-\r
-    if((pbBuffer == NULL) || ((uFlags & BFLAG_MASK) != uFlags))\r
-    {\r
-        REDERROR();\r
-        fValid = false;\r
-    }\r
-    else\r
-    {\r
-        NODEHEADER  buf;\r
-        uint16_t    uMetaFlags = uFlags & BFLAG_META_MASK;\r
-\r
-        /*  Casting pbBuffer to (NODEHEADER *) would run afoul MISRA-C:2012\r
-            R11.3, so instead copy the fields out.\r
-        */\r
-        RedMemCpy(&buf.ulSignature, &pbBuffer[NODEHEADER_OFFSET_SIG], sizeof(buf.ulSignature));\r
-        RedMemCpy(&buf.ulCRC,       &pbBuffer[NODEHEADER_OFFSET_CRC], sizeof(buf.ulCRC));\r
-        RedMemCpy(&buf.ullSequence, &pbBuffer[NODEHEADER_OFFSET_SEQ], sizeof(buf.ullSequence));\r
-\r
-      #ifdef REDCONF_ENDIAN_SWAP\r
-        buf.ulCRC = RedRev32(buf.ulCRC);\r
-        buf.ulSignature = RedRev32(buf.ulSignature);\r
-        buf.ullSequence = RedRev64(buf.ullSequence);\r
-      #endif\r
-\r
-        /*  Make sure the signature is correct for the type of metadata block\r
-            requested by the caller.\r
-        */\r
-        switch(buf.ulSignature)\r
-        {\r
-            case META_SIG_MASTER:\r
-                fValid = (uMetaFlags == BFLAG_META_MASTER);\r
-                break;\r
-          #if REDCONF_IMAP_EXTERNAL == 1\r
-            case META_SIG_IMAP:\r
-                fValid = (uMetaFlags == BFLAG_META_IMAP);\r
-                break;\r
-          #endif\r
-            case META_SIG_INODE:\r
-                fValid = (uMetaFlags == BFLAG_META_INODE);\r
-                break;\r
-          #if DINDIR_POINTERS > 0U\r
-            case META_SIG_DINDIR:\r
-                fValid = (uMetaFlags == BFLAG_META_DINDIR);\r
-                break;\r
-          #endif\r
-          #if REDCONF_DIRECT_POINTERS < INODE_ENTRIES\r
-            case META_SIG_INDIR:\r
-                fValid = (uMetaFlags == BFLAG_META_INDIR);\r
-                break;\r
-          #endif\r
-            default:\r
-                fValid = false;\r
-                break;\r
-        }\r
-\r
-        if(fValid)\r
-        {\r
-            uint32_t ulComputedCrc;\r
-\r
-            /*  Check for disk corruption by comparing the stored CRC with one\r
-                computed from the data.\r
-\r
-                Also check the sequence number: if it is greater than the\r
-                current sequence number, the block is from a previous format\r
-                or the disk is writing blocks out of order.  During mount,\r
-                before the metaroots have been read, the sequence number will\r
-                be unknown, and the check is skipped.\r
-            */\r
-            ulComputedCrc = RedCrcNode(pbBuffer);\r
-            if(buf.ulCRC != ulComputedCrc)\r
-            {\r
-                fValid = false;\r
-            }\r
-            else if(gpRedVolume->fMounted && (buf.ullSequence >= gpRedVolume->ullSequence))\r
-            {\r
-                fValid = false;\r
-            }\r
-            else\r
-            {\r
-                /*  Buffer is valid.  No action, fValid is already true.\r
-                */\r
-            }\r
-        }\r
-    }\r
-\r
-    return fValid;\r
-}\r
-\r
-\r
-/** @brief Derive the index of the buffer.\r
-\r
-    @param pBuffer  The buffer to derive the index of.\r
-    @param pbIdx    On success, populated with the index of the buffer.\r
-\r
-    @return Boolean indicating result.\r
-\r
-    @retval true    Success.\r
-    @retval false   Failure.  @p pBuffer is not a valid buffer pointer.\r
-*/\r
-static bool BufferToIdx(\r
-    const void *pBuffer,\r
-    uint8_t    *pbIdx)\r
-{\r
-    bool        fRet = false;\r
-\r
-    if((pBuffer != NULL) && (pbIdx != NULL))\r
-    {\r
-        uint8_t bIdx;\r
-\r
-        /*  pBuffer should be a pointer to one of the block buffers.\r
-\r
-            A good compiler should optimize this loop into a bounds check and an\r
-            alignment check, although GCC has been observed to not do so; if the\r
-            number of buffers is small, it should not make much difference.  The\r
-            alternative is to use pointer comparisons, but this both deviates\r
-            from MISRA-C:2012 and involves undefined behavior.\r
-        */\r
-        for(bIdx = 0U; bIdx < REDCONF_BUFFER_COUNT; bIdx++)\r
-        {\r
-            if(pBuffer == &gBufCtx.b.aabBuffer[bIdx][0U])\r
-            {\r
-                break;\r
-            }\r
-        }\r
-\r
-        if(    (bIdx < REDCONF_BUFFER_COUNT)\r
-            && (gBufCtx.aHead[bIdx].ulBlock != BBLK_INVALID)\r
-            && (gBufCtx.aHead[bIdx].bVolNum == gbRedVolNum))\r
-        {\r
-            *pbIdx = bIdx;\r
-            fRet = true;\r
-        }\r
-    }\r
-\r
-    return fRet;\r
-}\r
-\r
-\r
-#if REDCONF_READ_ONLY == 0\r
-/** @brief Write out a dirty buffer.\r
-\r
-    @param bIdx The index of the buffer to write.\r
-\r
-    @return A negated ::REDSTATUS code indicating the operation result.\r
-\r
-    @retval 0           Operation was successful.\r
-    @retval -RED_EIO    A disk I/O error occurred.\r
-    @retval -RED_EINVAL Invalid parameters.\r
-*/\r
-static REDSTATUS BufferWrite(\r
-    uint8_t     bIdx)\r
-{\r
-    REDSTATUS   ret = 0;\r
-\r
-    if(bIdx < REDCONF_BUFFER_COUNT)\r
-    {\r
-        const BUFFERHEAD *pHead = &gBufCtx.aHead[bIdx];\r
-\r
-        REDASSERT((pHead->uFlags & BFLAG_DIRTY) != 0U);\r
-\r
-        if((pHead->uFlags & BFLAG_META) != 0U)\r
-        {\r
-            ret = BufferFinalize(gBufCtx.b.aabBuffer[bIdx], pHead->uFlags);\r
-        }\r
-\r
-        if(ret == 0)\r
-        {\r
-            ret = RedIoWrite(pHead->bVolNum, pHead->ulBlock, 1U, gBufCtx.b.aabBuffer[bIdx]);\r
-\r
-          #ifdef REDCONF_ENDIAN_SWAP\r
-            BufferEndianSwap(gBufCtx.b.aabBuffer[bIdx], pHead->uFlags);\r
-          #endif\r
-        }\r
-    }\r
-    else\r
-    {\r
-        REDERROR();\r
-        ret = -RED_EINVAL;\r
-    }\r
-\r
-    return ret;\r
-}\r
-\r
-\r
-/** @brief Finalize a metadata buffer.\r
-\r
-    This updates the CRC and the sequence number.  It also sets the signature,\r
-    though this is only truly needed if the buffer is new.\r
-\r
-    @param pbBuffer Pointer to the metadata buffer to finalize.\r
-    @param uFlags   The associated buffer flags.  Used to determine the expected\r
-                    signature.\r
-\r
-    @return A negated ::REDSTATUS code indicating the operation result.\r
-\r
-    @retval 0           Operation was successful.\r
-    @retval -RED_EINVAL Invalid parameter; or maximum sequence number reached.\r
-*/\r
-static REDSTATUS BufferFinalize(\r
-    uint8_t    *pbBuffer,\r
-    uint16_t    uFlags)\r
-{\r
-    REDSTATUS   ret = 0;\r
-\r
-    if((pbBuffer == NULL) || ((uFlags & BFLAG_MASK) != uFlags))\r
-    {\r
-        REDERROR();\r
-        ret = -RED_EINVAL;\r
-    }\r
-    else\r
-    {\r
-        uint32_t ulSignature;\r
-\r
-        switch(uFlags & BFLAG_META_MASK)\r
-        {\r
-            case BFLAG_META_MASTER:\r
-                ulSignature = META_SIG_MASTER;\r
-                break;\r
-          #if REDCONF_IMAP_EXTERNAL == 1\r
-            case BFLAG_META_IMAP:\r
-                ulSignature = META_SIG_IMAP;\r
-                break;\r
-          #endif\r
-            case BFLAG_META_INODE:\r
-                ulSignature = META_SIG_INODE;\r
-                break;\r
-          #if DINDIR_POINTERS > 0U\r
-            case BFLAG_META_DINDIR:\r
-                ulSignature = META_SIG_DINDIR;\r
-                break;\r
-          #endif\r
-          #if REDCONF_DIRECT_POINTERS < INODE_ENTRIES\r
-            case BFLAG_META_INDIR:\r
-                ulSignature = META_SIG_INDIR;\r
-                break;\r
-          #endif\r
-            default:\r
-                ulSignature = 0U;\r
-                break;\r
-        }\r
-\r
-        if(ulSignature == 0U)\r
-        {\r
-            REDERROR();\r
-            ret = -RED_EINVAL;\r
-        }\r
-        else\r
-        {\r
-            uint64_t ullSeqNum = gpRedVolume->ullSequence;\r
-\r
-            ret = RedVolSeqNumIncrement();\r
-            if(ret == 0)\r
-            {\r
-                uint32_t ulCrc;\r
-\r
-                RedMemCpy(&pbBuffer[NODEHEADER_OFFSET_SIG], &ulSignature, sizeof(ulSignature));\r
-                RedMemCpy(&pbBuffer[NODEHEADER_OFFSET_SEQ], &ullSeqNum, sizeof(ullSeqNum));\r
-\r
-              #ifdef REDCONF_ENDIAN_SWAP\r
-                BufferEndianSwap(pbBuffer, uFlags);\r
-              #endif\r
-\r
-                ulCrc = RedCrcNode(pbBuffer);\r
-              #ifdef REDCONF_ENDIAN_SWAP\r
-                ulCrc = RedRev32(ulCrc);\r
-              #endif\r
-                RedMemCpy(&pbBuffer[NODEHEADER_OFFSET_CRC], &ulCrc, sizeof(ulCrc));\r
-            }\r
-        }\r
-    }\r
-\r
-    return ret;\r
-}\r
-#endif /* REDCONF_READ_ONLY == 0 */\r
-\r
-\r
-#ifdef REDCONF_ENDIAN_SWAP\r
-/** @brief Swap the byte order of a metadata buffer\r
-\r
-    Does nothing if the buffer is not a metadata node.  Also does nothing for\r
-    meta roots, which don't go through the buffers anyways.\r
-\r
-    @param pBuffer  Pointer to the metadata buffer to swap\r
-    @param uFlags   The associated buffer flags.  Used to determin the type of\r
-                    metadata node.\r
-*/\r
-static void BufferEndianSwap(\r
-    void       *pBuffer,\r
-    uint16_t    uFlags)\r
-{\r
-    if((pBuffer == NULL) || ((uFlags & BFLAG_MASK) != uFlags))\r
-    {\r
-        REDERROR();\r
-    }\r
-    else if((uFlags & BFLAG_META_MASK) != 0)\r
-    {\r
-        BufferEndianSwapHeader(pBuffer);\r
-\r
-        switch(uFlags & BFLAG_META_MASK)\r
-        {\r
-            case BFLAG_META_MASTER:\r
-                BufferEndianSwapMaster(pBuffer);\r
-                break;\r
-            case BFLAG_META_INODE:\r
-                BufferEndianSwapInode(pBuffer);\r
-                break;\r
-          #if DINDIR_POINTERS > 0U\r
-            case BFLAG_META_DINDIR:\r
-                BufferEndianSwapIndir(pBuffer);\r
-                break;\r
-          #endif\r
-          #if REDCONF_DIRECT_POINTERS < INODE_ENTRIES\r
-            case BFLAG_META_INDIR:\r
-                BufferEndianSwapIndir(pBuffer);\r
-                break;\r
-          #endif\r
-            default:\r
-                break;\r
-        }\r
-    }\r
-    else\r
-    {\r
-        /*  File data buffers do not need to be swapped.\r
-        */\r
-    }\r
-}\r
-\r
-\r
-/** @brief Swap the byte order of a metadata node header\r
-\r
-    @param pHeader  Pointer to the metadata node header to swap\r
-*/\r
-static void BufferEndianSwapHeader(\r
-    NODEHEADER *pHeader)\r
-{\r
-    if(pHeader == NULL)\r
-    {\r
-        REDERROR();\r
-    }\r
-    else\r
-    {\r
-        pHeader->ulSignature = RedRev32(pHeader->ulSignature);\r
-        pHeader->ulCRC = RedRev32(pHeader->ulCRC);\r
-        pHeader->ullSequence = RedRev64(pHeader->ullSequence);\r
-    }\r
-}\r
-\r
-\r
-/** @brief Swap the byte order of a master block\r
-\r
-    @param pMaster  Pointer to the master block to swap\r
-*/\r
-static void BufferEndianSwapMaster(\r
-    MASTERBLOCK *pMaster)\r
-{\r
-    if(pMaster == NULL)\r
-    {\r
-        REDERROR();\r
-    }\r
-    else\r
-    {\r
-        pMaster->ulVersion = RedRev32(pMaster->ulVersion);\r
-        pMaster->ulFormatTime = RedRev32(pMaster->ulFormatTime);\r
-        pMaster->ulInodeCount = RedRev32(pMaster->ulInodeCount);\r
-        pMaster->ulBlockCount = RedRev32(pMaster->ulBlockCount);\r
-        pMaster->uMaxNameLen = RedRev16(pMaster->uMaxNameLen);\r
-        pMaster->uDirectPointers = RedRev16(pMaster->uDirectPointers);\r
-        pMaster->uIndirectPointers = RedRev16(pMaster->uIndirectPointers);\r
-    }\r
-}\r
-\r
-\r
-/** @brief Swap the byte order of an inode\r
-\r
-    @param pInode   Pointer to the inode to swap\r
-*/\r
-static void BufferEndianSwapInode(\r
-    INODE  *pInode)\r
-{\r
-    if(pInode == NULL)\r
-    {\r
-        REDERROR();\r
-    }\r
-    else\r
-    {\r
-        uint32_t ulIdx;\r
-\r
-        pInode->ullSize = RedRev64(pInode->ullSize);\r
-\r
-      #if REDCONF_INODE_BLOCKS == 1\r
-        pInode->ulBlocks = RedRev32(pInode->ulBlocks);\r
-      #endif\r
-\r
-      #if REDCONF_INODE_TIMESTAMPS == 1\r
-        pInode->ulATime = RedRev32(pInode->ulATime);\r
-        pInode->ulMTime = RedRev32(pInode->ulMTime);\r
-        pInode->ulCTime = RedRev32(pInode->ulCTime);\r
-      #endif\r
-\r
-        pInode->uMode = RedRev16(pInode->uMode);\r
-\r
-      #if (REDCONF_API_POSIX == 1) && (REDCONF_API_POSIX_LINK == 1)\r
-        pInode->uNLink = RedRev16(pInode->uNLink);\r
-      #endif\r
-\r
-      #if REDCONF_API_POSIX == 1\r
-        pInode->ulPInode = RedRev32(pInode->ulPInode);\r
-      #endif\r
-\r
-        for(ulIdx = 0; ulIdx < INODE_ENTRIES; ulIdx++)\r
-        {\r
-            pInode->aulEntries[ulIdx] = RedRev32(pInode->aulEntries[ulIdx]);\r
-        }\r
-    }\r
-}\r
-\r
-\r
-#if REDCONF_DIRECT_POINTERS < INODE_ENTRIES\r
-/** @brief Swap the byte order of an indirect or double indirect node\r
-\r
-    @param pIndir   Pointer to the node to swap\r
-*/\r
-static void BufferEndianSwapIndir(\r
-    INDIR  *pIndir)\r
-{\r
-    if(pIndir == NULL)\r
-    {\r
-        REDERROR();\r
-    }\r
-    else\r
-    {\r
-        uint32_t ulIdx;\r
-\r
-        pIndir->ulInode = RedRev32(pIndir->ulInode);\r
-\r
-        for(ulIdx = 0; ulIdx < INDIR_ENTRIES; ulIdx++)\r
-        {\r
-            pIndir->aulEntries[ulIdx] = RedRev32(pIndir->aulEntries[ulIdx]);\r
-        }\r
-    }\r
-}\r
-\r
-#endif /* REDCONF_DIRECT_POINTERS < INODE_ENTRIES */\r
-#endif /* #ifdef REDCONF_ENDIAN_SWAP */\r
-\r
-\r
-/** @brief Mark a buffer as least recently used.\r
-\r
-    @param bIdx The index of the buffer to make LRU.\r
-*/\r
-static void BufferMakeLRU(\r
-    uint8_t bIdx)\r
-{\r
-    if(bIdx >= REDCONF_BUFFER_COUNT)\r
-    {\r
-        REDERROR();\r
-    }\r
-    else if(bIdx != gBufCtx.abMRU[REDCONF_BUFFER_COUNT - 1U])\r
-    {\r
-        uint8_t bMruIdx;\r
-\r
-        /*  Find the current position of the buffer in the MRU array.  We do not\r
-            need to check the last slot, since we already know from the above\r
-            check that the index is not there.\r
-        */\r
-        for(bMruIdx = 0U; bMruIdx < (REDCONF_BUFFER_COUNT - 1U); bMruIdx++)\r
-        {\r
-            if(bIdx == gBufCtx.abMRU[bMruIdx])\r
-            {\r
-                break;\r
-            }\r
-        }\r
-\r
-        if(bMruIdx < (REDCONF_BUFFER_COUNT - 1U))\r
-        {\r
-            /*  Move the buffer index to the back of the MRU array, making it\r
-                the LRU buffer.\r
-            */\r
-            RedMemMove(&gBufCtx.abMRU[bMruIdx], &gBufCtx.abMRU[bMruIdx + 1U], REDCONF_BUFFER_COUNT - ((uint32_t)bMruIdx + 1U));\r
-            gBufCtx.abMRU[REDCONF_BUFFER_COUNT - 1U] = bIdx;\r
-        }\r
-        else\r
-        {\r
-            REDERROR();\r
-        }\r
-    }\r
-    else\r
-    {\r
-        /*  Buffer already LRU, nothing to do.\r
-        */\r
-    }\r
-}\r
-\r
-\r
-/** @brief Mark a buffer as most recently used.\r
-\r
-    @param bIdx The index of the buffer to make MRU.\r
-*/\r
-static void BufferMakeMRU(\r
-    uint8_t bIdx)\r
-{\r
-    if(bIdx >= REDCONF_BUFFER_COUNT)\r
-    {\r
-        REDERROR();\r
-    }\r
-    else if(bIdx != gBufCtx.abMRU[0U])\r
-    {\r
-        uint8_t bMruIdx;\r
-\r
-        /*  Find the current position of the buffer in the MRU array.  We do not\r
-            need to check the first slot, since we already know from the above\r
-            check that the index is not there.\r
-        */\r
-        for(bMruIdx = 1U; bMruIdx < REDCONF_BUFFER_COUNT; bMruIdx++)\r
-        {\r
-            if(bIdx == gBufCtx.abMRU[bMruIdx])\r
-            {\r
-                break;\r
-            }\r
-        }\r
-\r
-        if(bMruIdx < REDCONF_BUFFER_COUNT)\r
-        {\r
-            /*  Move the buffer index to the front of the MRU array, making it\r
-                the MRU buffer.\r
-            */\r
-            RedMemMove(&gBufCtx.abMRU[1U], &gBufCtx.abMRU[0U], bMruIdx);\r
-            gBufCtx.abMRU[0U] = bIdx;\r
-        }\r
-        else\r
-        {\r
-            REDERROR();\r
-        }\r
-    }\r
-    else\r
-    {\r
-        /*  Buffer already MRU, nothing to do.\r
-        */\r
-    }\r
-}\r
-\r
-\r
-/** @brief Find a block in the buffers.\r
-\r
-    @param ulBlock  The block number to find.\r
-    @param pbIdx    If the block is buffered (true is returned), populated with\r
-                    the index of the buffer.\r
-\r
-    @return Boolean indicating whether or not the block is buffered.\r
-\r
-    @retval true    @p ulBlock is buffered, and its index has been stored in\r
-                    @p pbIdx.\r
-    @retval false   @p ulBlock is not buffered.\r
-*/\r
-static bool BufferFind(\r
-    uint32_t ulBlock,\r
-    uint8_t *pbIdx)\r
-{\r
-    bool     ret = false;\r
-\r
-    if((ulBlock >= gpRedVolume->ulBlockCount) || (pbIdx == NULL))\r
-    {\r
-        REDERROR();\r
-    }\r
-    else\r
-    {\r
-        uint8_t bIdx;\r
-\r
-        for(bIdx = 0U; bIdx < REDCONF_BUFFER_COUNT; bIdx++)\r
-        {\r
-            const BUFFERHEAD *pHead = &gBufCtx.aHead[bIdx];\r
-\r
-            if((pHead->bVolNum == gbRedVolNum) && (pHead->ulBlock == ulBlock))\r
-            {\r
-                *pbIdx = bIdx;\r
-                ret = true;\r
-                break;\r
-            }\r
-        }\r
-    }\r
-\r
-    return ret;\r
-}\r
-\r
+/*             ----> DO NOT REMOVE THE FOLLOWING NOTICE <----
+
+                   Copyright (c) 2014-2015 Datalight, Inc.
+                       All Rights Reserved Worldwide.
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; use version 2 of the License.
+
+    This program is distributed in the hope that it will be useful,
+    but "AS-IS," WITHOUT ANY WARRANTY; without even the implied warranty
+    of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+/*  Businesses and individuals that for commercial or other reasons cannot
+    comply with the terms of the GPLv2 license may obtain a commercial license
+    before incorporating Reliance Edge into proprietary software for
+    distribution in any form.  Visit http://www.datalight.com/reliance-edge for
+    more information.
+*/
+/** @file
+    @brief Implements the block device buffering system.
+
+    This module implements the block buffer cache.  It has a number of block
+    sized buffers which are used to store data from a given block (identified
+    by both block number and volume number: this cache is shared among all
+    volumes).  Block buffers may be either dirty or clean.  Most I/O passes
+    through this module.  When a buffer is needed for a block which is not in
+    the cache, a "victim" is selected via a simple LRU scheme.
+*/
+#include <redfs.h>
+#include <redcore.h>
+
+
+#if DINDIR_POINTERS > 0U
+  #define INODE_META_BUFFERS 3U /* Inode, double indirect, indirect */
+#elif REDCONF_INDIRECT_POINTERS > 0U
+  #define INODE_META_BUFFERS 2U /* Inode, indirect */
+#elif REDCONF_DIRECT_POINTERS == INODE_ENTRIES
+  #define INODE_META_BUFFERS 1U /* Inode only */
+#endif
+
+#define INODE_BUFFERS (INODE_META_BUFFERS + 1U) /* Add data buffer */
+
+#if REDCONF_IMAP_EXTERNAL == 1
+  #define IMAP_BUFFERS 1U
+#else
+  #define IMAP_BUFFERS 0U
+#endif
+
+#if (REDCONF_READ_ONLY == 1) || (REDCONF_API_FSE == 1)
+  /*  Read, write, truncate, lookup: One inode all the way down, plus imap.
+  */
+  #define MINIMUM_BUFFER_COUNT (INODE_BUFFERS + IMAP_BUFFERS)
+#elif REDCONF_API_POSIX == 1
+  #if REDCONF_API_POSIX_RENAME == 1
+    #if REDCONF_RENAME_ATOMIC == 1
+      /*  Two parent directories all the way down.  Source and destination inode
+          buffer.  One inode buffer for cyclic rename detection.  Imap.  The
+          parent inode buffers are released before deleting the destination
+          inode, so that does not increase the minimum.
+      */
+      #define MINIMUM_BUFFER_COUNT (INODE_BUFFERS + INODE_BUFFERS + 3U + IMAP_BUFFERS)
+    #else
+      /*  Two parent directories all the way down.  Source inode buffer.  One
+          inode buffer for cyclic rename detection.  Imap.
+      */
+      #define MINIMUM_BUFFER_COUNT (INODE_BUFFERS + INODE_BUFFERS + 2U + IMAP_BUFFERS)
+    #endif
+  #else
+    /*  Link/create: Needs a parent inode all the way down, an extra inode
+        buffer, and an imap buffer.
+
+        Unlink is the same, since the parent inode buffers are released before
+        the inode is deleted.
+    */
+    #define MINIMUM_BUFFER_COUNT (INODE_BUFFERS + 1U + IMAP_BUFFERS)
+  #endif
+#endif
+
+#if REDCONF_BUFFER_COUNT < MINIMUM_BUFFER_COUNT
+#error "REDCONF_BUFFER_COUNT is too low for the configuration"
+#endif
+
+
+/*  A note on the typecasts in the below macros: Operands to bitwise operators
+    are subject to the "usual arithmetic conversions".  This means that the
+    flags, which have uint16_t values, are promoted to int.  MISRA-C:2012 R10.1
+    forbids using signed integers in bitwise operations, so we cast to uint32_t
+    to avoid the integer promotion, then back to uint16_t to reflect the actual
+    type.
+*/
+#define BFLAG_META_MASK (uint16_t)((uint32_t)BFLAG_META_MASTER | BFLAG_META_IMAP | BFLAG_META_INODE | BFLAG_META_INDIR | BFLAG_META_DINDIR)
+#define BFLAG_MASK (uint16_t)((uint32_t)BFLAG_DIRTY | BFLAG_NEW | BFLAG_META_MASK)
+
+
+/*  An invalid block number.  Used to indicate buffers which are not currently
+    in use.
+*/
+#define BBLK_INVALID UINT32_MAX
+
+
+/** @brief Metadata stored for each block buffer.
+
+    To make better use of CPU caching when searching the BUFFERHEAD array, this
+    structure should be kept small.
+*/
+typedef struct
+{
+    uint32_t    ulBlock;    /**< Block number the buffer is associated with; BBLK_INVALID if unused. */
+    uint8_t     bVolNum;    /**< Volume the block resides on. */
+    uint8_t     bRefCount;  /**< Number of references. */
+    uint16_t    uFlags;     /**< Buffer flags: mask of BFLAG_* values. */
+} BUFFERHEAD;
+
+
+/** @brief State information for the block buffer module.
+*/
+typedef struct
+{
+    /** Number of buffers which are referenced (have a bRefCount > 0).
+    */
+    uint16_t    uNumUsed;
+
+    /** MRU array.  Each element of the array stores a buffer index; each buffer
+        index appears in the array once and only once.  The first element of the
+        array is the most-recently-used (MRU) buffer, followed by the next most
+        recently used, and so on, till the last element, which is the least-
+        recently-used (LRU) buffer.
+    */
+    uint8_t     abMRU[REDCONF_BUFFER_COUNT];
+
+    /** Buffer heads, storing metadata for each buffer.
+    */
+    BUFFERHEAD  aHead[REDCONF_BUFFER_COUNT];
+
+    /** Array of memory for the block buffers themselves.
+
+        Force 64-bit alignment of the aabBuffer array to ensure that it is safe
+        to cast buffer pointers to node structure pointers.
+    */
+    ALIGNED_2D_BYTE_ARRAY(b, aabBuffer, REDCONF_BUFFER_COUNT, REDCONF_BLOCK_SIZE);
+} BUFFERCTX;
+
+
+static bool BufferIsValid(const uint8_t  *pbBuffer, uint16_t uFlags);
+static bool BufferToIdx(const void *pBuffer, uint8_t *pbIdx);
+#if REDCONF_READ_ONLY == 0
+static REDSTATUS BufferWrite(uint8_t bIdx);
+static REDSTATUS BufferFinalize(uint8_t *pbBuffer, uint16_t uFlags);
+#endif
+static void BufferMakeLRU(uint8_t bIdx);
+static void BufferMakeMRU(uint8_t bIdx);
+static bool BufferFind(uint32_t ulBlock, uint8_t *pbIdx);
+
+#ifdef REDCONF_ENDIAN_SWAP
+static void BufferEndianSwap(const void *pBuffer, uint16_t uFlags);
+static void BufferEndianSwapHeader(NODEHEADER *pHeader);
+static void BufferEndianSwapMaster(MASTERBLOCK *pMaster);
+static void BufferEndianSwapMetaRoot(METAROOT *pMetaRoot);
+static void BufferEndianSwapInode(INODE *pInode);
+static void BufferEndianSwapIndir(INDIR *pIndir);
+#endif
+
+
+static BUFFERCTX gBufCtx;
+
+
+/** @brief Initialize the buffers.
+*/
+void RedBufferInit(void)
+{
+    uint8_t bIdx;
+
+    RedMemSet(&gBufCtx, 0U, sizeof(gBufCtx));
+
+    for(bIdx = 0U; bIdx < REDCONF_BUFFER_COUNT; bIdx++)
+    {
+        /*  When the buffers have been freshly initialized, acquire the buffers
+            in the order in which they appear in the array.
+        */
+        gBufCtx.abMRU[bIdx] = (uint8_t)((REDCONF_BUFFER_COUNT - bIdx) - 1U);
+        gBufCtx.aHead[bIdx].ulBlock = BBLK_INVALID;
+    }
+}
+
+
+/** @brief Acquire a buffer.
+
+    @param ulBlock  Block number to acquire.
+    @param uFlags   BFLAG_ values for the operation.
+    @param ppBuffer On success, populated with the acquired buffer.
+
+    @return A negated ::REDSTATUS code indicating the operation result.
+
+    @retval 0           Operation was successful.
+    @retval -RED_EIO    A disk I/O error occurred.
+    @retval -RED_EINVAL Invalid parameters.
+    @retval -RED_EBUSY  All buffers are referenced.
+*/
+REDSTATUS RedBufferGet(
+    uint32_t    ulBlock,
+    uint16_t    uFlags,
+    void      **ppBuffer)
+{
+    REDSTATUS   ret = 0;
+    uint8_t     bIdx;
+
+    if((ulBlock >= gpRedVolume->ulBlockCount) || ((uFlags & BFLAG_MASK) != uFlags) || (ppBuffer == NULL))
+    {
+        REDERROR();
+        ret = -RED_EINVAL;
+    }
+    else
+    {
+        if(BufferFind(ulBlock, &bIdx))
+        {
+            /*  Error if the buffer exists and BFLAG_NEW was specified, since
+                the new flag is used when a block is newly allocated/created, so
+                the block was previously free and and there should never be an
+                existing buffer for a free block.
+
+                Error if the buffer exists but does not have the same type as
+                was requested.
+            */
+            if(    ((uFlags & BFLAG_NEW) != 0U)
+                || ((uFlags & BFLAG_META_MASK) != (gBufCtx.aHead[bIdx].uFlags & BFLAG_META_MASK)))
+            {
+                CRITICAL_ERROR();
+                ret = -RED_EFUBAR;
+            }
+        }
+        else if(gBufCtx.uNumUsed == REDCONF_BUFFER_COUNT)
+        {
+            /*  The MINIMUM_BUFFER_COUNT is supposed to ensure that no operation
+                ever runs out of buffers, so this should never happen.
+            */
+            CRITICAL_ERROR();
+            ret = -RED_EBUSY;
+        }
+        else
+        {
+            BUFFERHEAD *pHead;
+
+            /*  Search for the least recently used buffer which is not
+                referenced.
+            */
+            for(bIdx = (uint8_t)(REDCONF_BUFFER_COUNT - 1U); bIdx > 0U; bIdx--)
+            {
+                if(gBufCtx.aHead[gBufCtx.abMRU[bIdx]].bRefCount == 0U)
+                {
+                    break;
+                }
+            }
+
+            bIdx = gBufCtx.abMRU[bIdx];
+            pHead = &gBufCtx.aHead[bIdx];
+
+            if(pHead->bRefCount == 0U)
+            {
+                /*  If the LRU buffer is valid and dirty, write it out before
+                    repurposing it.
+                */
+                if(((pHead->uFlags & BFLAG_DIRTY) != 0U) && (pHead->ulBlock != BBLK_INVALID))
+                {
+                  #if REDCONF_READ_ONLY == 1
+                    CRITICAL_ERROR();
+                    ret = -RED_EFUBAR;
+                  #else
+                    ret = BufferWrite(bIdx);
+                  #endif
+                }
+            }
+            else
+            {
+                /*  All the buffers are used, which should have been caught by
+                    checking gBufCtx.uNumUsed.
+                */
+                CRITICAL_ERROR();
+                ret = -RED_EBUSY;
+            }
+
+            if(ret == 0)
+            {
+                if((uFlags & BFLAG_NEW) == 0U)
+                {
+                    /*  Invalidate the LRU buffer.  If the read fails, we do not
+                        want the buffer head to continue to refer to the old
+                        block number, since the read, even if it fails, may have
+                        partially overwritten the buffer data (consider the case
+                        where block size exceeds sector size, and some but not
+                        all of the sectors are read successfully), and if the
+                        buffer were to be used subsequently with its partially
+                        erroneous contents, bad things could happen.
+                    */
+                    pHead->ulBlock = BBLK_INVALID;
+
+                    ret = RedIoRead(gbRedVolNum, ulBlock, 1U, gBufCtx.b.aabBuffer[bIdx]);
+
+                    if((ret == 0) && ((uFlags & BFLAG_META) != 0U))
+                    {
+                        if(!BufferIsValid(gBufCtx.b.aabBuffer[bIdx], uFlags))
+                        {
+                            /*  A corrupt metadata node is usually a critical
+                                error.  The master block is an exception since
+                                it might be invalid because the volume is not
+                                mounted; that condition is expected and should
+                                not result in an assertion.
+                            */
+                            CRITICAL_ASSERT((uFlags & BFLAG_META_MASTER) == BFLAG_META_MASTER);
+                            ret = -RED_EIO;
+                        }
+                    }
+
+                  #ifdef REDCONF_ENDIAN_SWAP
+                    if(ret == 0)
+                    {
+                        BufferEndianSwap(gBufCtx.b.aabBuffer[bIdx], uFlags);
+                    }
+                  #endif
+                }
+                else
+                {
+                    RedMemSet(gBufCtx.b.aabBuffer[bIdx], 0U, REDCONF_BLOCK_SIZE);
+                }
+            }
+
+            if(ret == 0)
+            {
+                pHead->bVolNum = gbRedVolNum;
+                pHead->ulBlock = ulBlock;
+                pHead->uFlags = 0U;
+            }
+        }
+
+        /*  Reference the buffer, update its flags, and promote it to MRU.  This
+            happens both when BufferFind() found an existing buffer for the
+            block and when the LRU buffer was repurposed to create a buffer for
+            the block.
+        */
+        if(ret == 0)
+        {
+            BUFFERHEAD *pHead = &gBufCtx.aHead[bIdx];
+
+            pHead->bRefCount++;
+
+            if(pHead->bRefCount == 1U)
+            {
+                gBufCtx.uNumUsed++;
+            }
+
+            /*  BFLAG_NEW tells this function to zero the buffer instead of
+                reading it from disk; it has no meaning later on, and thus is
+                not saved.
+            */
+            pHead->uFlags |= (uFlags & (~BFLAG_NEW));
+
+            BufferMakeMRU(bIdx);
+
+            *ppBuffer = gBufCtx.b.aabBuffer[bIdx];
+        }
+    }
+
+    return ret;
+}
+
+
+/** @brief Release a buffer.
+
+    @param pBuffer  The buffer to release.
+ */
+void RedBufferPut(
+    const void *pBuffer)
+{
+    uint8_t     bIdx;
+
+    if(!BufferToIdx(pBuffer, &bIdx))
+    {
+        REDERROR();
+    }
+    else
+    {
+        REDASSERT(gBufCtx.aHead[bIdx].bRefCount > 0U);
+        gBufCtx.aHead[bIdx].bRefCount--;
+
+        if(gBufCtx.aHead[bIdx].bRefCount == 0U)
+        {
+            REDASSERT(gBufCtx.uNumUsed > 0U);
+            gBufCtx.uNumUsed--;
+        }
+    }
+}
+
+
+#if REDCONF_READ_ONLY == 0
+/** @brief Flush all buffers for the active volume in the given range of blocks.
+
+    @param ulBlockStart Starting block number to flush.
+    @param ulBlockCount Count of blocks, starting at @p ulBlockStart, to flush.
+                        Must not be zero.
+
+    @return A negated ::REDSTATUS code indicating the operation result.
+
+    @retval 0           Operation was successful.
+    @retval -RED_EIO    A disk I/O error occurred.
+    @retval -RED_EINVAL Invalid parameters.
+*/
+REDSTATUS RedBufferFlush(
+    uint32_t    ulBlockStart,
+    uint32_t    ulBlockCount)
+{
+    REDSTATUS   ret = 0;
+
+    if(    (ulBlockStart >= gpRedVolume->ulBlockCount)
+        || ((gpRedVolume->ulBlockCount - ulBlockStart) < ulBlockCount)
+        || (ulBlockCount == 0U))
+    {
+        REDERROR();
+        ret = -RED_EINVAL;
+    }
+    else
+    {
+        uint8_t bIdx;
+
+        for(bIdx = 0U; bIdx < REDCONF_BUFFER_COUNT; bIdx++)
+        {
+            BUFFERHEAD *pHead = &gBufCtx.aHead[bIdx];
+
+            if(    (pHead->bVolNum == gbRedVolNum)
+                && (pHead->ulBlock != BBLK_INVALID)
+                && ((pHead->uFlags & BFLAG_DIRTY) != 0U)
+                && (pHead->ulBlock >= ulBlockStart)
+                && (pHead->ulBlock < (ulBlockStart + ulBlockCount)))
+            {
+                ret = BufferWrite(bIdx);
+
+                if(ret == 0)
+                {
+                    pHead->uFlags &= (~BFLAG_DIRTY);
+                }
+                else
+                {
+                    break;
+                }
+            }
+        }
+    }
+
+    return ret;
+}
+
+
+/** @brief Mark a buffer dirty
+
+    @param pBuffer  The buffer to mark dirty.
+*/
+void RedBufferDirty(
+    const void *pBuffer)
+{
+    uint8_t     bIdx;
+
+    if(!BufferToIdx(pBuffer, &bIdx))
+    {
+        REDERROR();
+    }
+    else
+    {
+        REDASSERT(gBufCtx.aHead[bIdx].bRefCount > 0U);
+
+        gBufCtx.aHead[bIdx].uFlags |= BFLAG_DIRTY;
+    }
+}
+
+
+/** @brief Branch a buffer, marking it dirty and assigning a new block number.
+
+    @param pBuffer      The buffer to branch.
+    @param ulBlockNew   The new block number for the buffer.
+*/
+void RedBufferBranch(
+    const void *pBuffer,
+    uint32_t    ulBlockNew)
+{
+    uint8_t     bIdx;
+
+    if(    !BufferToIdx(pBuffer, &bIdx)
+        || (ulBlockNew >= gpRedVolume->ulBlockCount))
+    {
+        REDERROR();
+    }
+    else
+    {
+        BUFFERHEAD *pHead = &gBufCtx.aHead[bIdx];
+
+        REDASSERT(pHead->bRefCount > 0U);
+        REDASSERT((pHead->uFlags & BFLAG_DIRTY) == 0U);
+
+        pHead->uFlags |= BFLAG_DIRTY;
+        pHead->ulBlock = ulBlockNew;
+    }
+}
+
+
+#if (REDCONF_API_POSIX == 1) || FORMAT_SUPPORTED
+/** @brief Discard a buffer, releasing it and marking it invalid.
+
+    @param pBuffer  The buffer to discard.
+*/
+void RedBufferDiscard(
+    const void *pBuffer)
+{
+    uint8_t     bIdx;
+
+    if(!BufferToIdx(pBuffer, &bIdx))
+    {
+        REDERROR();
+    }
+    else
+    {
+        REDASSERT(gBufCtx.aHead[bIdx].bRefCount == 1U);
+        REDASSERT(gBufCtx.uNumUsed > 0U);
+
+        gBufCtx.aHead[bIdx].bRefCount = 0U;
+        gBufCtx.aHead[bIdx].ulBlock = BBLK_INVALID;
+
+        gBufCtx.uNumUsed--;
+
+        BufferMakeLRU(bIdx);
+    }
+}
+#endif
+#endif /* REDCONF_READ_ONLY == 0 */
+
+
+/** @brief Discard a range of buffers, marking them invalid.
+
+    @param ulBlockStart The starting block number to discard
+    @param ulBlockCount The number of blocks, starting at @p ulBlockStart, to
+                        discard.  Must not be zero.
+
+    @return A negated ::REDSTATUS code indicating the operation result.
+
+    @retval 0           Operation was successful.
+    @retval -RED_EINVAL Invalid parameters.
+    @retval -RED_EBUSY  A block in the desired range is referenced.
+*/
+REDSTATUS RedBufferDiscardRange(
+    uint32_t    ulBlockStart,
+    uint32_t    ulBlockCount)
+{
+    REDSTATUS   ret = 0;
+
+    if(    (ulBlockStart >= gpRedVolume->ulBlockCount)
+        || ((gpRedVolume->ulBlockCount - ulBlockStart) < ulBlockCount)
+        || (ulBlockCount == 0U))
+    {
+        REDERROR();
+        ret = -RED_EINVAL;
+    }
+    else
+    {
+        uint8_t bIdx;
+
+        for(bIdx = 0U; bIdx < REDCONF_BUFFER_COUNT; bIdx++)
+        {
+            BUFFERHEAD *pHead = &gBufCtx.aHead[bIdx];
+
+            if(    (pHead->bVolNum == gbRedVolNum)
+                && (pHead->ulBlock != BBLK_INVALID)
+                && (pHead->ulBlock >= ulBlockStart)
+                && (pHead->ulBlock < (ulBlockStart + ulBlockCount)))
+            {
+                if(pHead->bRefCount == 0U)
+                {
+                    pHead->ulBlock = BBLK_INVALID;
+
+                    BufferMakeLRU(bIdx);
+                }
+                else
+                {
+                    /*  This should never happen.  There are three general cases
+                        when this function is used:
+
+                        1) Discarding every block, as happens during unmount
+                           and at the end of format.  There should no longer be
+                           any referenced buffers at those points.
+                        2) Discarding a block which has become free.  All
+                           buffers for such blocks should be put or branched
+                           beforehand.
+                        3) Discarding of blocks that were just written straight
+                           to disk, leaving stale data in the buffer.  The write
+                           code should never reference buffers for these blocks,
+                           since they would not be needed or used.
+                    */
+                    CRITICAL_ERROR();
+                    ret = -RED_EBUSY;
+                    break;
+                }
+            }
+        }
+    }
+
+    return ret;
+}
+
+
+/** Determine whether a metadata buffer is valid.
+
+    This includes checking its signature, CRC, and sequence number.
+
+    @param pbBuffer Pointer to the metadata buffer to validate.
+    @param uFlags   The buffer flags provided by the caller.  Used to determine
+                    the expected signature.
+
+    @return Whether the metadata buffer is valid.
+
+    @retval true    The metadata buffer is valid.
+    @retval false   The metadata buffer is invalid.
+*/
+static bool BufferIsValid(
+    const uint8_t  *pbBuffer,
+    uint16_t        uFlags)
+{
+    bool            fValid;
+
+    if((pbBuffer == NULL) || ((uFlags & BFLAG_MASK) != uFlags))
+    {
+        REDERROR();
+        fValid = false;
+    }
+    else
+    {
+        NODEHEADER  buf;
+        uint16_t    uMetaFlags = uFlags & BFLAG_META_MASK;
+
+        /*  Casting pbBuffer to (NODEHEADER *) would run afoul MISRA-C:2012
+            R11.3, so instead copy the fields out.
+        */
+        RedMemCpy(&buf.ulSignature, &pbBuffer[NODEHEADER_OFFSET_SIG], sizeof(buf.ulSignature));
+        RedMemCpy(&buf.ulCRC,       &pbBuffer[NODEHEADER_OFFSET_CRC], sizeof(buf.ulCRC));
+        RedMemCpy(&buf.ullSequence, &pbBuffer[NODEHEADER_OFFSET_SEQ], sizeof(buf.ullSequence));
+
+      #ifdef REDCONF_ENDIAN_SWAP
+        buf.ulCRC = RedRev32(buf.ulCRC);
+        buf.ulSignature = RedRev32(buf.ulSignature);
+        buf.ullSequence = RedRev64(buf.ullSequence);
+      #endif
+
+        /*  Make sure the signature is correct for the type of metadata block
+            requested by the caller.
+        */
+        switch(buf.ulSignature)
+        {
+            case META_SIG_MASTER:
+                fValid = (uMetaFlags == BFLAG_META_MASTER);
+                break;
+          #if REDCONF_IMAP_EXTERNAL == 1
+            case META_SIG_IMAP:
+                fValid = (uMetaFlags == BFLAG_META_IMAP);
+                break;
+          #endif
+            case META_SIG_INODE:
+                fValid = (uMetaFlags == BFLAG_META_INODE);
+                break;
+          #if DINDIR_POINTERS > 0U
+            case META_SIG_DINDIR:
+                fValid = (uMetaFlags == BFLAG_META_DINDIR);
+                break;
+          #endif
+          #if REDCONF_DIRECT_POINTERS < INODE_ENTRIES
+            case META_SIG_INDIR:
+                fValid = (uMetaFlags == BFLAG_META_INDIR);
+                break;
+          #endif
+            default:
+                fValid = false;
+                break;
+        }
+
+        if(fValid)
+        {
+            uint32_t ulComputedCrc;
+
+            /*  Check for disk corruption by comparing the stored CRC with one
+                computed from the data.
+
+                Also check the sequence number: if it is greater than the
+                current sequence number, the block is from a previous format
+                or the disk is writing blocks out of order.  During mount,
+                before the metaroots have been read, the sequence number will
+                be unknown, and the check is skipped.
+            */
+            ulComputedCrc = RedCrcNode(pbBuffer);
+            if(buf.ulCRC != ulComputedCrc)
+            {
+                fValid = false;
+            }
+            else if(gpRedVolume->fMounted && (buf.ullSequence >= gpRedVolume->ullSequence))
+            {
+                fValid = false;
+            }
+            else
+            {
+                /*  Buffer is valid.  No action, fValid is already true.
+                */
+            }
+        }
+    }
+
+    return fValid;
+}
+
+
+/** @brief Derive the index of the buffer.
+
+    @param pBuffer  The buffer to derive the index of.
+    @param pbIdx    On success, populated with the index of the buffer.
+
+    @return Boolean indicating result.
+
+    @retval true    Success.
+    @retval false   Failure.  @p pBuffer is not a valid buffer pointer.
+*/
+static bool BufferToIdx(
+    const void *pBuffer,
+    uint8_t    *pbIdx)
+{
+    bool        fRet = false;
+
+    if((pBuffer != NULL) && (pbIdx != NULL))
+    {
+        uint8_t bIdx;
+
+        /*  pBuffer should be a pointer to one of the block buffers.
+
+            A good compiler should optimize this loop into a bounds check and an
+            alignment check, although GCC has been observed to not do so; if the
+            number of buffers is small, it should not make much difference.  The
+            alternative is to use pointer comparisons, but this both deviates
+            from MISRA-C:2012 and involves undefined behavior.
+        */
+        for(bIdx = 0U; bIdx < REDCONF_BUFFER_COUNT; bIdx++)
+        {
+            if(pBuffer == &gBufCtx.b.aabBuffer[bIdx][0U])
+            {
+                break;
+            }
+        }
+
+        if(    (bIdx < REDCONF_BUFFER_COUNT)
+            && (gBufCtx.aHead[bIdx].ulBlock != BBLK_INVALID)
+            && (gBufCtx.aHead[bIdx].bVolNum == gbRedVolNum))
+        {
+            *pbIdx = bIdx;
+            fRet = true;
+        }
+    }
+
+    return fRet;
+}
+
+
+#if REDCONF_READ_ONLY == 0
+/** @brief Write out a dirty buffer.
+
+    @param bIdx The index of the buffer to write.
+
+    @return A negated ::REDSTATUS code indicating the operation result.
+
+    @retval 0           Operation was successful.
+    @retval -RED_EIO    A disk I/O error occurred.
+    @retval -RED_EINVAL Invalid parameters.
+*/
+static REDSTATUS BufferWrite(
+    uint8_t     bIdx)
+{
+    REDSTATUS   ret = 0;
+
+    if(bIdx < REDCONF_BUFFER_COUNT)
+    {
+        const BUFFERHEAD *pHead = &gBufCtx.aHead[bIdx];
+
+        REDASSERT((pHead->uFlags & BFLAG_DIRTY) != 0U);
+
+        if((pHead->uFlags & BFLAG_META) != 0U)
+        {
+            ret = BufferFinalize(gBufCtx.b.aabBuffer[bIdx], pHead->uFlags);
+        }
+
+        if(ret == 0)
+        {
+            ret = RedIoWrite(pHead->bVolNum, pHead->ulBlock, 1U, gBufCtx.b.aabBuffer[bIdx]);
+
+          #ifdef REDCONF_ENDIAN_SWAP
+            BufferEndianSwap(gBufCtx.b.aabBuffer[bIdx], pHead->uFlags);
+          #endif
+        }
+    }
+    else
+    {
+        REDERROR();
+        ret = -RED_EINVAL;
+    }
+
+    return ret;
+}
+
+
+/** @brief Finalize a metadata buffer.
+
+    This updates the CRC and the sequence number.  It also sets the signature,
+    though this is only truly needed if the buffer is new.
+
+    @param pbBuffer Pointer to the metadata buffer to finalize.
+    @param uFlags   The associated buffer flags.  Used to determine the expected
+                    signature.
+
+    @return A negated ::REDSTATUS code indicating the operation result.
+
+    @retval 0           Operation was successful.
+    @retval -RED_EINVAL Invalid parameter; or maximum sequence number reached.
+*/
+static REDSTATUS BufferFinalize(
+    uint8_t    *pbBuffer,
+    uint16_t    uFlags)
+{
+    REDSTATUS   ret = 0;
+
+    if((pbBuffer == NULL) || ((uFlags & BFLAG_MASK) != uFlags))
+    {
+        REDERROR();
+        ret = -RED_EINVAL;
+    }
+    else
+    {
+        uint32_t ulSignature;
+
+        switch(uFlags & BFLAG_META_MASK)
+        {
+            case BFLAG_META_MASTER:
+                ulSignature = META_SIG_MASTER;
+                break;
+          #if REDCONF_IMAP_EXTERNAL == 1
+            case BFLAG_META_IMAP:
+                ulSignature = META_SIG_IMAP;
+                break;
+          #endif
+            case BFLAG_META_INODE:
+                ulSignature = META_SIG_INODE;
+                break;
+          #if DINDIR_POINTERS > 0U
+            case BFLAG_META_DINDIR:
+                ulSignature = META_SIG_DINDIR;
+                break;
+          #endif
+          #if REDCONF_DIRECT_POINTERS < INODE_ENTRIES
+            case BFLAG_META_INDIR:
+                ulSignature = META_SIG_INDIR;
+                break;
+          #endif
+            default:
+                ulSignature = 0U;
+                break;
+        }
+
+        if(ulSignature == 0U)
+        {
+            REDERROR();
+            ret = -RED_EINVAL;
+        }
+        else
+        {
+            uint64_t ullSeqNum = gpRedVolume->ullSequence;
+
+            ret = RedVolSeqNumIncrement();
+            if(ret == 0)
+            {
+                uint32_t ulCrc;
+
+                RedMemCpy(&pbBuffer[NODEHEADER_OFFSET_SIG], &ulSignature, sizeof(ulSignature));
+                RedMemCpy(&pbBuffer[NODEHEADER_OFFSET_SEQ], &ullSeqNum, sizeof(ullSeqNum));
+
+              #ifdef REDCONF_ENDIAN_SWAP
+                BufferEndianSwap(pbBuffer, uFlags);
+              #endif
+
+                ulCrc = RedCrcNode(pbBuffer);
+              #ifdef REDCONF_ENDIAN_SWAP
+                ulCrc = RedRev32(ulCrc);
+              #endif
+                RedMemCpy(&pbBuffer[NODEHEADER_OFFSET_CRC], &ulCrc, sizeof(ulCrc));
+            }
+        }
+    }
+
+    return ret;
+}
+#endif /* REDCONF_READ_ONLY == 0 */
+
+
+#ifdef REDCONF_ENDIAN_SWAP
+/** @brief Swap the byte order of a metadata buffer
+
+    Does nothing if the buffer is not a metadata node.  Also does nothing for
+    meta roots, which don't go through the buffers anyways.
+
+    @param pBuffer  Pointer to the metadata buffer to swap
+    @param uFlags   The associated buffer flags.  Used to determin the type of
+                    metadata node.
+*/
+static void BufferEndianSwap(
+    void       *pBuffer,
+    uint16_t    uFlags)
+{
+    if((pBuffer == NULL) || ((uFlags & BFLAG_MASK) != uFlags))
+    {
+        REDERROR();
+    }
+    else if((uFlags & BFLAG_META_MASK) != 0)
+    {
+        BufferEndianSwapHeader(pBuffer);
+
+        switch(uFlags & BFLAG_META_MASK)
+        {
+            case BFLAG_META_MASTER:
+                BufferEndianSwapMaster(pBuffer);
+                break;
+            case BFLAG_META_INODE:
+                BufferEndianSwapInode(pBuffer);
+                break;
+          #if DINDIR_POINTERS > 0U
+            case BFLAG_META_DINDIR:
+                BufferEndianSwapIndir(pBuffer);
+                break;
+          #endif
+          #if REDCONF_DIRECT_POINTERS < INODE_ENTRIES
+            case BFLAG_META_INDIR:
+                BufferEndianSwapIndir(pBuffer);
+                break;
+          #endif
+            default:
+                break;
+        }
+    }
+    else
+    {
+        /*  File data buffers do not need to be swapped.
+        */
+    }
+}
+
+
+/** @brief Swap the byte order of a metadata node header
+
+    @param pHeader  Pointer to the metadata node header to swap
+*/
+static void BufferEndianSwapHeader(
+    NODEHEADER *pHeader)
+{
+    if(pHeader == NULL)
+    {
+        REDERROR();
+    }
+    else
+    {
+        pHeader->ulSignature = RedRev32(pHeader->ulSignature);
+        pHeader->ulCRC = RedRev32(pHeader->ulCRC);
+        pHeader->ullSequence = RedRev64(pHeader->ullSequence);
+    }
+}
+
+
+/** @brief Swap the byte order of a master block
+
+    @param pMaster  Pointer to the master block to swap
+*/
+static void BufferEndianSwapMaster(
+    MASTERBLOCK *pMaster)
+{
+    if(pMaster == NULL)
+    {
+        REDERROR();
+    }
+    else
+    {
+        pMaster->ulVersion = RedRev32(pMaster->ulVersion);
+        pMaster->ulFormatTime = RedRev32(pMaster->ulFormatTime);
+        pMaster->ulInodeCount = RedRev32(pMaster->ulInodeCount);
+        pMaster->ulBlockCount = RedRev32(pMaster->ulBlockCount);
+        pMaster->uMaxNameLen = RedRev16(pMaster->uMaxNameLen);
+        pMaster->uDirectPointers = RedRev16(pMaster->uDirectPointers);
+        pMaster->uIndirectPointers = RedRev16(pMaster->uIndirectPointers);
+    }
+}
+
+
+/** @brief Swap the byte order of an inode
+
+    @param pInode   Pointer to the inode to swap
+*/
+static void BufferEndianSwapInode(
+    INODE  *pInode)
+{
+    if(pInode == NULL)
+    {
+        REDERROR();
+    }
+    else
+    {
+        uint32_t ulIdx;
+
+        pInode->ullSize = RedRev64(pInode->ullSize);
+
+      #if REDCONF_INODE_BLOCKS == 1
+        pInode->ulBlocks = RedRev32(pInode->ulBlocks);
+      #endif
+
+      #if REDCONF_INODE_TIMESTAMPS == 1
+        pInode->ulATime = RedRev32(pInode->ulATime);
+        pInode->ulMTime = RedRev32(pInode->ulMTime);
+        pInode->ulCTime = RedRev32(pInode->ulCTime);
+      #endif
+
+        pInode->uMode = RedRev16(pInode->uMode);
+
+      #if (REDCONF_API_POSIX == 1) && (REDCONF_API_POSIX_LINK == 1)
+        pInode->uNLink = RedRev16(pInode->uNLink);
+      #endif
+
+      #if REDCONF_API_POSIX == 1
+        pInode->ulPInode = RedRev32(pInode->ulPInode);
+      #endif
+
+        for(ulIdx = 0; ulIdx < INODE_ENTRIES; ulIdx++)
+        {
+            pInode->aulEntries[ulIdx] = RedRev32(pInode->aulEntries[ulIdx]);
+        }
+    }
+}
+
+
+#if REDCONF_DIRECT_POINTERS < INODE_ENTRIES
+/** @brief Swap the byte order of an indirect or double indirect node
+
+    @param pIndir   Pointer to the node to swap
+*/
+static void BufferEndianSwapIndir(
+    INDIR  *pIndir)
+{
+    if(pIndir == NULL)
+    {
+        REDERROR();
+    }
+    else
+    {
+        uint32_t ulIdx;
+
+        pIndir->ulInode = RedRev32(pIndir->ulInode);
+
+        for(ulIdx = 0; ulIdx < INDIR_ENTRIES; ulIdx++)
+        {
+            pIndir->aulEntries[ulIdx] = RedRev32(pIndir->aulEntries[ulIdx]);
+        }
+    }
+}
+
+#endif /* REDCONF_DIRECT_POINTERS < INODE_ENTRIES */
+#endif /* #ifdef REDCONF_ENDIAN_SWAP */
+
+
+/** @brief Mark a buffer as least recently used.
+
+    @param bIdx The index of the buffer to make LRU.
+*/
+static void BufferMakeLRU(
+    uint8_t bIdx)
+{
+    if(bIdx >= REDCONF_BUFFER_COUNT)
+    {
+        REDERROR();
+    }
+    else if(bIdx != gBufCtx.abMRU[REDCONF_BUFFER_COUNT - 1U])
+    {
+        uint8_t bMruIdx;
+
+        /*  Find the current position of the buffer in the MRU array.  We do not
+            need to check the last slot, since we already know from the above
+            check that the index is not there.
+        */
+        for(bMruIdx = 0U; bMruIdx < (REDCONF_BUFFER_COUNT - 1U); bMruIdx++)
+        {
+            if(bIdx == gBufCtx.abMRU[bMruIdx])
+            {
+                break;
+            }
+        }
+
+        if(bMruIdx < (REDCONF_BUFFER_COUNT - 1U))
+        {
+            /*  Move the buffer index to the back of the MRU array, making it
+                the LRU buffer.
+            */
+            RedMemMove(&gBufCtx.abMRU[bMruIdx], &gBufCtx.abMRU[bMruIdx + 1U], REDCONF_BUFFER_COUNT - ((uint32_t)bMruIdx + 1U));
+            gBufCtx.abMRU[REDCONF_BUFFER_COUNT - 1U] = bIdx;
+        }
+        else
+        {
+            REDERROR();
+        }
+    }
+    else
+    {
+        /*  Buffer already LRU, nothing to do.
+        */
+    }
+}
+
+
+/** @brief Mark a buffer as most recently used.
+
+    @param bIdx The index of the buffer to make MRU.
+*/
+static void BufferMakeMRU(
+    uint8_t bIdx)
+{
+    if(bIdx >= REDCONF_BUFFER_COUNT)
+    {
+        REDERROR();
+    }
+    else if(bIdx != gBufCtx.abMRU[0U])
+    {
+        uint8_t bMruIdx;
+
+        /*  Find the current position of the buffer in the MRU array.  We do not
+            need to check the first slot, since we already know from the above
+            check that the index is not there.
+        */
+        for(bMruIdx = 1U; bMruIdx < REDCONF_BUFFER_COUNT; bMruIdx++)
+        {
+            if(bIdx == gBufCtx.abMRU[bMruIdx])
+            {
+                break;
+            }
+        }
+
+        if(bMruIdx < REDCONF_BUFFER_COUNT)
+        {
+            /*  Move the buffer index to the front of the MRU array, making it
+                the MRU buffer.
+            */
+            RedMemMove(&gBufCtx.abMRU[1U], &gBufCtx.abMRU[0U], bMruIdx);
+            gBufCtx.abMRU[0U] = bIdx;
+        }
+        else
+        {
+            REDERROR();
+        }
+    }
+    else
+    {
+        /*  Buffer already MRU, nothing to do.
+        */
+    }
+}
+
+
+/** @brief Find a block in the buffers.
+
+    @param ulBlock  The block number to find.
+    @param pbIdx    If the block is buffered (true is returned), populated with
+                    the index of the buffer.
+
+    @return Boolean indicating whether or not the block is buffered.
+
+    @retval true    @p ulBlock is buffered, and its index has been stored in
+                    @p pbIdx.
+    @retval false   @p ulBlock is not buffered.
+*/
+static bool BufferFind(
+    uint32_t ulBlock,
+    uint8_t *pbIdx)
+{
+    bool     ret = false;
+
+    if((ulBlock >= gpRedVolume->ulBlockCount) || (pbIdx == NULL))
+    {
+        REDERROR();
+    }
+    else
+    {
+        uint8_t bIdx;
+
+        for(bIdx = 0U; bIdx < REDCONF_BUFFER_COUNT; bIdx++)
+        {
+            const BUFFERHEAD *pHead = &gBufCtx.aHead[bIdx];
+
+            if((pHead->bVolNum == gbRedVolNum) && (pHead->ulBlock == ulBlock))
+            {
+                *pbIdx = bIdx;
+                ret = true;
+                break;
+            }
+        }
+    }
+
+    return ret;
+}
+
index fc94f5ca273f1a168c90005cda2e41a862a3ea1d..bcc9bff710c1bb336b8a07fb612fc3ec7bff3d9e 100644 (file)
-# Reliance Edge Release Notes\r
-\r
-This file contains a list of updates made to Reliance Edge over the course of\r
-recent releases and a list of known issues.\r
-\r
-## Release History and Changes\r
-\r
-### Reliance Edge v1.0, July 2015\r
-\r
-#### Common Code Changes\r
-\r
-- First release of commercial kit and MISRA C:2012 Design Assurance Package.\r
-  The commercial kit includes many new tools and tests which were not previously\r
-  available.\r
-- Overhauled parsing of command-line parameters to be consistent for all tools\r
-  and tests.  Command-line tools now use Unix-style short and long options (such\r
-  as `-H` and `--help`) instead of DOS-style switches (such as `/?`).\r
-- Renamed all os/\*/include/ostypes.h headers to os/\*/include/redostypes.h, so\r
-  that all headers use the product prefix.  If you created a port using v0.9,\r
-  this header needs to be renamed and its header guard (#ifndef OSTYPES_H etc.)\r
-  should also be updated.\r
-- Add a new header for OS-specific MISRA C:2012 deviation macros, located at\r
-  os/\*/include/redosdeviations.h.  If you created a port using v0.9, copy the\r
-  template from os/stub/include/redosdeviations.h into the include directory.\r
-- Eliminated support for sector sizes less than 256.  If using a smaller sector\r
-  size (say for a RAM disk), this must now be emulated in the implementation of\r
-  the block device OS service.\r
-- Added RedFseFormat() as an optional FSE API, allowing FSE applications to\r
-  format the volume at run-time.\r
-  - This added a new macro to redconf.h: existing redconf.h files from v0.9 must\r
-    be updated to work with v1.0.  Open redconf.h with the configuration tool,\r
-    ignore the warning about the missing macro, and save it.\r
-- Internal restructuring has renamed the macros for the string and memory\r
-  functions used in redconf.h.  An existing redconf.h file from v0.9 will need\r
-  to be updated; for a file containing the old names, the new config tool will\r
-  default to using the (slow) Reliance Edge string/memory functions; to use the\r
-  C library or custom versions, this will need to be selected in the\r
-  configuration utility.\r
-- Fix a bug which would result in an error when attempting to create a name with\r
-  one or more trailing path separators (such as `red_mkdir("/foo/bar/")`).\r
-- Fix a bug where an open handle for an inode on one volume would prevent the\r
-  same inode number from being deleted on a different volume.\r
-\r
-#### FreeRTOS Port Changes\r
-\r
-- The implementation of the timestamp OS service no longer requires that\r
-  `configUSE_TIMERS` be set to `1`.\r
-\r
-### Reliance Edge v0.9 (Beta), April 2015\r
-\r
-First public release.\r
-\r
-## Known Issues\r
-\r
-### Visual Studio 2005\r
-\r
-The Reliance Edge Win32 port (used for the host tools and the Win32 test\r
-project) cannot be compiled by Visual Studio 2005.  This is not going to be\r
-fixed since VS2005 is an old toolset.  Newer versions of Visual Studio, starting\r
-with Visual Studio 2008, work just fine.\r
-\r
+# Reliance Edge Release Notes
+
+This file contains a list of updates made to Reliance Edge over the course of
+recent releases and a list of known issues.
+
+## Release History and Changes
+
+### Reliance Edge v1.0.4, July 2016
+
+- Added ARM mbed and ARM mbed OS support in the commercial kit, with an example
+  projects for ARM mbed OS on the NXP FRDM-K64F board.
+- Some minor deficiencies in the POSIX-like API test suite have been addressed.
+
+### Reliance Edge v1.0.3, June 2016
+
+- Added support for static memory allocation configuration in FreeRTOS
+  version 9.  No common code changes.
+  
+### Reliance Edge v1.0.2, February 2016
+
+#### Common Code Changes
+- A new per-volume configuration option has been added: users can specify a
+  number of times to retry a block device read, write or flush operation before
+  returning a failure.  The configuration tool has been updated to version 1.0.2
+  with this change.
+  - This added a new field to the volume configuration in to redconf.c: existing
+    redconf.c files from v1.0.1 and earlier must be updated to work with v1.0.2.
+    Open redconf.h and redconf.c with the configuration tool, enable
+    "Retry block device I/O on failure" for any volumes if desired, and save the
+    redconf files.
+
+#### FreeRTOS Port Changes
+- Added support for the STM32 HAL SD card driver in the FreeRTOS block device
+  interface.  Two boards are supported out-of-the-box: the STM324xG-EVAL and the
+  STM32F746NG-Discovery.  A sample project is included for the STM324xG-EVAL.
+
+#### MQX Port Changes
+- Fixed a bug which prevented Reliance Edge from compiling if the File System
+  Essentials API was selected in the configuration.
+- Fixed a bug which would have returned an uninitialized value from
+  `RedOsBDevFlush()` for block devices that support flushing.
+  
+### Reliance Edge v1.0.1, October 2015
+
+- Added MQX RTOS support in the commercial kit, with example projects for
+  the Kinetis Design Studio.
+- Bug fix in the F_DRIVER implementation of the FreeRTOS block device service.
+
+### Reliance Edge v1.0, July 2015
+
+#### Common Code Changes
+
+- First release of commercial kit and MISRA C:2012 Design Assurance Package.
+  The commercial kit includes many new tools and tests which were not previously
+  available.
+- Overhauled parsing of command-line parameters to be consistent for all tools
+  and tests.  Command-line tools now use Unix-style short and long options (such
+  as `-H` and `--help`) instead of DOS-style switches (such as `/?`).
+- Renamed all os/\*/include/ostypes.h headers to os/\*/include/redostypes.h, so
+  that all headers use the product prefix.  If you created a port using v0.9,
+  this header needs to be renamed and its header guard (#ifndef OSTYPES_H etc.)
+  should also be updated.
+- Add a new header for OS-specific MISRA C:2012 deviation macros, located at
+  os/\*/include/redosdeviations.h.  If you created a port using v0.9, copy the
+  template from os/stub/include/redosdeviations.h into the include directory.
+- Eliminated support for sector sizes less than 256.  If using a smaller sector
+  size (say for a RAM disk), this must now be emulated in the implementation of
+  the block device OS service.
+- Added RedFseFormat() as an optional FSE API, allowing FSE applications to
+  format the volume at run-time.
+  - This added a new macro to redconf.h: existing redconf.h files from v0.9 must
+    be updated to work with v1.0.  Open redconf.h with the configuration tool,
+    ignore the warning about the missing macro, and save it.
+- Internal restructuring has renamed the macros for the string and memory
+  functions used in redconf.h.  An existing redconf.h file from v0.9 will need
+  to be updated; for a file containing the old names, the new config tool will
+  default to using the (slow) Reliance Edge string/memory functions; to use the
+  C library or custom versions, this will need to be selected in the
+  configuration utility.
+- Fix a bug which would result in an error when attempting to create a name with
+  one or more trailing path separators (such as `red_mkdir("/foo/bar/")`).
+- Fix a bug where an open handle for an inode on one volume would prevent the
+  same inode number from being deleted on a different volume.
+
+#### FreeRTOS Port Changes
+
+- The implementation of the timestamp OS service no longer requires that
+  `configUSE_TIMERS` be set to `1`.
+
+### Reliance Edge v0.9 (Beta), April 2015
+
+First public release.
+
+## Known Issues
+
+### Visual Studio 2005
+
+The Reliance Edge Win32 port (used for the host tools and the Win32 test
+project) cannot be compiled by Visual Studio 2005.  This is not going to be
+fixed since VS2005 is an old toolset.  Newer versions of Visual Studio, starting
+with Visual Studio 2008, work just fine.
+
index e12f1f290d196cd82961f8817db27d7979bb1912..dedfdbb2aa5cdb4803c0f87cf49e9d20053d0302 100644 (file)
-\r
-\r
-RELIANCE EDGE RELEASE NOTES\r
-\r
-\r
-This file contains a list of updates made to Reliance Edge over the\r
-course of recent releases and a list of known issues.\r
-\r
-\r
-Release History and Changes\r
-\r
-Reliance Edge v1.0, July 2015\r
-\r
-Common Code Changes\r
-\r
--   First release of commercial kit and MISRA C:2012 Design\r
-    Assurance Package. The commercial kit includes many new tools and\r
-    tests which were not previously available.\r
--   Overhauled parsing of command-line parameters to be consistent for\r
-    all tools and tests. Command-line tools now use Unix-style short and\r
-    long options (such as -H and --help) instead of DOS-style switches\r
-    (such as /?).\r
--   Renamed all os/*/include/ostypes.h headers to\r
-    os/*/include/redostypes.h, so that all headers use the\r
-    product prefix. If you created a port using v0.9, this header needs\r
-    to be renamed and its header guard (#ifndef OSTYPES_H etc.) should\r
-    also be updated.\r
--   Add a new header for OS-specific MISRA C:2012 deviation macros,\r
-    located at os/*/include/redosdeviations.h. If you created a port\r
-    using v0.9, copy the template from os/stub/include/redosdeviations.h\r
-    into the include directory.\r
--   Eliminated support for sector sizes less than 256. If using a\r
-    smaller sector size (say for a RAM disk), this must now be emulated\r
-    in the implementation of the block device OS service.\r
--   Added RedFseFormat() as an optional FSE API, allowing FSE\r
-    applications to format the volume at run-time.\r
--   This added a new macro to redconf.h: existing redconf.h files from\r
-    v0.9 must be updated to work with v1.0. Open redconf.h with the\r
-    configuration tool, ignore the warning about the missing macro, and\r
-    save it.\r
--   Internal restructuring has renamed the macros for the string and\r
-    memory functions used in redconf.h. An existing redconf.h file from\r
-    v0.9 will need to be updated; for a file containing the old names,\r
-    the new config tool will default to using the (slow) Reliance Edge\r
-    string/memory functions; to use the C library or custom versions,\r
-    this will need to be selected in the configuration utility.\r
--   Fix a bug which would result in an error when attempting to create a\r
-    name with one or more trailing path separators (such as\r
-    red_mkdir("/foo/bar/")).\r
--   Fix a bug where an open handle for an inode on one volume would\r
-    prevent the same inode number from being deleted on a\r
-    different volume.\r
-\r
-FreeRTOS Port Changes\r
-\r
--   The implementation of the timestamp OS service no longer requires\r
-    that configUSE_TIMERS be set to 1.\r
-\r
-Reliance Edge v0.9 (Beta), April 2015\r
-\r
-First public release.\r
-\r
-\r
-Known Issues\r
-\r
-Visual Studio 2005\r
-\r
-The Reliance Edge Win32 port (used for the host tools and the Win32 test\r
-project) cannot be compiled by Visual Studio 2005. This is not going to\r
-be fixed since VS2005 is an old toolset. Newer versions of Visual\r
-Studio, starting with Visual Studio 2008, work just fine.\r
+
+
+RELIANCE EDGE RELEASE NOTES
+
+
+This file contains a list of updates made to Reliance Edge over the
+course of recent releases and a list of known issues.
+
+
+Release History and Changes
+
+Reliance Edge v1.0.4, July 2016
+
+-   Added ARM mbed and ARM mbed OS support in the commercial kit, with
+    an example projects for ARM mbed OS on the NXP FRDM-K64F board.
+-   Some minor deficiencies in the POSIX-like API test suite have
+    been addressed.
+
+Reliance Edge v1.0.3, June 2016
+
+-   Added support for static memory allocation configuration in FreeRTOS
+    version 9. No common code changes.
+
+Reliance Edge v1.0.2, February 2016
+
+Common Code Changes
+
+-   A new per-volume configuration option has been added: users can
+    specify a number of times to retry a block device read, write or
+    flush operation before returning a failure. The configuration tool
+    has been updated to version 1.0.2 with this change.
+-   This added a new field to the volume configuration in to redconf.c:
+    existing redconf.c files from v1.0.1 and earlier must be updated to
+    work with v1.0.2. Open redconf.h and redconf.c with the
+    configuration tool, enable "Retry block device I/O on failure" for
+    any volumes if desired, and save the redconf files.
+
+FreeRTOS Port Changes
+
+-   Added support for the STM32 HAL SD card driver in the FreeRTOS block
+    device interface. Two boards are supported out-of-the-box: the
+    STM324xG-EVAL and the STM32F746NG-Discovery. A sample project is
+    included for the STM324xG-EVAL.
+
+MQX Port Changes
+
+-   Fixed a bug which prevented Reliance Edge from compiling if the File
+    System Essentials API was selected in the configuration.
+-   Fixed a bug which would have returned an uninitialized value from
+    RedOsBDevFlush() for block devices that support flushing.
+
+Reliance Edge v1.0.1, October 2015
+
+-   Added MQX RTOS support in the commercial kit, with example projects
+    for the Kinetis Design Studio.
+-   Bug fix in the F_DRIVER implementation of the FreeRTOS block
+    device service.
+
+Reliance Edge v1.0, July 2015
+
+Common Code Changes
+
+-   First release of commercial kit and MISRA C:2012 Design
+    Assurance Package. The commercial kit includes many new tools and
+    tests which were not previously available.
+-   Overhauled parsing of command-line parameters to be consistent for
+    all tools and tests. Command-line tools now use Unix-style short and
+    long options (such as -H and --help) instead of DOS-style switches
+    (such as /?).
+-   Renamed all os/*/include/ostypes.h headers to
+    os/*/include/redostypes.h, so that all headers use the
+    product prefix. If you created a port using v0.9, this header needs
+    to be renamed and its header guard (#ifndef OSTYPES_H etc.) should
+    also be updated.
+-   Add a new header for OS-specific MISRA C:2012 deviation macros,
+    located at os/*/include/redosdeviations.h. If you created a port
+    using v0.9, copy the template from os/stub/include/redosdeviations.h
+    into the include directory.
+-   Eliminated support for sector sizes less than 256. If using a
+    smaller sector size (say for a RAM disk), this must now be emulated
+    in the implementation of the block device OS service.
+-   Added RedFseFormat() as an optional FSE API, allowing FSE
+    applications to format the volume at run-time.
+-   This added a new macro to redconf.h: existing redconf.h files from
+    v0.9 must be updated to work with v1.0. Open redconf.h with the
+    configuration tool, ignore the warning about the missing macro, and
+    save it.
+-   Internal restructuring has renamed the macros for the string and
+    memory functions used in redconf.h. An existing redconf.h file from
+    v0.9 will need to be updated; for a file containing the old names,
+    the new config tool will default to using the (slow) Reliance Edge
+    string/memory functions; to use the C library or custom versions,
+    this will need to be selected in the configuration utility.
+-   Fix a bug which would result in an error when attempting to create a
+    name with one or more trailing path separators (such as
+    red_mkdir("/foo/bar/")).
+-   Fix a bug where an open handle for an inode on one volume would
+    prevent the same inode number from being deleted on a
+    different volume.
+
+FreeRTOS Port Changes
+
+-   The implementation of the timestamp OS service no longer requires
+    that configUSE_TIMERS be set to 1.
+
+Reliance Edge v0.9 (Beta), April 2015
+
+First public release.
+
+
+Known Issues
+
+Visual Studio 2005
+
+The Reliance Edge Win32 port (used for the host tools and the Win32 test
+project) cannot be compiled by Visual Studio 2005. This is not going to
+be fixed since VS2005 is an old toolset. Newer versions of Visual
+Studio, starting with Visual Studio 2008, work just fine.
diff --git a/FreeRTOS-Plus/Source/Reliance-Edge/include/redpath.h b/FreeRTOS-Plus/Source/Reliance-Edge/include/redpath.h
new file mode 100644 (file)
index 0000000..06f7c36
--- /dev/null
@@ -0,0 +1,38 @@
+/*             ----> DO NOT REMOVE THE FOLLOWING NOTICE <----
+
+                   Copyright (c) 2014-2015 Datalight, Inc.
+                       All Rights Reserved Worldwide.
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; use version 2 of the License.
+
+    This program is distributed in the hope that it will be useful,
+    but "AS-IS," WITHOUT ANY WARRANTY; without even the implied warranty
+    of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+/*  Businesses and individuals that for commercial or other reasons cannot
+    comply with the terms of the GPLv2 license may obtain a commercial license
+    before incorporating Reliance Edge into proprietary software for
+    distribution in any form.  Visit http://www.datalight.com/reliance-edge for
+    more information.
+*/
+/** @file
+    @brief Interfaces of path utilities for the POSIX-like API layer.
+*/
+#ifndef REDPATH_H
+#define REDPATH_H
+
+
+REDSTATUS RedPathSplit(const char *pszPath, uint8_t *pbVolNum, const char **ppszLocalPath);
+REDSTATUS RedPathLookup(const char *pszLocalPath, uint32_t *pulInode);
+REDSTATUS RedPathToName(const char *pszLocalPath, uint32_t *pulPInode, const char **ppszName);
+
+
+#endif
+
index 017061e41c2ec840b0703a96f61e3ba92eb48cc2..cbe768336eac9dac977a3fd5491b0fb9b444ad50 100644 (file)
-/*             ----> DO NOT REMOVE THE FOLLOWING NOTICE <----\r
-\r
-                   Copyright (c) 2014-2015 Datalight, Inc.\r
-                       All Rights Reserved Worldwide.\r
-\r
-    This program is free software; you can redistribute it and/or modify\r
-    it under the terms of the GNU General Public License as published by\r
-    the Free Software Foundation; use version 2 of the License.\r
-\r
-    This program is distributed in the hope that it will be useful,\r
-    but "AS-IS," WITHOUT ANY WARRANTY; without even the implied warranty\r
-    of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
-    GNU General Public License for more details.\r
-\r
-    You should have received a copy of the GNU General Public License along\r
-    with this program; if not, write to the Free Software Foundation, Inc.,\r
-    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
-*/\r
-/*  Businesses and individuals that for commercial or other reasons cannot\r
-    comply with the terms of the GPLv2 license may obtain a commercial license\r
-    before incorporating Reliance Edge into proprietary software for\r
-    distribution in any form.  Visit http://www.datalight.com/reliance-edge for\r
-    more information.\r
-*/\r
-/** @file\r
-    @brief Prototypes for Reliance Edge test entry points.\r
-*/\r
-#ifndef REDTESTS_H\r
-#define REDTESTS_H\r
-\r
-#include <redtypes.h>\r
-#include "redtestutils.h"\r
-#include "redver.h"\r
-\r
-/*  This macro is only defined by the error injection project.\r
-*/\r
-#ifdef REDCONF_ERROR_INJECTION\r
-#include <rederrinject.h>\r
-#endif\r
-\r
-#define FSSTRESS_SUPPORTED  \\r
-    (    ((RED_KIT == RED_KIT_GPL) || (RED_KIT == RED_KIT_SANDBOX)) \\r
-      && (REDCONF_OUTPUT == 1) && (REDCONF_READ_ONLY == 0) && (REDCONF_PATH_SEPARATOR == '/') \\r
-      && (REDCONF_API_POSIX == 1) && (REDCONF_API_POSIX_UNLINK == 1) && (REDCONF_API_POSIX_MKDIR == 1) \\r
-      && (REDCONF_API_POSIX_RMDIR == 1) && (REDCONF_API_POSIX_RENAME == 1) && (REDCONF_API_POSIX_LINK == 1) \\r
-      && (REDCONF_API_POSIX_FTRUNCATE == 1) && (REDCONF_API_POSIX_READDIR == 1))\r
-\r
-#define FSE_STRESS_TEST_SUPPORTED \\r
-    (    ((RED_KIT == RED_KIT_COMMERCIAL) || (RED_KIT == RED_KIT_SANDBOX)) \\r
-      && (REDCONF_OUTPUT == 1) && (REDCONF_READ_ONLY == 0) && (REDCONF_API_FSE == 1) \\r
-      && (REDCONF_API_FSE_FORMAT == 1) && (REDCONF_API_FSE_TRANSMASKSET == 1) && (REDCONF_API_FSE_TRANSMASKGET == 1) \\r
-      && (REDCONF_API_FSE_TRUNCATE == 1))\r
-\r
-#define POSIX_API_TEST_SUPPORTED \\r
-    (    ((RED_KIT == RED_KIT_COMMERCIAL) || (RED_KIT == RED_KIT_SANDBOX)) \\r
-      && (REDCONF_OUTPUT == 1) && (REDCONF_READ_ONLY == 0) && (REDCONF_API_POSIX == 1) \\r
-      && (REDCONF_API_POSIX_FORMAT == 1) && (REDCONF_API_POSIX_UNLINK == 1))\r
-\r
-#define FSE_API_TEST_SUPPORTED \\r
-   (    ((RED_KIT == RED_KIT_COMMERCIAL) || (RED_KIT == RED_KIT_SANDBOX)) \\r
-      && (REDCONF_OUTPUT == 1) && (REDCONF_READ_ONLY == 0) && (REDCONF_API_FSE == 1) \\r
-      && (REDCONF_API_FSE_FORMAT == 1))\r
-\r
-#define STOCH_POSIX_TEST_SUPPORTED \\r
-    (    ((RED_KIT == RED_KIT_COMMERCIAL) || (RED_KIT == RED_KIT_SANDBOX)) \\r
-      && (REDCONF_OUTPUT == 1) && (REDCONF_READ_ONLY == 0) && (REDCONF_API_POSIX == 1) \\r
-      && (REDCONF_READ_ONLY == 0) && (REDCONF_API_POSIX_FORMAT == 1) && (REDCONF_API_POSIX_READDIR == 1) \\r
-      && (REDCONF_API_POSIX_MKDIR == 1) && (REDCONF_API_POSIX_RMDIR == 1) && (REDCONF_API_POSIX_UNLINK == 1) \\r
-      && (REDCONF_API_POSIX_RENAME == 1))\r
-\r
-#define FSIOTEST_SUPPORTED \\r
-    (    ((RED_KIT == RED_KIT_COMMERCIAL) || (RED_KIT == RED_KIT_SANDBOX)) \\r
-      && (REDCONF_OUTPUT == 1) && (REDCONF_API_POSIX == 1))\r
-\r
-#define BDEVTEST_SUPPORTED \\r
-    (    ((RED_KIT == RED_KIT_COMMERCIAL) || (RED_KIT == RED_KIT_SANDBOX)) \\r
-      && (REDCONF_OUTPUT == 1) && (REDCONF_READ_ONLY == 0))\r
-\r
-#define DISKFULL_TEST_SUPPORTED \\r
-   (    ((RED_KIT == RED_KIT_COMMERCIAL) || (RED_KIT == RED_KIT_SANDBOX)) \\r
-     && (REDCONF_OUTPUT == 1) && (REDCONF_READ_ONLY == 0) && (REDCONF_API_POSIX == 1) \\r
-     && (REDCONF_API_POSIX_FORMAT == 1) && (REDCONF_API_POSIX_FTRUNCATE == 1))\r
-\r
-\r
-typedef enum\r
-{\r
-    PARAMSTATUS_OK,     /* Parameters were good; continue. */\r
-    PARAMSTATUS_BAD,    /* Parameters were bad; stop. */\r
-    PARAMSTATUS_HELP    /* Help request; not an error, but stop. */\r
-} PARAMSTATUS;\r
-\r
-\r
-#if FSSTRESS_SUPPORTED\r
-typedef struct\r
-{\r
-    bool        fNoCleanup; /**< --no-cleanup */\r
-    uint32_t    ulLoops;    /**< --loops */\r
-    uint32_t    ulNops;     /**< --nops */\r
-    bool        fNamePad;   /**< --namepad */\r
-    uint32_t    ulSeed;     /**< --seed */\r
-    bool        fVerbose;   /**< --verbose */\r
-} FSSTRESSPARAM;\r
-\r
-PARAMSTATUS FsstressParseParams(int argc, char *argv[], FSSTRESSPARAM *pParam, uint8_t *pbVolNum, const char **ppszDevice);\r
-void FsstressDefaultParams(FSSTRESSPARAM *pParam);\r
-int FsstressStart(const FSSTRESSPARAM *pParam);\r
-#endif\r
-\r
-#if STOCH_POSIX_TEST_SUPPORTED\r
-typedef struct\r
-{\r
-    const char *pszVolume;          /**< Volume path prefix. */\r
-    uint32_t    ulIterations;       /**< --iterations */\r
-    uint32_t    ulFileListMax;      /**< --files */\r
-    uint32_t    ulDirListMax;       /**< --dirs */\r
-    uint32_t    ulOpenFileListMax;  /**< --open-files */\r
-    uint32_t    ulOpenDirListMax;   /**< --open-dirs */\r
-    uint32_t    ulRandomSeed;       /**< --seed */\r
-} STOCHPOSIXPARAM;\r
-\r
-PARAMSTATUS RedStochPosixParseParams(int argc, char *argv[], STOCHPOSIXPARAM *pParam, uint8_t *pbVolNum, const char **ppszDevice);\r
-void RedStochPosixDefaultParams(STOCHPOSIXPARAM *pParam);\r
-int RedStochPosixStart(const STOCHPOSIXPARAM *pParam);\r
-#endif\r
-\r
-#if FSE_STRESS_TEST_SUPPORTED\r
-typedef struct\r
-{\r
-    uint8_t     bVolNum;        /**< Volume number. */\r
-    uint32_t    ulFileCount;    /**< --files */\r
-    uint32_t    ulMaxFileSize;  /**< --max */\r
-    uint32_t    ulMaxOpSize;    /**< --buffer-size */\r
-    uint32_t    ulNops;         /**< --nops */\r
-    uint32_t    ulLoops;        /**< --loops */\r
-    uint32_t    ulSampleRate;   /**< --sample-rate */\r
-    uint64_t    ullSeed;        /**< --seed */\r
-} FSESTRESSPARAM;\r
-\r
-PARAMSTATUS FseStressParseParams(int argc, char *argv[], FSESTRESSPARAM *pParam, uint8_t *pbVolNum, const char **ppszDevice);\r
-void FseStressDefaultParams(FSESTRESSPARAM *pParam);\r
-int FseStressStart(const FSESTRESSPARAM *pParam);\r
-#endif\r
-\r
-#if POSIX_API_TEST_SUPPORTED\r
-typedef struct\r
-{\r
-    const char *pszVolume;      /**< Volume path prefix. */\r
-    bool        fQuick;         /**< --quick */\r
-    bool        fQuitOnFailure; /**< --quit-on-failure */\r
-    bool        fDebugErrors;   /**< --debug */\r
-} POSIXTESTPARAM;\r
-\r
-PARAMSTATUS RedPosixTestParseParams(int argc, char *argv[], POSIXTESTPARAM *pParam, uint8_t *pbVolNum, const char **ppszDevice);\r
-void RedPosixTestDefaultParams(POSIXTESTPARAM *pParam);\r
-int RedPosixTestStart(const POSIXTESTPARAM *pParam);\r
-#endif\r
-\r
-\r
-#if FSE_API_TEST_SUPPORTED\r
-typedef struct\r
-{\r
-    uint8_t bVolNum;        /**< Volume number. */\r
-    bool    fQuitOnFailure; /**< --quit-on-failure */\r
-    bool    fDebugErrors;   /**< --debug */\r
-} FSETESTPARAM;\r
-\r
-PARAMSTATUS RedFseTestParseParams(int argc, char *argv[], FSETESTPARAM *pParam, uint8_t *pbVolNum, const char **ppszDevice);\r
-void RedFseTestDefaultParams(FSETESTPARAM *pParam);\r
-int RedFseTestStart(const FSETESTPARAM *pParam);\r
-#endif\r
-\r
-#if FSIOTEST_SUPPORTED\r
-typedef enum\r
-{\r
-    TESTFS_RELEDGE, /* Datalight Reliance Edge */\r
-    TESTFS_FATFS,   /* ChaN's FatFs */\r
-    TESTFS_FATSL    /* FreeRTOS+FAT SL */\r
-} TESTFS;\r
-\r
-typedef struct\r
-{\r
-    TESTFS      testfs;                 /**< --fs */\r
-    const char *pszVolume;              /**< Volume path prefix. */\r
-    bool        fSeqRead;               /**< --seq=r */\r
-    bool        fSeqWrite;              /**< --seq=w */\r
-    bool        fSeqRewrite;            /**< --seq=e */\r
-    bool        fRandomRead;            /**< --rand=r */\r
-    bool        fRandomWrite;           /**< --rand=w */\r
-    bool        fMixedWrite;            /**< --mixed */\r
-    bool        fScanTest;              /**< --scan */\r
-    uint32_t    ulFSBlockSize;          /**< --block-size */\r
-    uint32_t    ulMaxFileSize;          /**< --max */\r
-    uint32_t    ulRandomReadPasses;     /**< --rand-pass=r:w (r part) */\r
-    uint32_t    ulRandomWritePasses;    /**< --rand-pass=r:w (w part) */\r
-    uint32_t    ulMixedWritePasses;     /**< --mixed-pass */\r
-    int32_t     iFlushOnWriteRatio;     /**< --rand-fow */\r
-    uint32_t    ulBufferMin;            /**< --start */\r
-    uint32_t    ulBufferSize;           /**< --buffer-size */\r
-    bool        fWriteVerify;           /**< --verify */\r
-    uint32_t    ulSampleRate;           /**< --sample-rate */\r
-    uint32_t    ulScanCount;            /**< --scan-files */\r
-    uint64_t    ullSeed;                /**< --seed */\r
-} FSIOTESTPARAM;\r
-\r
-PARAMSTATUS FSIOTestParseParams(int argc, char *argv[], FSIOTESTPARAM *pParam, uint8_t *pbVolNum, const char **ppszDevice);\r
-void FSIOTestDefaultParams(FSIOTESTPARAM *pParam);\r
-int FSIOTestStart(const FSIOTESTPARAM *pParam);\r
-#endif\r
-\r
-#if BDEVTEST_SUPPORTED\r
-typedef struct\r
-{\r
-    uint8_t     bDrvNum;        /**< Volume number (for sector size/count). */\r
-    bool        fSeqWrite;      /**< --seq:w */\r
-    bool        fSeqRead;       /**< --seq:r */\r
-    bool        fRandWrite;     /**< --rand:w */\r
-    bool        fRandRead;      /**< --rand:r */\r
-    uint32_t    ulSampleSecs;   /**< --sample-rate */\r
-    uint32_t    ulPasses;       /**< --passes */\r
-    uint32_t    ulMinIOSectors; /**< --count=min[:max] (min part) */\r
-    uint32_t    ulMaxIOSectors; /**< --count=min[:max] (max part) */\r
-    uint32_t    ulMaxSizeKB;    /**< --max */\r
-    uint32_t    ulTestSeconds;  /**< --time */\r
-    bool        fVerify;        /**< --verify */\r
-    bool        fAsyncWrites;   /**< --async */\r
-    uint64_t    ullSeed;        /**< --seed */\r
-} BDEVTESTPARAM;\r
-\r
-PARAMSTATUS BDevTestParseParams(int argc, char *argv[], BDEVTESTPARAM *pParam, uint8_t *pbVolNum, const char **ppszDevice);\r
-void BDevTestDefaultParams(BDEVTESTPARAM *pParam);\r
-int BDevTestStart(const BDEVTESTPARAM *pParam);\r
-#endif\r
-\r
-#if DISKFULL_TEST_SUPPORTED\r
-typedef struct\r
-{\r
-    const char *pszVolume;      /**< Volume path prefix. */\r
-    bool        fQuitOnFailure; /**< --quit-on-failure */\r
-    bool        fDebugErrors;   /**< --debug */\r
-} DISKFULLTESTPARAM;\r
-\r
-PARAMSTATUS DiskFullTestParseParams(int argc, char *argv[], DISKFULLTESTPARAM *pParam, uint8_t *pbVolNum, const char **ppszDevice);\r
-void DiskFullTestDefaultParams(DISKFULLTESTPARAM *pParam);\r
-int DiskFullTestStart(const DISKFULLTESTPARAM *pParam);\r
-#endif\r
-\r
-\r
-#endif\r
-\r
+/*             ----> DO NOT REMOVE THE FOLLOWING NOTICE <----
+
+                   Copyright (c) 2014-2015 Datalight, Inc.
+                       All Rights Reserved Worldwide.
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; use version 2 of the License.
+
+    This program is distributed in the hope that it will be useful,
+    but "AS-IS," WITHOUT ANY WARRANTY; without even the implied warranty
+    of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+/*  Businesses and individuals that for commercial or other reasons cannot
+    comply with the terms of the GPLv2 license may obtain a commercial license
+    before incorporating Reliance Edge into proprietary software for
+    distribution in any form.  Visit http://www.datalight.com/reliance-edge for
+    more information.
+*/
+/** @file
+    @brief Prototypes for Reliance Edge test entry points.
+*/
+#ifndef REDTESTS_H
+#define REDTESTS_H
+
+#include <redtypes.h>
+#include "redtestutils.h"
+#include "redver.h"
+
+/*  This macro is only defined by the error injection project.
+*/
+#ifdef REDCONF_ERROR_INJECTION
+#include <rederrinject.h>
+#endif
+
+#define FSSTRESS_SUPPORTED  \
+    (    ((RED_KIT == RED_KIT_GPL) || (RED_KIT == RED_KIT_SANDBOX)) \
+      && (REDCONF_OUTPUT == 1) && (REDCONF_READ_ONLY == 0) && (REDCONF_PATH_SEPARATOR == '/') \
+      && (REDCONF_API_POSIX == 1) && (REDCONF_API_POSIX_UNLINK == 1) && (REDCONF_API_POSIX_MKDIR == 1) \
+      && (REDCONF_API_POSIX_RMDIR == 1) && (REDCONF_API_POSIX_RENAME == 1) && (REDCONF_API_POSIX_LINK == 1) \
+      && (REDCONF_API_POSIX_FTRUNCATE == 1) && (REDCONF_API_POSIX_READDIR == 1))
+
+#define FSE_STRESS_TEST_SUPPORTED \
+    (    ((RED_KIT == RED_KIT_COMMERCIAL) || (RED_KIT == RED_KIT_SANDBOX)) \
+      && (REDCONF_OUTPUT == 1) && (REDCONF_READ_ONLY == 0) && (REDCONF_API_FSE == 1) \
+      && (REDCONF_API_FSE_FORMAT == 1) && (REDCONF_API_FSE_TRANSMASKSET == 1) && (REDCONF_API_FSE_TRANSMASKGET == 1) \
+      && (REDCONF_API_FSE_TRUNCATE == 1))
+
+#define POSIX_API_TEST_SUPPORTED \
+    (    ((RED_KIT == RED_KIT_COMMERCIAL) || (RED_KIT == RED_KIT_SANDBOX)) \
+      && (REDCONF_OUTPUT == 1) && (REDCONF_READ_ONLY == 0) && (REDCONF_API_POSIX == 1) \
+      && (REDCONF_API_POSIX_FORMAT == 1) && (REDCONF_API_POSIX_UNLINK == 1))
+
+#define FSE_API_TEST_SUPPORTED \
+   (    ((RED_KIT == RED_KIT_COMMERCIAL) || (RED_KIT == RED_KIT_SANDBOX)) \
+      && (REDCONF_OUTPUT == 1) && (REDCONF_READ_ONLY == 0) && (REDCONF_API_FSE == 1) \
+      && (REDCONF_API_FSE_FORMAT == 1))
+
+#define STOCH_POSIX_TEST_SUPPORTED \
+    (    ((RED_KIT == RED_KIT_COMMERCIAL) || (RED_KIT == RED_KIT_SANDBOX)) \
+      && (REDCONF_OUTPUT == 1) && (REDCONF_READ_ONLY == 0) && (REDCONF_API_POSIX == 1) \
+      && (REDCONF_READ_ONLY == 0) && (REDCONF_API_POSIX_FORMAT == 1) && (REDCONF_API_POSIX_READDIR == 1) \
+      && (REDCONF_API_POSIX_MKDIR == 1) && (REDCONF_API_POSIX_RMDIR == 1) && (REDCONF_API_POSIX_UNLINK == 1) \
+      && (REDCONF_API_POSIX_RENAME == 1))
+
+#define FSIOTEST_SUPPORTED \
+    (    ((RED_KIT == RED_KIT_COMMERCIAL) || (RED_KIT == RED_KIT_SANDBOX)) \
+      && (REDCONF_OUTPUT == 1) && (REDCONF_API_POSIX == 1))
+
+#define BDEVTEST_SUPPORTED \
+    (    ((RED_KIT == RED_KIT_COMMERCIAL) || (RED_KIT == RED_KIT_SANDBOX)) \
+      && (REDCONF_OUTPUT == 1) && (REDCONF_READ_ONLY == 0))
+
+#define DISKFULL_TEST_SUPPORTED \
+   (    ((RED_KIT == RED_KIT_COMMERCIAL) || (RED_KIT == RED_KIT_SANDBOX)) \
+     && (REDCONF_OUTPUT == 1) && (REDCONF_READ_ONLY == 0) && (REDCONF_API_POSIX == 1) \
+     && (REDCONF_API_POSIX_FORMAT == 1) && (REDCONF_API_POSIX_FTRUNCATE == 1))
+
+
+typedef enum
+{
+    PARAMSTATUS_OK,     /* Parameters were good; continue. */
+    PARAMSTATUS_BAD,    /* Parameters were bad; stop. */
+    PARAMSTATUS_HELP    /* Help request; not an error, but stop. */
+} PARAMSTATUS;
+
+
+#if FSSTRESS_SUPPORTED
+typedef struct
+{
+    bool        fNoCleanup; /**< --no-cleanup */
+    uint32_t    ulLoops;    /**< --loops */
+    uint32_t    ulNops;     /**< --nops */
+    bool        fNamePad;   /**< --namepad */
+    uint32_t    ulSeed;     /**< --seed */
+    bool        fVerbose;   /**< --verbose */
+} FSSTRESSPARAM;
+
+PARAMSTATUS FsstressParseParams(int argc, char *argv[], FSSTRESSPARAM *pParam, uint8_t *pbVolNum, const char **ppszDevice);
+void FsstressDefaultParams(FSSTRESSPARAM *pParam);
+int FsstressStart(const FSSTRESSPARAM *pParam);
+#endif
+
+#if STOCH_POSIX_TEST_SUPPORTED
+typedef struct
+{
+    const char *pszVolume;          /**< Volume path prefix. */
+    uint32_t    ulIterations;       /**< --iterations */
+    uint32_t    ulFileListMax;      /**< --files */
+    uint32_t    ulDirListMax;       /**< --dirs */
+    uint32_t    ulOpenFileListMax;  /**< --open-files */
+    uint32_t    ulOpenDirListMax;   /**< --open-dirs */
+    uint32_t    ulRandomSeed;       /**< --seed */
+} STOCHPOSIXPARAM;
+
+PARAMSTATUS RedStochPosixParseParams(int argc, char *argv[], STOCHPOSIXPARAM *pParam, uint8_t *pbVolNum, const char **ppszDevice);
+void RedStochPosixDefaultParams(STOCHPOSIXPARAM *pParam);
+int RedStochPosixStart(const STOCHPOSIXPARAM *pParam);
+#endif
+
+#if FSE_STRESS_TEST_SUPPORTED
+typedef struct
+{
+    uint8_t     bVolNum;        /**< Volume number. */
+    uint32_t    ulFileCount;    /**< --files */
+    uint32_t    ulMaxFileSize;  /**< --max */
+    uint32_t    ulMaxOpSize;    /**< --buffer-size */
+    uint32_t    ulNops;         /**< --nops */
+    uint32_t    ulLoops;        /**< --loops */
+    uint32_t    ulSampleRate;   /**< --sample-rate */
+    uint64_t    ullSeed;        /**< --seed */
+} FSESTRESSPARAM;
+
+PARAMSTATUS FseStressParseParams(int argc, char *argv[], FSESTRESSPARAM *pParam, uint8_t *pbVolNum, const char **ppszDevice);
+void FseStressDefaultParams(FSESTRESSPARAM *pParam);
+int FseStressStart(const FSESTRESSPARAM *pParam);
+#endif
+
+#if POSIX_API_TEST_SUPPORTED
+typedef struct
+{
+    const char *pszVolume;      /**< Volume path prefix. */
+    bool        fQuick;         /**< --quick */
+    bool        fQuitOnFailure; /**< --quit-on-failure */
+    bool        fDebugErrors;   /**< --debug */
+} POSIXTESTPARAM;
+
+PARAMSTATUS RedPosixTestParseParams(int argc, char *argv[], POSIXTESTPARAM *pParam, uint8_t *pbVolNum, const char **ppszDevice);
+void RedPosixTestDefaultParams(POSIXTESTPARAM *pParam);
+int RedPosixTestStart(const POSIXTESTPARAM *pParam);
+#endif
+
+
+#if POSIX_API_TEST_SUPPORTED
+typedef struct
+{
+    const char *pszVolume;      /**< Volume path prefix. */
+    bool        fQuick;         /**< --quick */
+    bool        fVerbose;       /**< --verbose */
+    bool        fQuitOnFailure; /**< --quit-on-failure */
+    bool        fDebugErrors;   /**< --debug */
+} OSAPITESTPARAM;
+
+PARAMSTATUS RedOsApiTestParseParams(int argc, char *argv[], OSAPITESTPARAM *pParam, const char **ppszDevice);
+void RedOsApiTestDefaultParams(OSAPITESTPARAM *pParam);
+int RedOsApiTestStart(const OSAPITESTPARAM *pParam);
+#endif
+
+
+#if FSE_API_TEST_SUPPORTED
+typedef struct
+{
+    uint8_t bVolNum;        /**< Volume number. */
+    bool    fQuitOnFailure; /**< --quit-on-failure */
+    bool    fDebugErrors;   /**< --debug */
+} FSETESTPARAM;
+
+PARAMSTATUS RedFseTestParseParams(int argc, char *argv[], FSETESTPARAM *pParam, uint8_t *pbVolNum, const char **ppszDevice);
+void RedFseTestDefaultParams(FSETESTPARAM *pParam);
+int RedFseTestStart(const FSETESTPARAM *pParam);
+#endif
+
+#if FSIOTEST_SUPPORTED
+typedef enum
+{
+    TESTFS_RELEDGE, /* Datalight Reliance Edge */
+    TESTFS_FATFS,   /* ChaN's FatFs */
+    TESTFS_FATSL    /* FreeRTOS+FAT SL */
+} TESTFS;
+
+typedef struct
+{
+    TESTFS      testfs;                 /**< --fs */
+    const char *pszVolume;              /**< Volume path prefix. */
+    bool        fSeqRead;               /**< --seq=r */
+    bool        fSeqWrite;              /**< --seq=w */
+    bool        fSeqRewrite;            /**< --seq=e */
+    bool        fRandomRead;            /**< --rand=r */
+    bool        fRandomWrite;           /**< --rand=w */
+    bool        fMixedWrite;            /**< --mixed */
+    bool        fScanTest;              /**< --scan */
+    uint32_t    ulFSBlockSize;          /**< --block-size */
+    uint32_t    ulMaxFileSize;          /**< --max */
+    uint32_t    ulRandomReadPasses;     /**< --rand-pass=r:w (r part) */
+    uint32_t    ulRandomWritePasses;    /**< --rand-pass=r:w (w part) */
+    uint32_t    ulMixedWritePasses;     /**< --mixed-pass */
+    int32_t     iFlushOnWriteRatio;     /**< --rand-fow */
+    uint32_t    ulBufferMin;            /**< --start */
+    uint32_t    ulBufferSize;           /**< --buffer-size */
+    bool        fWriteVerify;           /**< --verify */
+    uint32_t    ulSampleRate;           /**< --sample-rate */
+    uint32_t    ulScanCount;            /**< --scan-files */
+    uint64_t    ullSeed;                /**< --seed */
+} FSIOTESTPARAM;
+
+PARAMSTATUS FSIOTestParseParams(int argc, char *argv[], FSIOTESTPARAM *pParam, uint8_t *pbVolNum, const char **ppszDevice);
+void FSIOTestDefaultParams(FSIOTESTPARAM *pParam);
+int FSIOTestStart(const FSIOTESTPARAM *pParam);
+#endif
+
+#if BDEVTEST_SUPPORTED
+typedef struct
+{
+    uint8_t     bDrvNum;        /**< Volume number (for sector size/count). */
+    bool        fSeqWrite;      /**< --seq:w */
+    bool        fSeqRead;       /**< --seq:r */
+    bool        fRandWrite;     /**< --rand:w */
+    bool        fRandRead;      /**< --rand:r */
+    uint32_t    ulSampleSecs;   /**< --sample-rate */
+    uint32_t    ulPasses;       /**< --passes */
+    uint32_t    ulMinIOSectors; /**< --count=min[:max] (min part) */
+    uint32_t    ulMaxIOSectors; /**< --count=min[:max] (max part) */
+    uint32_t    ulMaxSizeKB;    /**< --max */
+    uint32_t    ulTestSeconds;  /**< --time */
+    bool        fVerify;        /**< --verify */
+    bool        fAsyncWrites;   /**< --async */
+    uint64_t    ullSeed;        /**< --seed */
+} BDEVTESTPARAM;
+
+PARAMSTATUS BDevTestParseParams(int argc, char *argv[], BDEVTESTPARAM *pParam, uint8_t *pbVolNum, const char **ppszDevice);
+void BDevTestDefaultParams(BDEVTESTPARAM *pParam);
+int BDevTestStart(const BDEVTESTPARAM *pParam);
+#endif
+
+#if DISKFULL_TEST_SUPPORTED
+typedef struct
+{
+    const char *pszVolume;      /**< Volume path prefix. */
+    bool        fQuitOnFailure; /**< --quit-on-failure */
+    bool        fDebugErrors;   /**< --debug */
+} DISKFULLTESTPARAM;
+
+PARAMSTATUS DiskFullTestParseParams(int argc, char *argv[], DISKFULLTESTPARAM *pParam, uint8_t *pbVolNum, const char **ppszDevice);
+void DiskFullTestDefaultParams(DISKFULLTESTPARAM *pParam);
+int DiskFullTestStart(const DISKFULLTESTPARAM *pParam);
+#endif
+
+
+#endif
+
index de4a1a1a1becff154525e80c081e9fad8127c372..9cfb8425914c0208dea627e212663abb0124397a 100644 (file)
@@ -33,7 +33,7 @@
 
     <!-- This macro is updated automatically: do not edit! -->
 */
-#define RED_BUILD_NUMBER "664"
+#define RED_BUILD_NUMBER "677"
 
 #define RED_KIT_GPL         0U  /* Open source GPL kit. */
 #define RED_KIT_COMMERCIAL  1U  /* Commercially-licensed kit. */
@@ -48,7 +48,7 @@
 
 /** @brief Version number to display in output.
 */
-#define RED_VERSION "v1.0"
+#define RED_VERSION "v1.0.4"
 
 
 /** @brief On-disk version number.
 
 /** @brief Full product name and version.
 */
-#define RED_PRODUCT_NAME "Datalight "RED_PRODUCT_BASE_NAME" "RED_VERSION" Build "RED_BUILD_NUMBER ALPHABETA
+#define RED_PRODUCT_NAME "Datalight " RED_PRODUCT_BASE_NAME " " RED_VERSION " Build " RED_BUILD_NUMBER ALPHABETA
 
 
 /** @brief Product copyright.
 */
-#define RED_PRODUCT_LEGAL "Copyright (c) 2014-2015 Datalight, Inc.  All Rights Reserved Worldwide."
+#define RED_PRODUCT_LEGAL "Copyright (c) 2014-2016 Datalight, Inc.  All Rights Reserved Worldwide."
 
 
 /** @brief Product patents.
 /** @brief Product edition.
 */
 #if RED_KIT == RED_KIT_GPL
-#define RED_PRODUCT_EDITION "Open-Source GPLv2 Edition -- Compiled "__DATE__" at "__TIME__
+#define RED_PRODUCT_EDITION "Open-Source GPLv2 Edition -- Compiled " __DATE__ " at " __TIME__
 #elif RED_KIT == RED_KIT_COMMERCIAL
-#define RED_PRODUCT_EDITION "Commercial Edition -- Compiled "__DATE__" at "__TIME__
+#define RED_PRODUCT_EDITION "Commercial Edition -- Compiled " __DATE__ " at " __TIME__
 #else
-#define RED_PRODUCT_EDITION "Developer Sandbox -- Compiled "__DATE__" at "__TIME__
+#define RED_PRODUCT_EDITION "Developer Sandbox -- Compiled " __DATE__ " at " __TIME__
 #endif
 
 
index 3b1758ed404d8363b62fb2ab5066daa0a385c628..266cf5e1521c45b0f711ef476b15c04be8960108 100644 (file)
-/*             ----> DO NOT REMOVE THE FOLLOWING NOTICE <----\r
-\r
-                   Copyright (c) 2014-2015 Datalight, Inc.\r
-                       All Rights Reserved Worldwide.\r
-\r
-    This program is free software; you can redistribute it and/or modify\r
-    it under the terms of the GNU General Public License as published by\r
-    the Free Software Foundation; use version 2 of the License.\r
-\r
-    This program is distributed in the hope that it will be useful,\r
-    but "AS-IS," WITHOUT ANY WARRANTY; without even the implied warranty\r
-    of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
-    GNU General Public License for more details.\r
-\r
-    You should have received a copy of the GNU General Public License along\r
-    with this program; if not, write to the Free Software Foundation, Inc.,\r
-    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
-*/\r
-/*  Businesses and individuals that for commercial or other reasons cannot\r
-    comply with the terms of the GPLv2 license may obtain a commercial license\r
-    before incorporating Reliance Edge into proprietary software for\r
-    distribution in any form.  Visit http://www.datalight.com/reliance-edge for\r
-    more information.\r
-*/\r
-/** @file\r
-*/\r
-#ifndef REDVOLUME_H\r
-#define REDVOLUME_H\r
-\r
-\r
-/** @brief Per-volume configuration structure.\r
-\r
-    Contains the configuration values that may differ between volumes.  Must be\r
-    declared in an array in redconf.c in the Reliance Edge project directory and\r
-    statically initialized with values representing the volume configuration of\r
-    the target system.\r
-*/\r
-typedef struct\r
-{\r
-    /** The sector size for the block device underlying the volume: the basic\r
-        unit for reading and writing to the storage media.  Commonly ranges\r
-        between 512 and 4096, but any power-of-two value not greater than the\r
-        block size will work.\r
-    */\r
-    uint32_t    ulSectorSize;\r
-\r
-    /** The number of sectors in this file system volume.\r
-    */\r
-    uint64_t    ullSectorCount;\r
-\r
-    /** Whether a sector write on the block device underlying the volume is\r
-        atomic.  It is atomic if when the sector write is interrupted, the\r
-        contents of the sector are guaranteed to be either all of the new data,\r
-        or all of the old data.  If unsure, leave as false.\r
-    */\r
-    bool        fAtomicSectorWrite;\r
-\r
-    /** This is the maximum number of inodes (files and directories).  This\r
-        number includes the root directory inode (inode 2; created during\r
-        format), but does not include inodes 0 or 1, which do not exist on\r
-        disk.  The number of inodes cannot be less than 1.\r
-    */\r
-    uint32_t    ulInodeCount;\r
-\r
-  #if REDCONF_API_POSIX == 1\r
-    /** The path prefix for the volume; for example, "VOL1:", "FlashDisk", etc.\r
-    */\r
-    const char *pszPathPrefix;\r
-  #endif\r
-} VOLCONF;\r
-\r
-extern const VOLCONF gaRedVolConf[REDCONF_VOLUME_COUNT];\r
-extern const VOLCONF * CONST_IF_ONE_VOLUME gpRedVolConf;\r
-\r
-\r
-/** @brief Per-volume run-time data.\r
-*/\r
-typedef struct\r
-{\r
-    /** Whether the volume is currently mounted.\r
-    */\r
-    bool        fMounted;\r
-\r
-  #if REDCONF_READ_ONLY == 0\r
-    /** Whether the volume is read-only.\r
-    */\r
-    bool        fReadOnly;\r
-\r
-    /** The active automatic transaction mask.\r
-    */\r
-    uint32_t    ulTransMask;\r
-  #endif\r
-\r
-    /** The power of 2 difference between sector size and block size.\r
-    */\r
-    uint8_t     bBlockSectorShift;\r
-\r
-    /** The number of logical blocks in this file system volume.  The unit here\r
-        is the global block size.\r
-    */\r
-    uint32_t    ulBlockCount;\r
-\r
-    /** The total number of allocable blocks; Also the maximum count of free\r
-        blocks.\r
-    */\r
-    uint32_t    ulBlocksAllocable;\r
-\r
-    /** The maximum number of bytes that an inode is capable of addressing.\r
-    */\r
-    uint64_t    ullMaxInodeSize;\r
-\r
-    /** The current metadata sequence number.  This value is included in all\r
-        metadata nodes and incremented every time a metadata node is written.\r
-        It is assumed to never wrap around.\r
-    */\r
-    uint64_t    ullSequence;\r
-} VOLUME;\r
-\r
-/*  Array of VOLUME structures, populated at during RedCoreInit().\r
-*/\r
-extern VOLUME gaRedVolume[REDCONF_VOLUME_COUNT];\r
-\r
-/*  Volume number currently being accessed; populated during\r
-    RedCoreVolSetCurrent().\r
-*/\r
-extern CONST_IF_ONE_VOLUME uint8_t gbRedVolNum;\r
-\r
-/*  Pointer to the volume currently being accessed; populated during\r
-    RedCoreVolSetCurrent().\r
-*/\r
-extern VOLUME * CONST_IF_ONE_VOLUME gpRedVolume;\r
-\r
-#endif\r
-\r
+/*             ----> DO NOT REMOVE THE FOLLOWING NOTICE <----
+
+                   Copyright (c) 2014-2015 Datalight, Inc.
+                       All Rights Reserved Worldwide.
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; use version 2 of the License.
+
+    This program is distributed in the hope that it will be useful,
+    but "AS-IS," WITHOUT ANY WARRANTY; without even the implied warranty
+    of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+/*  Businesses and individuals that for commercial or other reasons cannot
+    comply with the terms of the GPLv2 license may obtain a commercial license
+    before incorporating Reliance Edge into proprietary software for
+    distribution in any form.  Visit http://www.datalight.com/reliance-edge for
+    more information.
+*/
+/** @file
+*/
+#ifndef REDVOLUME_H
+#define REDVOLUME_H
+
+
+/** @brief Per-volume configuration structure.
+
+    Contains the configuration values that may differ between volumes.  Must be
+    declared in an array in redconf.c in the Reliance Edge project directory and
+    statically initialized with values representing the volume configuration of
+    the target system.
+*/
+typedef struct
+{
+    /** The sector size for the block device underlying the volume: the basic
+        unit for reading and writing to the storage media.  Commonly ranges
+        between 512 and 4096, but any power-of-two value not greater than the
+        block size will work.
+    */
+    uint32_t    ulSectorSize;
+
+    /** The number of sectors in this file system volume.
+    */
+    uint64_t    ullSectorCount;
+
+    /** Whether a sector write on the block device underlying the volume is
+        atomic.  It is atomic if when the sector write is interrupted, the
+        contents of the sector are guaranteed to be either all of the new data,
+        or all of the old data.  If unsure, leave as false.
+    */
+    bool        fAtomicSectorWrite;
+
+    /** This is the maximum number of inodes (files and directories).  This
+        number includes the root directory inode (inode 2; created during
+        format), but does not include inodes 0 or 1, which do not exist on
+        disk.  The number of inodes cannot be less than 1.
+    */
+    uint32_t    ulInodeCount;
+
+    /** This is the maximum number of times a block device I/O operation will
+        be retried.  If a block device read, write, or flush fails, Reliance
+        Edge will try again up to this number of times until the operation is
+        successful.  Set this to 0 to disable retries.
+    */
+    uint8_t     bBlockIoRetries;
+
+  #if REDCONF_API_POSIX == 1
+    /** The path prefix for the volume; for example, "VOL1:", "FlashDisk", etc.
+    */
+    const char *pszPathPrefix;
+  #endif
+} VOLCONF;
+
+extern const VOLCONF gaRedVolConf[REDCONF_VOLUME_COUNT];
+extern const VOLCONF * CONST_IF_ONE_VOLUME gpRedVolConf;
+
+
+/** @brief Per-volume run-time data.
+*/
+typedef struct
+{
+    /** Whether the volume is currently mounted.
+    */
+    bool        fMounted;
+
+  #if REDCONF_READ_ONLY == 0
+    /** Whether the volume is read-only.
+    */
+    bool        fReadOnly;
+
+    /** The active automatic transaction mask.
+    */
+    uint32_t    ulTransMask;
+  #endif
+
+    /** The power of 2 difference between sector size and block size.
+    */
+    uint8_t     bBlockSectorShift;
+
+    /** The number of logical blocks in this file system volume.  The unit here
+        is the global block size.
+    */
+    uint32_t    ulBlockCount;
+
+    /** The total number of allocable blocks; Also the maximum count of free
+        blocks.
+    */
+    uint32_t    ulBlocksAllocable;
+
+    /** The maximum number of bytes that an inode is capable of addressing.
+    */
+    uint64_t    ullMaxInodeSize;
+
+    /** The current metadata sequence number.  This value is included in all
+        metadata nodes and incremented every time a metadata node is written.
+        It is assumed to never wrap around.
+    */
+    uint64_t    ullSequence;
+} VOLUME;
+
+/*  Array of VOLUME structures, populated at during RedCoreInit().
+*/
+extern VOLUME gaRedVolume[REDCONF_VOLUME_COUNT];
+
+/*  Volume number currently being accessed; populated during
+    RedCoreVolSetCurrent().
+*/
+extern CONST_IF_ONE_VOLUME uint8_t gbRedVolNum;
+
+/*  Pointer to the volume currently being accessed; populated during
+    RedCoreVolSetCurrent().
+*/
+extern VOLUME * CONST_IF_ONE_VOLUME gpRedVolume;
+
+#endif
+
index 6a8a5920f0d1a0701accf948296a47f7e316ac09..cfde3a7082d5eed853f3fd13e60746c97b40d941 100644 (file)
-/*             ----> DO NOT REMOVE THE FOLLOWING NOTICE <----\r
-\r
-                   Copyright (c) 2014-2015 Datalight, Inc.\r
-                       All Rights Reserved Worldwide.\r
-\r
-    This program is free software; you can redistribute it and/or modify\r
-    it under the terms of the GNU General Public License as published by\r
-    the Free Software Foundation; use version 2 of the License.\r
-\r
-    This program is distributed in the hope that it will be useful,\r
-    but "AS-IS," WITHOUT ANY WARRANTY; without even the implied warranty\r
-    of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
-    GNU General Public License for more details.\r
-\r
-    You should have received a copy of the GNU General Public License along\r
-    with this program; if not, write to the Free Software Foundation, Inc.,\r
-    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
-*/\r
-/*  Businesses and individuals that for commercial or other reasons cannot\r
-    comply with the terms of the GPLv2 license may obtain a commercial license\r
-    before incorporating Reliance Edge into proprietary software for\r
-    distribution in any form.  Visit http://www.datalight.com/reliance-edge for\r
-    more information.\r
-*/\r
-/** @file\r
-    @brief Macros to encapsulate MISRA C:2012 deviations in OS-specific code.\r
-*/\r
-#ifndef REDOSDEVIATIONS_H\r
-#define REDOSDEVIATIONS_H\r
-\r
-\r
-#if REDCONF_OUTPUT == 1\r
-/*  Needed for PRINT_ASSERT() and OUTPUT_CHARACTER().\r
-*/\r
-#include <stdio.h>\r
-#endif\r
-\r
-\r
-#if REDCONF_ASSERTS == 1\r
-#if REDCONF_OUTPUT == 1\r
-/** Print a formatted message for an assertion.\r
-\r
-    Usages of this macro deviate from MISRA C:2012 Rule 21.6 (required).  Using\r
-    printf() is the most convenient way to output this information; and the risk\r
-    of "unspecified, undefined and implementation-defined" behavior causing\r
-    problems (as cited in the rationale for the rule) is small.  The driver does\r
-    not depend on this string being outputted correctly.  Furthermore, use of\r
-    printf() disappears when either asserts or output are disabled.\r
-\r
-    As Rule 21.6 is required, a separate deviation record is required.\r
-*/\r
-#define PRINT_ASSERT(file, line) \\r
-    (void)printf("Assertion failed in \"%s\" at line %u\n\r", ((file) == NULL) ? "" : (file), (unsigned)(line))\r
-#else\r
-#define PRINT_ASSERT(file, line) do { (void)(file); (void)(line); } while(false)\r
-#endif /* REDCONF_OUTPUT == 1 */\r
-#endif /* REDCONF_ASSERTS == 1 */\r
-\r
-\r
-/** Cast a value to unsigned long.\r
-\r
-    Usages of this macro deviate from MISRA C:2012 Directive 4.6.  This macro is\r
-    used in two places to cast a uint64_t value (used by the block device\r
-    abstraction for sector numbers) to unsigned long, since third-party code\r
-    which is not under the control of this project uses unsigned long for sector\r
-    numbers.  The cast is guaranteed to not lose any information, since when the\r
-    disk is opened the sector count is verified to be less than or equal to an\r
-    unsigned long value.  The text of the directive mentions that "it might be\r
-    desirable not to apply this guideline when interfacing with ... code outside\r
-    the project's control", which describes the situation for this deviation.\r
-\r
-    As Directive 4.6 is advisory, a deviation record is not required.  This\r
-    notice is the only record of the deviation.\r
-*/\r
-#define CAST_ULONG(ull) ((unsigned long)(ull))\r
-\r
-\r
-/** Cast a const-qualified pointer to a pointer which is *not* const-qualified.\r
-\r
-    Usages of this macro deviate from MISRA C:2012 Rule 11.8.  This macro is\r
-    used in exactly one place in order to cope with a poorly designed\r
-    third-party interface.  Reliance Edge, at every level of the stack, uses\r
-    const-qualified pointers for buffers used in write operations, since the\r
-    data is read from the buffer, and the buffer does not need to be modified\r
-    (consistent with Rule 8.13).  One of the third-party block device interfaces\r
-    that Reliance Edge interfaces with does not follow this convention: it uses\r
-    an unqualified pointer for the buffer parameter of its sector write\r
-    function.  This forces the need for the cast to avoid warnings.  The\r
-    implementation of the sector write function is provided by the user, so it\r
-    is to be hoped that the buffer is not actually modified.\r
-\r
-    As Rule 11.8 is required, a separate deviation record is required.\r
-*/\r
-#define CAST_AWAY_CONST(type, ptr) ((type *)(ptr))\r
-\r
-\r
-/** Allocate zero-initialized (cleared) memory.\r
-\r
-    All usages of this macro deviate from MISRA C:2012 Directive 4.12 (required)\r
-    and Rule 21.3 (required).  In the context of the single place it is actually\r
-    used, this macro also deviates from Rule 22.1 (required).\r
-\r
-    This macro is used in the FreeRTOS block device code in order to allocate a\r
-    RAM disk, when that implementation of the block device is selected.  The\r
-    primary rationale for all these deviations is that a) the RAM disk cannot be\r
-    allocated statically (since the volume information is stored in a\r
-    structure), and b) the RAM disk is primarily intended as a temporary testing\r
-    tool for users who want to try out Reliance Edge before the real storage\r
-    media is available.  In most real systems, Reliance Edge is used with\r
-    non-volatile storage like SD/MMC or eMMC, not with RAM disks.\r
-\r
-    Rule 22.1 states that all resources which are allocated must also be\r
-    explicitly freed.  The RAM disk is allocated and never freed, deviating from\r
-    that rule.  This is done because the data in the RAM disk is emulating a\r
-    non-volatile storage medium, and thus needs to persist even after the block\r
-    device is closed, to allow the file system to be ormatted and then mounted,\r
-    or unmounted and remounted in the course of a test.  Thus the memory will\r
-    remain allocated until the target device is rebooted.  This is assumed to be\r
-    acceptable for the primary purpose of the RAM disk, which is preliminary\r
-    testing.\r
-\r
-    As Directive 4.12, Rule 21.3, and Rule 22.1 are all required, separate\r
-    deviation records are required.\r
-*/\r
-#define ALLOCATE_CLEARED_MEMORY(nelem, elsize) calloc(nelem, elsize)\r
-\r
-\r
-#if REDCONF_OUTPUT == 1\r
-/** Output a character to a serial port or other display device.\r
-\r
-    Usages of this macro deviate from MISRA C:2012 Rule 21.6 (required).\r
-    FreeRTOS does not include a standard method of printing characters, so\r
-    putchar() is the most convenient and portable way to accomplish the task.\r
-    The risk of "unspecified, undefined and implementation-defined" behavior\r
-    causing problems (as cited in the rationale for the rule) is small.  The\r
-    driver does not depend on the character being outputted correctly.\r
-    Furthermore, use of putchar() disappears when output is disabled.\r
-\r
-    As Rule 21.6 is required, a separate deviation record is required.\r
-*/\r
-#define OUTPUT_CHARACTER(ch) (void)putchar(ch)\r
-#endif\r
-\r
-\r
-#if (REDCONF_TASK_COUNT > 1U) && (REDCONF_API_POSIX == 1)\r
-/** Cast a TaskHandle_t (a pointer type) to uintptr_t.\r
-\r
-    Usage of this macro deivate from MISRA-C:2012 Rule 11.4 (advisory).  This\r
-    macro is used for the FreeRTOS version of RedOsTaskId().  Some RTOSes\r
-    natively use an integer for task IDs; others use pointers.  RedOsTaskId()\r
-    uses integers, FreeRTOS uses pointers; to reconcile this difference, the\r
-    pointer must be cast to integer.  This is fairly safe, since the resulting\r
-    integer is never cast back to a pointer; and although the integer\r
-    representation of a pointer is implementation-defined, the representation is\r
-    irrelevant provided that unique pointers are converted to unique integers.\r
-\r
-    As Rule 11.4 is advisory, a deviation record is not required.  This notice\r
-    is the only record of the deviation.\r
-*/\r
-#define CAST_TASK_PTR_TO_UINTPTR(taskptr) ((uintptr_t)(taskptr))\r
-#endif\r
-\r
-\r
-#endif\r
-\r
+/*             ----> DO NOT REMOVE THE FOLLOWING NOTICE <----
+
+                   Copyright (c) 2014-2015 Datalight, Inc.
+                       All Rights Reserved Worldwide.
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; use version 2 of the License.
+
+    This program is distributed in the hope that it will be useful,
+    but "AS-IS," WITHOUT ANY WARRANTY; without even the implied warranty
+    of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+/*  Businesses and individuals that for commercial or other reasons cannot
+    comply with the terms of the GPLv2 license may obtain a commercial license
+    before incorporating Reliance Edge into proprietary software for
+    distribution in any form.  Visit http://www.datalight.com/reliance-edge for
+    more information.
+*/
+/** @file
+    @brief Macros to encapsulate MISRA C:2012 deviations in OS-specific code.
+*/
+#ifndef REDOSDEVIATIONS_H
+#define REDOSDEVIATIONS_H
+
+
+#if REDCONF_OUTPUT == 1
+/*  Needed for PRINT_ASSERT() and OUTPUT_CHARACTER().
+*/
+#include <stdio.h>
+#endif
+
+
+#if (REDCONF_ASSERTS == 1) && (REDCONF_OUTPUT == 1)
+/** Print a formatted message for an assertion.
+
+    Usages of this macro deviate from MISRA C:2012 Rule 21.6 (required).  Using
+    printf() is the most convenient way to output this information; and the risk
+    of "unspecified, undefined and implementation-defined" behavior causing
+    problems (as cited in the rationale for the rule) is small.  The driver does
+    not depend on this string being outputted correctly.  Furthermore, use of
+    printf() disappears when either asserts or output are disabled.
+
+    As Rule 21.6 is required, a separate deviation record is required.
+*/
+#define PRINT_ASSERT(file, line) \
+    printf("Assertion failed in \"%s\" at line %u\n\r", ((file) == NULL) ? "" : (file), (unsigned)(line))
+#endif
+
+
+/** Cast a value to unsigned long.
+
+    Usages of this macro deviate from MISRA C:2012 Directive 4.6.  This macro is
+    used in two places to cast a uint64_t value (used by the block device
+    abstraction for sector numbers) to unsigned long, since third-party code
+    which is not under the control of this project uses unsigned long for sector
+    numbers.  The cast is guaranteed to not lose any information, since when the
+    disk is opened the sector count is verified to be less than or equal to an
+    unsigned long value.  The text of the directive mentions that "it might be
+    desirable not to apply this guideline when interfacing with ... code outside
+    the project's control", which describes the situation for this deviation.
+
+    As Directive 4.6 is advisory, a deviation record is not required.  This
+    notice is the only record of the deviation.
+*/
+#define CAST_ULONG(ull) ((unsigned long)(ull))
+
+
+/** Cast a const-qualified pointer to a pointer which is *not* const-qualified.
+
+    Usages of this macro deviate from MISRA C:2012 Rule 11.8.  This macro is
+    used in exactly one place in order to cope with a poorly designed
+    third-party interface.  Reliance Edge, at every level of the stack, uses
+    const-qualified pointers for buffers used in write operations, since the
+    data is read from the buffer, and the buffer does not need to be modified
+    (consistent with Rule 8.13).  One of the third-party block device interfaces
+    that Reliance Edge interfaces with does not follow this convention: it uses
+    an unqualified pointer for the buffer parameter of its sector write
+    function.  This forces the need for the cast to avoid warnings.  The
+    implementation of the sector write function is provided by the user, so it
+    is to be hoped that the buffer is not actually modified.
+
+    As Rule 11.8 is required, a separate deviation record is required.
+*/
+#define CAST_AWAY_CONST(type, ptr) ((type *)(ptr))
+
+
+/** Allocate zero-initialized (cleared) memory.
+
+    All usages of this macro deviate from MISRA C:2012 Directive 4.12 (required)
+    and Rule 21.3 (required).  In the context of the single place it is actually
+    used, this macro also deviates from Rule 22.1 (required).
+
+    This macro is used in the FreeRTOS block device code in order to allocate a
+    RAM disk, when that implementation of the block device is selected.  The
+    primary rationale for all these deviations is that a) the RAM disk cannot be
+    allocated statically (since the volume information is stored in a
+    structure), and b) the RAM disk is primarily intended as a temporary testing
+    tool for users who want to try out Reliance Edge before the real storage
+    media is available.  In most real systems, Reliance Edge is used with
+    non-volatile storage like SD/MMC or eMMC, not with RAM disks.
+
+    Rule 22.1 states that all resources which are allocated must also be
+    explicitly freed.  The RAM disk is allocated and never freed, deviating from
+    that rule.  This is done because the data in the RAM disk is emulating a
+    non-volatile storage medium, and thus needs to persist even after the block
+    device is closed, to allow the file system to be ormatted and then mounted,
+    or unmounted and remounted in the course of a test.  Thus the memory will
+    remain allocated until the target device is rebooted.  This is assumed to be
+    acceptable for the primary purpose of the RAM disk, which is preliminary
+    testing.
+
+    As Directive 4.12, Rule 21.3, and Rule 22.1 are all required, separate
+    deviation records are required.
+*/
+#define ALLOCATE_CLEARED_MEMORY(nelem, elsize) calloc(nelem, elsize)
+
+
+#if REDCONF_OUTPUT == 1
+/** Output a character to a serial port or other display device.
+
+    Usages of this macro deviate from MISRA C:2012 Rule 21.6 (required).
+    FreeRTOS does not include a standard method of printing characters, so
+    putchar() is the most convenient and portable way to accomplish the task.
+    The risk of "unspecified, undefined and implementation-defined" behavior
+    causing problems (as cited in the rationale for the rule) is small.  The
+    driver does not depend on the character being outputted correctly.
+    Furthermore, use of putchar() disappears when output is disabled.
+
+    As Rule 21.6 is required, a separate deviation record is required.
+*/
+#define OUTPUT_CHARACTER(ch) (void)putchar(ch)
+#endif
+
+
+#if (REDCONF_TASK_COUNT > 1U) && (REDCONF_API_POSIX == 1)
+/** Cast a TaskHandle_t (a pointer type) to uintptr_t.
+
+    Usage of this macro deivate from MISRA-C:2012 Rule 11.4 (advisory).  This
+    macro is used for the FreeRTOS version of RedOsTaskId().  Some RTOSes
+    natively use an integer for task IDs; others use pointers.  RedOsTaskId()
+    uses integers, FreeRTOS uses pointers; to reconcile this difference, the
+    pointer must be cast to integer.  This is fairly safe, since the resulting
+    integer is never cast back to a pointer; and although the integer
+    representation of a pointer is implementation-defined, the representation is
+    irrelevant provided that unique pointers are converted to unique integers.
+
+    As Rule 11.4 is advisory, a deviation record is not required.  This notice
+    is the only record of the deviation.
+*/
+#define CAST_TASK_PTR_TO_UINTPTR(taskptr) ((uintptr_t)(taskptr))
+#endif
+
+
+/** Ignore the return value of a function (cast to void)
+
+    Usages of this macro deviate from MISRA C:2012 Directive 4.7, which states
+    that error information must be checked immediately after a function returns
+    potential error information.
+
+    If asserts and output are enabled, then this macro is used to document that
+    the return value of printf() is ignored.  A failure of printf() does not
+    impact the filesystem core, nor is there anything the filesystem can do to
+    respond to such an error (especially since it occurs within an assert).
+    Thus, the most reasonable action is to ignore the error.
+
+    In the STM32 SDIO block device implementation, errors are also ignored in an
+    IRQ interrupt handler.  This is the most reasonable action to take for two
+    reasons: (a) it would be dangerous to spend processor time responding to the
+    error inside the IRQ handler; (b) it has been verified that the same error
+    is propegated to the DiskRead/Write method, which does return the error to
+    the core.
+
+    In the Atmel SD/MMC block device implementation, error information from
+    sd_mmc_read_capacity() is ignored.  This is a reasonable action because all
+    of the possible error conditions were eliminated by a previous check.
+    sd_mmc_read_capacity() fails under the same conditions as
+    sd_mmc_test_unit_ready(), which was checked ealier in the same function.
+
+    In the mutex module, error information returned from the mutex release
+    function is ignored when asserts are disabled.  This is a reasonable action
+    because the mutex release function (xSemaphoreGive) is documented only to
+    fail if the mutex was not obtained correctly, which can be demonstrably
+    avoided.
+
+    As Directive 4.7 is required, a separate deviation record is required.
+*/
+#define IGNORE_ERRORS(fn) ((void) (fn))
+
+
+/** @brief Determine whether a pointer is aligned on a 32-bit boundary.
+
+    This is used to determine whether a data buffer meets the requirements of
+    the underlying block device implementation.  When transferring data via
+    DMA (Direct Memory Access) on an STM32 device, the data buffer must be cast
+    as a uint32 pointer, and unexpected behavior may occur if the buffer is not
+    aligned correctly.
+
+    There is no way to perform this check without deviating from MISRA C rules
+    against casting pointers to integer types.  Usage of this macro deviates
+    from MISRA C:2012 Rule 11.4 (advisory).  The main rationale the rule cites
+    against converting pointers to integers is that the chosen integer type may
+    not be able to represent the pointer; this is a non-issue here since we use
+    uintptr_t.  The text says the rule still applies when using uintptr_t due to
+    concern about unaligned pointers, but that is not an issue here since the
+    integer value of the pointer is not saved and not converted back into a
+    pointer and dereferenced.  The result of casting a pointer to a sufficiently
+    large integer is implementation-defined, but macros similar to this one have
+    been used by Datalight for a long time in a wide variety of environments and
+    they have always worked as expected.
+
+    This deviation only occurs when using the STM32 SDIO block device
+    implementation.
+
+    As Rule 11.4 is advisory, a deviation record is not required.  This notice
+    is the only record of deviation.
+*/
+#define IS_UINT32_ALIGNED_PTR(ptr) (((uintptr_t)(ptr) & (sizeof(uint32_t) - 1U)) == 0U)
+
+
+/** @brief Cast a 32-bit aligned void pointer to a uint32 pointer.
+
+    Usages of this macro deviate from MISRA C:2012 Rule 11.5 (advisory).  A
+    cast from a void pointer to an object pointer is discouraged because of
+    potential alignment issues.  However, this macro is only used to cast
+    pointers that have already been tested to be 32-bit aligned, so the
+    operation will be safe.
+
+    This deviation only occurs when using the STM32 SDIO block device
+    implementation.
+
+    As rule 11.5 is advisory, a deviation record is not required.  This notice
+    is the only record of the deviation.
+*/
+#define CAST_UINT32_PTR(ptr) ((uint32_t *) (ptr))
+
+
+#endif
+
index 3b322cee835b9fc3ca84384a74c8e8cf3b5275ae..9822caf5c47f4bc2930319282bf5b6b495594ce3 100644 (file)
@@ -1,54 +1,56 @@
-/*             ----> DO NOT REMOVE THE FOLLOWING NOTICE <----\r
-\r
-                   Copyright (c) 2014-2015 Datalight, Inc.\r
-                       All Rights Reserved Worldwide.\r
-\r
-    This program is free software; you can redistribute it and/or modify\r
-    it under the terms of the GNU General Public License as published by\r
-    the Free Software Foundation; use version 2 of the License.\r
-\r
-    This program is distributed in the hope that it will be useful,\r
-    but "AS-IS," WITHOUT ANY WARRANTY; without even the implied warranty\r
-    of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
-    GNU General Public License for more details.\r
-\r
-    You should have received a copy of the GNU General Public License along\r
-    with this program; if not, write to the Free Software Foundation, Inc.,\r
-    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
-*/\r
-/*  Businesses and individuals that for commercial or other reasons cannot\r
-    comply with the terms of the GPLv2 license may obtain a commercial license\r
-    before incorporating Reliance Edge into proprietary software for\r
-    distribution in any form.  Visit http://www.datalight.com/reliance-edge for\r
-    more information.\r
-*/\r
-/** @file\r
-    @brief Implements assertion handling.\r
-*/\r
-#include <redfs.h>\r
-\r
-#if REDCONF_ASSERTS == 1\r
-\r
-#include <redosdeviations.h>\r
-\r
-\r
-/** @brief Invoke the native assertion handler.\r
-\r
-    @param pszFileName  Null-terminated string containing the name of the file\r
-                        where the assertion fired.\r
-    @param ulLineNum    Line number in @p pszFileName where the assertion\r
-                        fired.\r
-*/\r
-void RedOsAssertFail(\r
-    const char *pszFileName,\r
-    uint32_t    ulLineNum)\r
-{\r
-    PRINT_ASSERT(pszFileName, ulLineNum);\r
-\r
-    for( ;; )\r
-    {\r
-    }\r
-}\r
-\r
-#endif\r
-\r
+/*             ----> DO NOT REMOVE THE FOLLOWING NOTICE <----
+
+                   Copyright (c) 2014-2015 Datalight, Inc.
+                       All Rights Reserved Worldwide.
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; use version 2 of the License.
+
+    This program is distributed in the hope that it will be useful,
+    but "AS-IS," WITHOUT ANY WARRANTY; without even the implied warranty
+    of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+/*  Businesses and individuals that for commercial or other reasons cannot
+    comply with the terms of the GPLv2 license may obtain a commercial license
+    before incorporating Reliance Edge into proprietary software for
+    distribution in any form.  Visit http://www.datalight.com/reliance-edge for
+    more information.
+*/
+/** @file
+    @brief Implements assertion handling.
+*/
+#include <redfs.h>
+
+#if REDCONF_ASSERTS == 1
+
+#include <redosdeviations.h>
+
+
+/** @brief Invoke the native assertion handler.
+
+    @param pszFileName  Null-terminated string containing the name of the file
+                        where the assertion fired.
+    @param ulLineNum    Line number in @p pszFileName where the assertion
+                        fired.
+*/
+void RedOsAssertFail(
+    const char *pszFileName,
+    uint32_t    ulLineNum)
+{
+  #if REDCONF_OUTPUT == 1
+    IGNORE_ERRORS(PRINT_ASSERT(pszFileName, ulLineNum));
+  #endif
+
+    while(true)
+    {
+    }
+}
+
+#endif
+
index c486e7c7ebb381e860126314447b5c6d8ae8322d..8fa1fcdaf891b422f903c7fe1aa4df6e098b19f5 100644 (file)
-/*             ----> DO NOT REMOVE THE FOLLOWING NOTICE <----\r
-\r
-                   Copyright (c) 2014-2015 Datalight, Inc.\r
-                       All Rights Reserved Worldwide.\r
-\r
-    This program is free software; you can redistribute it and/or modify\r
-    it under the terms of the GNU General Public License as published by\r
-    the Free Software Foundation; use version 2 of the License.\r
-\r
-    This program is distributed in the hope that it will be useful,\r
-    but "AS-IS," WITHOUT ANY WARRANTY; without even the implied warranty\r
-    of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
-    GNU General Public License for more details.\r
-\r
-    You should have received a copy of the GNU General Public License along\r
-    with this program; if not, write to the Free Software Foundation, Inc.,\r
-    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
-*/\r
-/*  Businesses and individuals that for commercial or other reasons cannot\r
-    comply with the terms of the GPLv2 license may obtain a commercial license\r
-    before incorporating Reliance Edge into proprietary software for\r
-    distribution in any form.  Visit http://www.datalight.com/reliance-edge for\r
-    more information.\r
-*/\r
-/** @file\r
-    @brief Implements block device I/O.\r
-*/\r
-#include <FreeRTOS.h>\r
-\r
-#include <redfs.h>\r
-#include <redvolume.h>\r
-#include <redosdeviations.h>\r
-\r
-\r
-/*------------------------------------------------------------------------------\r
-    Porting Note:\r
-\r
-    Several example implementations of this module for FreeRTOS are available.\r
-    If you are lucky, you can use one of these implementations; otherwise, these\r
-    can serve as examples of how to implement this service.\r
-------------------------------------------------------------------------------*/\r
-\r
-/** @brief The F_DRIVER example implementation.\r
-\r
-    This implementation is designed to reuse an existing block device driver\r
-    that was written for FreeRTOS+FAT SL.  If you have such a driver, with\r
-    little work it can be "dropped in" and used for Reliance Edge.  The only\r
-    customization required is that gpfnRedOsBDevInit needs to be defined and\r
-    pointed at the F_DRIVERINIT function.  This can be done in this module or in\r
-    another C file.\r
-\r
-    The disadantage of using the FreeRTOS F_DRIVER functions is that they only\r
-    support single-sector reads and writes.  Reliance Edge will issue\r
-    multi-sector requests, and servicing these one sector at a time will\r
-    significantly slow down the file system.\r
-*/\r
-#define BDEV_F_DRIVER       0U\r
-\r
-/** @brief The FatFs example implementation.\r
-\r
-    This implementation is designed to reuse an existing block device driver\r
-    that was written for FatFs.  If you have such a driver, it can be linked\r
-    in and used immediately.  The FatFs `diskio.h` header must be in the include\r
-    directory path.\r
-*/\r
-#define BDEV_FATFS          1U\r
-\r
-/** @brief The Atmel Studio Framework SD/MMC driver example implementation.\r
-\r
-    This implementation uses a modified version of the open source SD/MMC driver\r
-    included in the Atmel Studio Framework (ASF) and will work as-is for many\r
-    varieties of Atmel hardware.  This example assumes relatively minor\r
-    modifications to the ASF SD/MMC driver to make it support multi-sector read\r
-    and write requests, which greatly improves performance.  The modified driver\r
-    is distributed with Reliance Edge and is included in FreeRTOS Atmel projects\r
-    (such as in projects/freertos/atmel/sam4e-ek/src/ASF).\r
-\r
-    This example can easily be modified to work with an unmodified version of\r
-    the ASF SD/MMC driver.  Simply replace sd_mmc_mem_2_ram_multi() and\r
-    sd_mmc_ram_2_mem_multi() with sd_mmc_mem_2_ram() and sd_mmc_ram_2_mem()\r
-    respectively, and add a for loop to loop over each sector in the request.\r
-    However, as described in the manual, there are considerable performance\r
-    advantages to issuing real multi-sector requests, so using the modified\r
-    driver is recommended.\r
-*/\r
-#define BDEV_ATMEL_SDMMC    2U\r
-\r
-/** @brief The RAM disk example implementation.\r
-\r
-    This implementation uses a RAM disk.  It will allow you to compile and test\r
-    Reliance Edge even if your storage driver is not yet ready.  On typical\r
-    target hardware, the amount of spare RAM will be limited so generally only\r
-    very small disks will be available.\r
-*/\r
-#define BDEV_RAM_DISK       3U\r
-\r
-/** @brief Pick which example implementation is compiled.\r
-\r
-    Must be one of:\r
-    - #BDEV_F_DRIVER\r
-    - #BDEV_FATFS\r
-    - #BDEV_ATMEL_SDMMC\r
-    - #BDEV_RAM_DISK\r
-*/\r
-#define BDEV_EXAMPLE_IMPLEMENTATION BDEV_RAM_DISK\r
-\r
-\r
-static REDSTATUS DiskOpen(uint8_t bVolNum, BDEVOPENMODE mode);\r
-static REDSTATUS DiskClose(uint8_t bVolNum);\r
-static REDSTATUS DiskRead(uint8_t bVolNum, uint64_t ullSectorStart, uint32_t ulSectorCount, void *pBuffer);\r
-#if REDCONF_READ_ONLY == 0\r
-static REDSTATUS DiskWrite(uint8_t bVolNum, uint64_t ullSectorStart, uint32_t ulSectorCount, const void *pBuffer);\r
-static REDSTATUS DiskFlush(uint8_t bVolNum);\r
-#endif\r
-\r
-\r
-/** @brief Initialize a block device.\r
-\r
-    This function is called when the file system needs access to a block\r
-    device.\r
-\r
-    Upon successful return, the block device should be fully initialized and\r
-    ready to service read/write/flush/close requests.\r
-\r
-    The behavior of calling this function on a block device which is already\r
-    open is undefined.\r
-\r
-    @param bVolNum  The volume number of the volume whose block device is being\r
-                    initialized.\r
-    @param mode     The open mode, indicating the type of access required.\r
-\r
-    @return A negated ::REDSTATUS code indicating the operation result.\r
-\r
-    @retval 0           Operation was successful.\r
-    @retval -RED_EINVAL @p bVolNum is an invalid volume number.\r
-    @retval -RED_EIO    A disk I/O error occurred.\r
-*/\r
-REDSTATUS RedOsBDevOpen(\r
-    uint8_t         bVolNum,\r
-    BDEVOPENMODE    mode)\r
-{\r
-    REDSTATUS       ret;\r
-\r
-    if(bVolNum >= REDCONF_VOLUME_COUNT)\r
-    {\r
-        ret = -RED_EINVAL;\r
-    }\r
-    else\r
-    {\r
-        ret = DiskOpen(bVolNum, mode);\r
-    }\r
-\r
-    return ret;\r
-}\r
-\r
-\r
-/** @brief Uninitialize a block device.\r
-\r
-    This function is called when the file system no longer needs access to a\r
-    block device.  If any resource were allocated by RedOsBDevOpen() to service\r
-    block device requests, they should be freed at this time.\r
-\r
-    Upon successful return, the block device must be in such a state that it\r
-    can be opened again.\r
-\r
-    The behavior of calling this function on a block device which is already\r
-    closed is undefined.\r
-\r
-    @param bVolNum  The volume number of the volume whose block device is being\r
-                    uninitialized.\r
-\r
-    @return A negated ::REDSTATUS code indicating the operation result.\r
-\r
-    @retval 0           Operation was successful.\r
-    @retval -RED_EINVAL @p bVolNum is an invalid volume number.\r
-*/\r
-REDSTATUS RedOsBDevClose(\r
-    uint8_t     bVolNum)\r
-{\r
-    REDSTATUS   ret;\r
-\r
-    if(bVolNum >= REDCONF_VOLUME_COUNT)\r
-    {\r
-        ret = -RED_EINVAL;\r
-    }\r
-    else\r
-    {\r
-        ret = DiskClose(bVolNum);\r
-    }\r
-\r
-    return ret;\r
-}\r
-\r
-\r
-/** @brief Read sectors from a physical block device.\r
-\r
-    The behavior of calling this function is undefined if the block device is\r
-    closed or if it was opened with ::BDEV_O_WRONLY.\r
-\r
-    @param bVolNum          The volume number of the volume whose block device\r
-                            is being read from.\r
-    @param ullSectorStart   The starting sector number.\r
-    @param ulSectorCount    The number of sectors to read.\r
-    @param pBuffer          The buffer into which to read the sector data.\r
-\r
-    @return A negated ::REDSTATUS code indicating the operation result.\r
-\r
-    @retval 0           Operation was successful.\r
-    @retval -RED_EINVAL @p bVolNum is an invalid volume number, @p pBuffer is\r
-                        `NULL`, or @p ullStartSector and/or @p ulSectorCount\r
-                        refer to an invalid range of sectors.\r
-    @retval -RED_EIO    A disk I/O error occurred.\r
-*/\r
-REDSTATUS RedOsBDevRead(\r
-    uint8_t     bVolNum,\r
-    uint64_t    ullSectorStart,\r
-    uint32_t    ulSectorCount,\r
-    void       *pBuffer)\r
-{\r
-    REDSTATUS   ret = 0;\r
-\r
-    if(    (bVolNum >= REDCONF_VOLUME_COUNT)\r
-        || (ullSectorStart >= gaRedVolConf[bVolNum].ullSectorCount)\r
-        || ((gaRedVolConf[bVolNum].ullSectorCount - ullSectorStart) < ulSectorCount)\r
-        || (pBuffer == NULL))\r
-    {\r
-        ret = -RED_EINVAL;\r
-    }\r
-    else\r
-    {\r
-        ret = DiskRead(bVolNum, ullSectorStart, ulSectorCount, pBuffer);\r
-    }\r
-\r
-    return ret;\r
-}\r
-\r
-\r
-#if REDCONF_READ_ONLY == 0\r
-/** @brief Write sectors to a physical block device.\r
-\r
-    The behavior of calling this function is undefined if the block device is\r
-    closed or if it was opened with ::BDEV_O_RDONLY.\r
-\r
-    @param bVolNum          The volume number of the volume whose block device\r
-                            is being written to.\r
-    @param ullSectorStart   The starting sector number.\r
-    @param ulSectorCount    The number of sectors to write.\r
-    @param pBuffer          The buffer from which to write the sector data.\r
-\r
-    @return A negated ::REDSTATUS code indicating the operation result.\r
-\r
-    @retval 0           Operation was successful.\r
-    @retval -RED_EINVAL @p bVolNum is an invalid volume number, @p pBuffer is\r
-                        `NULL`, or @p ullStartSector and/or @p ulSectorCount\r
-                        refer to an invalid range of sectors.\r
-    @retval -RED_EIO    A disk I/O error occurred.\r
-*/\r
-REDSTATUS RedOsBDevWrite(\r
-    uint8_t     bVolNum,\r
-    uint64_t    ullSectorStart,\r
-    uint32_t    ulSectorCount,\r
-    const void *pBuffer)\r
-{\r
-    REDSTATUS   ret = 0;\r
-\r
-    if(    (bVolNum >= REDCONF_VOLUME_COUNT)\r
-        || (ullSectorStart >= gaRedVolConf[bVolNum].ullSectorCount)\r
-        || ((gaRedVolConf[bVolNum].ullSectorCount - ullSectorStart) < ulSectorCount)\r
-        || (pBuffer == NULL))\r
-    {\r
-        ret = -RED_EINVAL;\r
-    }\r
-    else\r
-    {\r
-        ret = DiskWrite(bVolNum, ullSectorStart, ulSectorCount, pBuffer);\r
-    }\r
-\r
-    return ret;\r
-}\r
-\r
-\r
-/** @brief Flush any caches beneath the file system.\r
-\r
-    This function must synchronously flush all software and hardware caches\r
-    beneath the file system, ensuring that all sectors written previously are\r
-    committed to permanent storage.\r
-\r
-    If the environment has no caching beneath the file system, the\r
-    implementation of this function can do nothing and return success.\r
-\r
-    The behavior of calling this function is undefined if the block device is\r
-    closed or if it was opened with ::BDEV_O_RDONLY.\r
-\r
-    @param bVolNum  The volume number of the volume whose block device is being\r
-                    flushed.\r
-\r
-    @return A negated ::REDSTATUS code indicating the operation result.\r
-\r
-    @retval 0           Operation was successful.\r
-    @retval -RED_EINVAL @p bVolNum is an invalid volume number.\r
-    @retval -RED_EIO    A disk I/O error occurred.\r
-*/\r
-REDSTATUS RedOsBDevFlush(\r
-    uint8_t     bVolNum)\r
-{\r
-    REDSTATUS   ret;\r
-\r
-    if(bVolNum >= REDCONF_VOLUME_COUNT)\r
-    {\r
-        ret = -RED_EINVAL;\r
-    }\r
-    else\r
-    {\r
-        ret = DiskFlush(bVolNum);\r
-    }\r
-\r
-    return ret;\r
-}\r
-#endif /* REDCONF_READ_ONLY == 0 */\r
-\r
-\r
-#if BDEV_EXAMPLE_IMPLEMENTATION == BDEV_F_DRIVER\r
-\r
-#include <api_mdriver.h>\r
-\r
-\r
-/*  This must be declared and initialized elsewere (e.g., in project code) to\r
-    point at the initialization function for the F_DRIVER block device.\r
-*/\r
-extern const F_DRIVERINIT gpfnRedOsBDevInit;\r
-\r
-static F_DRIVER *gapFDriver[REDCONF_VOLUME_COUNT];\r
-\r
-\r
-/** @brief Initialize a disk.\r
-\r
-    @param bVolNum  The volume number of the volume whose block device is being\r
-                    initialized.\r
-    @param mode     The open mode, indicating the type of access required.\r
-\r
-    @return A negated ::REDSTATUS code indicating the operation result.\r
-\r
-    @retval 0           Operation was successful.\r
-    @retval -RED_EIO    A disk I/O error occurred.\r
-*/\r
-static REDSTATUS DiskOpen(\r
-    uint8_t         bVolNum,\r
-    BDEVOPENMODE    mode)\r
-{\r
-    REDSTATUS       ret;\r
-\r
-    (void)mode;\r
-\r
-    if((gpfnRedOsBDevInit == NULL) || (gapFDriver[bVolNum] != NULL))\r
-    {\r
-        ret = -RED_EINVAL;\r
-    }\r
-    else\r
-    {\r
-        F_DRIVER *pDriver;\r
-\r
-        pDriver = gpfnRedOsBDevInit(bVolNum);\r
-        if(pDriver != NULL)\r
-        {\r
-            F_PHY   geom;\r
-            int     iErr;\r
-\r
-            /*  Validate that the geometry is consistent with the volume\r
-                configuration.\r
-            */\r
-            iErr = pDriver->getphy(pDriver, &geom);\r
-            if(iErr == 0)\r
-            {\r
-                if(    (geom.bytes_per_sector != gaRedVolConf[bVolNum].ulSectorSize)\r
-                    || (geom.number_of_sectors < gaRedVolConf[bVolNum].ullSectorCount))\r
-                {\r
-                    ret = -RED_EINVAL;\r
-                }\r
-                else\r
-                {\r
-                    gapFDriver[bVolNum] = pDriver;\r
-                    ret = 0;\r
-                }\r
-            }\r
-            else\r
-            {\r
-                ret = -RED_EIO;\r
-            }\r
-\r
-            if(ret != 0)\r
-            {\r
-                pDriver->release(pDriver);\r
-            }\r
-        }\r
-        else\r
-        {\r
-            ret = -RED_EIO;\r
-        }\r
-    }\r
-\r
-    return ret;\r
-}\r
-\r
-\r
-/** @brief Uninitialize a disk.\r
-\r
-    @param bVolNum  The volume number of the volume whose block device is being\r
-                    uninitialized.\r
-\r
-    @return A negated ::REDSTATUS code indicating the operation result.\r
-\r
-    @retval 0   Operation was successful.\r
-*/\r
-static REDSTATUS DiskClose(\r
-    uint8_t     bVolNum)\r
-{\r
-    REDSTATUS   ret;\r
-\r
-    if(gapFDriver[bVolNum] == NULL)\r
-    {\r
-        ret = -RED_EINVAL;\r
-    }\r
-    else\r
-    {\r
-        gapFDriver[bVolNum]->release(gapFDriver[bVolNum]);\r
-        gapFDriver[bVolNum] = NULL;\r
-\r
-        ret = 0;\r
-    }\r
-\r
-    return ret;\r
-}\r
-\r
-\r
-/** @brief Read sectors from a disk.\r
-\r
-    @param bVolNum          The volume number of the volume whose block device\r
-                            is being read from.\r
-    @param ullSectorStart   The starting sector number.\r
-    @param ulSectorCount    The number of sectors to read.\r
-    @param pBuffer          The buffer into which to read the sector data.\r
-\r
-    @return A negated ::REDSTATUS code indicating the operation result.\r
-\r
-    @retval 0           Operation was successful.\r
-    @retval -RED_EIO    A disk I/O error occurred.\r
-*/\r
-static REDSTATUS DiskRead(\r
-    uint8_t     bVolNum,\r
-    uint64_t    ullSectorStart,\r
-    uint32_t    ulSectorCount,\r
-    void       *pBuffer)\r
-{\r
-    REDSTATUS   ret = 0;\r
-    F_DRIVER   *pDriver = gapFDriver[bVolNum];\r
-\r
-    if(pDriver == NULL)\r
-    {\r
-        ret = -RED_EINVAL;\r
-    }\r
-    else\r
-    {\r
-        uint8_t    *pbBuffer = CAST_VOID_PTR_TO_UINT8_PTR(pBuffer);\r
-        uint32_t    ulSectorSize = gaRedVolConf[bVolNum].ulSectorSize;\r
-        uint32_t    ulSectorIdx;\r
-        int         iErr;\r
-\r
-        for(ulSectorIdx = 0U; ulSectorIdx < ulSectorCount; ulSectorIdx++)\r
-        {\r
-            iErr = pDriver->readsector(pDriver, &pbBuffer[ulSectorIdx * ulSectorSize],\r
-                                       CAST_ULONG(ullSectorStart + ulSectorCount));\r
-            if(iErr != 0)\r
-            {\r
-                ret = -RED_EIO;\r
-                break;\r
-            }\r
-        }\r
-    }\r
-\r
-    return ret;\r
-}\r
-\r
-\r
-#if REDCONF_READ_ONLY == 0\r
-/** @brief Write sectors to a disk.\r
-\r
-    @param bVolNum          The volume number of the volume whose block device\r
-                            is being written to.\r
-    @param ullSectorStart   The starting sector number.\r
-    @param ulSectorCount    The number of sectors to write.\r
-    @param pBuffer          The buffer from which to write the sector data.\r
-\r
-    @return A negated ::REDSTATUS code indicating the operation result.\r
-\r
-    @retval 0           Operation was successful.\r
-    @retval -RED_EINVAL The block device is not open.\r
-    @retval -RED_EIO    A disk I/O error occurred.\r
-*/\r
-static REDSTATUS DiskWrite(\r
-    uint8_t     bVolNum,\r
-    uint64_t    ullSectorStart,\r
-    uint32_t    ulSectorCount,\r
-    const void *pBuffer)\r
-{\r
-    REDSTATUS   ret = 0;\r
-    F_DRIVER   *pDriver = gapFDriver[bVolNum];\r
-\r
-    if(pDriver == NULL)\r
-    {\r
-        ret = -RED_EINVAL;\r
-    }\r
-    else\r
-    {\r
-        const uint8_t  *pbBuffer = CAST_VOID_PTR_TO_CONST_UINT8_PTR(pBuffer);\r
-        uint32_t        ulSectorSize = gaRedVolConf[bVolNum].ulSectorSize;\r
-        uint32_t        ulSectorIdx;\r
-        int             iErr;\r
-\r
-        for(ulSectorIdx = 0U; ulSectorIdx < ulSectorCount; ulSectorIdx++)\r
-        {\r
-            /*  We have to cast pbBuffer to non-const since the writesector\r
-                prototype is flawed, using a non-const pointer for the buffer.\r
-            */\r
-            iErr = pDriver->writesector(pDriver, CAST_AWAY_CONST(uint8_t, &pbBuffer[ulSectorIdx * ulSectorSize]),\r
-                                        CAST_ULONG(ullSectorStart + ulSectorCount));\r
-            if(iErr != 0)\r
-            {\r
-                ret = -RED_EIO;\r
-                break;\r
-            }\r
-        }\r
-    }\r
-\r
-    return ret;\r
-}\r
-\r
-\r
-/** @brief Flush any caches beneath the file system.\r
-\r
-    @param bVolNum  The volume number of the volume whose block device is being\r
-                    flushed.\r
-\r
-    @return A negated ::REDSTATUS code indicating the operation result.\r
-\r
-    @retval 0   Operation was successful.\r
-*/\r
-static REDSTATUS DiskFlush(\r
-    uint8_t     bVolNum)\r
-{\r
-    REDSTATUS   ret;\r
-\r
-    if(gapFDriver[bVolNum] == NULL)\r
-    {\r
-        ret = -RED_EINVAL;\r
-    }\r
-    else\r
-    {\r
-        /*  The F_DRIVER interface does not include a flush function, so to be\r
-            reliable the F_DRIVER implementation must use synchronous writes.\r
-        */\r
-        ret = 0;\r
-    }\r
-\r
-    return ret;\r
-}\r
-#endif /* REDCONF_READ_ONLY == 0 */\r
-\r
-\r
-#elif BDEV_EXAMPLE_IMPLEMENTATION == BDEV_FATFS\r
-\r
-#include <task.h>\r
-#include <diskio.h>\r
-\r
-/*  disk_read() and disk_write() use an unsigned 8-bit value to specify the\r
-    sector count, so no transfer can be larger than 255 sectors.\r
-*/\r
-#define MAX_SECTOR_TRANSFER UINT8_MAX\r
-\r
-\r
-/** @brief Initialize a disk.\r
-\r
-    @param bVolNum  The volume number of the volume whose block device is being\r
-                    initialized.\r
-    @param mode     The open mode, indicating the type of access required.\r
-\r
-    @return A negated ::REDSTATUS code indicating the operation result.\r
-\r
-    @retval 0           Operation was successful.\r
-    @retval -RED_EIO    A disk I/O error occurred.\r
-*/\r
-static REDSTATUS DiskOpen(\r
-    uint8_t         bVolNum,\r
-    BDEVOPENMODE    mode)\r
-{\r
-    DSTATUS         status;\r
-    uint32_t        ulTries;\r
-    REDSTATUS       ret = 0;\r
-\r
-    /*  With some implementations of disk_initialize(), such as the one\r
-        implemented by Atmel for the ASF, the first time the disk is opened, the\r
-        SD card can take a while to get ready, in which time disk_initialize()\r
-        returns an error.  Try numerous times, waiting half a second after each\r
-        failure.  Empirically, this has been observed to succeed on the second\r
-        try, so trying 10x more than that provides a margin of error.\r
-    */\r
-    for(ulTries = 0U; ulTries < 20U; ulTries++)\r
-    {\r
-        /*  Assuming that the volume number is also the correct drive number.\r
-            If this is not the case in your environment, a static constant array\r
-            can be declared to map volume numbers to the correct driver number.\r
-        */\r
-        status = disk_initialize(bVolNum);\r
-        if(status == 0)\r
-        {\r
-            break;\r
-        }\r
-\r
-        vTaskDelay(500U / portTICK_PERIOD_MS);\r
-    }\r
-\r
-    if(status != 0)\r
-    {\r
-        ret = -RED_EIO;\r
-    }\r
-\r
-    /*  Retrieve the sector size and sector count to ensure they are compatible\r
-        with our compile-time geometry.\r
-    */\r
-    if(ret == 0)\r
-    {\r
-        WORD    wSectorSize;\r
-        DWORD   dwSectorCount;\r
-        DRESULT result;\r
-\r
-        result = disk_ioctl(bVolNum, GET_SECTOR_SIZE, &wSectorSize);\r
-        if(result == RES_OK)\r
-        {\r
-            result = disk_ioctl(bVolNum, GET_SECTOR_COUNT, &dwSectorCount);\r
-            if(result == RES_OK)\r
-            {\r
-                if(    (wSectorSize != gaRedVolConf[bVolNum].ulSectorSize)\r
-                    || (dwSectorCount < gaRedVolConf[bVolNum].ullSectorCount))\r
-                {\r
-                    ret = -RED_EINVAL;\r
-                }\r
-            }\r
-            else\r
-            {\r
-                ret = -RED_EIO;\r
-            }\r
-        }\r
-        else\r
-        {\r
-            ret = -RED_EIO;\r
-        }\r
-    }\r
-\r
-    return ret;\r
-}\r
-\r
-\r
-/** @brief Uninitialize a disk.\r
-\r
-    @param bVolNum  The volume number of the volume whose block device is being\r
-                    uninitialized.\r
-\r
-    @return A negated ::REDSTATUS code indicating the operation result.\r
-\r
-    @retval 0   Operation was successful.\r
-*/\r
-static REDSTATUS DiskClose(\r
-    uint8_t     bVolNum)\r
-{\r
-    (void)bVolNum;\r
-    return 0;\r
-}\r
-\r
-\r
-/** @brief Read sectors from a disk.\r
-\r
-    @param bVolNum          The volume number of the volume whose block device\r
-                            is being read from.\r
-    @param ullSectorStart   The starting sector number.\r
-    @param ulSectorCount    The number of sectors to read.\r
-    @param pBuffer          The buffer into which to read the sector data.\r
-\r
-    @return A negated ::REDSTATUS code indicating the operation result.\r
-\r
-    @retval 0           Operation was successful.\r
-    @retval -RED_EIO    A disk I/O error occurred.\r
-*/\r
-static REDSTATUS DiskRead(\r
-    uint8_t     bVolNum,\r
-    uint64_t    ullSectorStart,\r
-    uint32_t    ulSectorCount,\r
-    void       *pBuffer)\r
-{\r
-    REDSTATUS   ret = 0;\r
-    uint32_t    ulSectorIdx = 0U;\r
-    uint32_t    ulSectorSize = gaRedVolConf[bVolNum].ulSectorSize;\r
-    uint8_t    *pbBuffer = CAST_VOID_PTR_TO_UINT8_PTR(pBuffer);\r
-\r
-    while(ulSectorIdx < ulSectorCount)\r
-    {\r
-        uint32_t    ulTransfer = REDMIN(ulSectorCount - ulSectorIdx, MAX_SECTOR_TRANSFER);\r
-        DRESULT     result;\r
-\r
-        result = disk_read(bVolNum, &pbBuffer[ulSectorIdx * ulSectorSize], (DWORD)(ullSectorStart + ulSectorIdx), (BYTE)ulTransfer);\r
-        if(result != RES_OK)\r
-        {\r
-            ret = -RED_EIO;\r
-            break;\r
-        }\r
-\r
-        ulSectorIdx += ulTransfer;\r
-    }\r
-\r
-    return ret;\r
-}\r
-\r
-\r
-#if REDCONF_READ_ONLY == 0\r
-/** @brief Write sectors to a disk.\r
-\r
-    @param bVolNum          The volume number of the volume whose block device\r
-                            is being written to.\r
-    @param ullSectorStart   The starting sector number.\r
-    @param ulSectorCount    The number of sectors to write.\r
-    @param pBuffer          The buffer from which to write the sector data.\r
-\r
-    @return A negated ::REDSTATUS code indicating the operation result.\r
-\r
-    @retval 0           Operation was successful.\r
-    @retval -RED_EIO    A disk I/O error occurred.\r
-*/\r
-static REDSTATUS DiskWrite(\r
-    uint8_t         bVolNum,\r
-    uint64_t        ullSectorStart,\r
-    uint32_t        ulSectorCount,\r
-    const void     *pBuffer)\r
-{\r
-    REDSTATUS       ret = 0;\r
-    uint32_t        ulSectorIdx = 0U;\r
-    uint32_t        ulSectorSize = gaRedVolConf[bVolNum].ulSectorSize;\r
-    const uint8_t  *pbBuffer = CAST_VOID_PTR_TO_CONST_UINT8_PTR(pBuffer);\r
-\r
-    while(ulSectorIdx < ulSectorCount)\r
-    {\r
-        uint32_t    ulTransfer = REDMIN(ulSectorCount - ulSectorIdx, MAX_SECTOR_TRANSFER);\r
-        DRESULT     result;\r
-\r
-        result = disk_write(bVolNum, &pbBuffer[ulSectorIdx * ulSectorSize], (DWORD)(ullSectorStart + ulSectorIdx), (BYTE)ulTransfer);\r
-        if(result != RES_OK)\r
-        {\r
-            ret = -RED_EIO;\r
-            break;\r
-        }\r
-\r
-        ulSectorIdx += ulTransfer;\r
-    }\r
-\r
-    return ret;\r
-}\r
-\r
-\r
-/** @brief Flush any caches beneath the file system.\r
-\r
-    @param bVolNum  The volume number of the volume whose block device is being\r
-                    flushed.\r
-\r
-    @return A negated ::REDSTATUS code indicating the operation result.\r
-\r
-    @retval 0   Operation was successful.\r
-*/\r
-static REDSTATUS DiskFlush(\r
-    uint8_t     bVolNum)\r
-{\r
-    REDSTATUS   ret;\r
-    DRESULT     result;\r
-\r
-    result = disk_ioctl(bVolNum, CTRL_SYNC, NULL);\r
-    if(result == RES_OK)\r
-    {\r
-        ret = 0;\r
-    }\r
-    else\r
-    {\r
-        ret = -RED_EIO;\r
-    }\r
-\r
-    return ret;\r
-}\r
-#endif /* REDCONF_READ_ONLY == 0 */\r
-\r
-\r
-#elif BDEV_EXAMPLE_IMPLEMENTATION == BDEV_ATMEL_SDMMC\r
-\r
-#include <task.h>\r
-\r
-#include <conf_sd_mmc.h>\r
-#include <sd_mmc.h>\r
-#include <sd_mmc_mem.h>\r
-#include <ctrl_access.h>\r
-\r
-/*  sd_mmc_mem_2_ram_multi() and sd_mmc_ram_2_mem_multi() use an unsigned\r
-    16-bit value to specify the sector count, so no transfer can be larger\r
-    than UINT16_MAX sectors.\r
-*/\r
-#define MAX_SECTOR_TRANSFER UINT16_MAX\r
-\r
-\r
-/** @brief Initialize a disk.\r
-\r
-    @param bVolNum  The volume number of the volume whose block device is being\r
-                    initialized.\r
-    @param mode     The open mode, indicating the type of access required.\r
-\r
-    @return A negated ::REDSTATUS code indicating the operation result.\r
-\r
-    @retval 0           Operation was successful.\r
-    @retval -RED_EIO    A disk I/O error occurred.\r
-    @retval -RED_EROFS  The device is read-only media and write access was\r
-                        requested.\r
-*/\r
-static REDSTATUS DiskOpen(\r
-    uint8_t         bVolNum,\r
-    BDEVOPENMODE    mode)\r
-{\r
-    REDSTATUS       ret = 0;\r
-    uint32_t        ulTries;\r
-    Ctrl_status     cs;\r
-\r
-    /*  Note: Assuming the volume number is the same as the SD card slot.  The\r
-        ASF SD/MMC driver supports two SD slots.  This implementation will need\r
-        to be modified if multiple volumes share a single SD card.\r
-    */\r
-\r
-    /*  The first time the disk is opened, the SD card can take a while to get\r
-        ready, in which time sd_mmc_test_unit_ready() returns either CTRL_BUSY\r
-        or CTRL_NO_PRESENT.  Try numerous times, waiting half a second after\r
-        each failure.  Empirically, this has been observed to succeed on the\r
-        second try, so trying 10x more than that provides a margin of error.\r
-    */\r
-    for(ulTries = 0U; ulTries < 20U; ulTries++)\r
-    {\r
-        cs = sd_mmc_test_unit_ready(bVolNum);\r
-        if((cs != CTRL_NO_PRESENT) && (cs != CTRL_BUSY))\r
-        {\r
-            break;\r
-        }\r
-\r
-        vTaskDelay(500U / portTICK_PERIOD_MS);\r
-    }\r
-\r
-    if(cs == CTRL_GOOD)\r
-    {\r
-      #if REDCONF_READ_ONLY == 0\r
-        if(mode != BDEV_O_RDONLY)\r
-        {\r
-            if(sd_mmc_wr_protect(bVolNum))\r
-            {\r
-                ret = -RED_EROFS;\r
-            }\r
-        }\r
-\r
-        if(ret == 0)\r
-      #endif\r
-        {\r
-            uint32_t ulSectorLast;\r
-\r
-            (void)sd_mmc_read_capacity(bVolNum, &ulSectorLast);\r
-\r
-            /*  The ASF SD/MMC driver only supports 512-byte sectors.\r
-            */\r
-            if(    (gaRedVolConf[bVolNum].ulSectorSize != 512U)\r
-                || (((uint64_t)ulSectorLast + 1U) < gaRedVolConf[bVolNum].ullSectorCount))\r
-            {\r
-                ret = -RED_EINVAL;\r
-            }\r
-        }\r
-    }\r
-    else\r
-    {\r
-        ret = -RED_EIO;\r
-    }\r
-\r
-    return ret;\r
-}\r
-\r
-\r
-/** @brief Uninitialize a disk.\r
-\r
-    @param bVolNum  The volume number of the volume whose block device is being\r
-                    uninitialized.\r
-\r
-    @return A negated ::REDSTATUS code indicating the operation result.\r
-\r
-    @retval 0   Operation was successful.\r
-*/\r
-static REDSTATUS DiskClose(\r
-    uint8_t     bVolNum)\r
-{\r
-    (void)bVolNum;\r
-    return 0;\r
-}\r
-\r
-\r
-/** @brief Read sectors from a disk.\r
-\r
-    @param bVolNum          The volume number of the volume whose block device\r
-                            is being read from.\r
-    @param ullSectorStart   The starting sector number.\r
-    @param ulSectorCount    The number of sectors to read.\r
-    @param pBuffer          The buffer into which to read the sector data.\r
-\r
-    @return A negated ::REDSTATUS code indicating the operation result.\r
-\r
-    @retval 0   Operation was successful.\r
-*/\r
-static REDSTATUS DiskRead(\r
-    uint8_t     bVolNum,\r
-    uint64_t    ullSectorStart,\r
-    uint32_t    ulSectorCount,\r
-    void       *pBuffer)\r
-{\r
-    REDSTATUS   ret = 0;\r
-    uint32_t    ulSectorIdx = 0U;\r
-    uint32_t    ulSectorSize = gaRedVolConf[bVolNum].ulSectorSize;\r
-    uint8_t    *pbBuffer = CAST_VOID_PTR_TO_UINT8_PTR(pBuffer);\r
-\r
-    while(ulSectorIdx < ulSectorCount)\r
-    {\r
-        uint32_t    ulTransfer = REDMIN(ulSectorCount - ulSectorIdx, MAX_SECTOR_TRANSFER);\r
-        Ctrl_status cs;\r
-\r
-        cs = sd_mmc_mem_2_ram_multi(bVolNum, (uint32_t)(ullSectorStart + ulSectorIdx),\r
-                                    (uint16_t)ulTransfer, &pbBuffer[ulSectorIdx * ulSectorSize]);\r
-        if(cs != CTRL_GOOD)\r
-        {\r
-            ret = -RED_EIO;\r
-            break;\r
-        }\r
-\r
-        ulSectorIdx += ulTransfer;\r
-    }\r
-\r
-    return ret;\r
-}\r
-\r
-\r
-#if REDCONF_READ_ONLY == 0\r
-/** @brief Write sectors to a disk.\r
-\r
-    @param bVolNum          The volume number of the volume whose block device\r
-                            is being written to.\r
-    @param ullSectorStart   The starting sector number.\r
-    @param ulSectorCount    The number of sectors to write.\r
-    @param pBuffer          The buffer from which to write the sector data.\r
-\r
-    @return A negated ::REDSTATUS code indicating the operation result.\r
-\r
-    @retval 0   Operation was successful.\r
-*/\r
-static REDSTATUS DiskWrite(\r
-    uint8_t         bVolNum,\r
-    uint64_t        ullSectorStart,\r
-    uint32_t        ulSectorCount,\r
-    const void     *pBuffer)\r
-{\r
-    REDSTATUS       ret = 0;\r
-    uint32_t        ulSectorIdx = 0U;\r
-    uint32_t        ulSectorSize = gaRedVolConf[bVolNum].ulSectorSize;\r
-    const uint8_t  *pbBuffer = CAST_VOID_PTR_TO_CONST_UINT8_PTR(pBuffer);\r
-\r
-    while(ulSectorIdx < ulSectorCount)\r
-    {\r
-        uint32_t    ulTransfer = REDMIN(ulSectorCount - ulSectorIdx, MAX_SECTOR_TRANSFER);\r
-        Ctrl_status cs;\r
-\r
-        cs = sd_mmc_ram_2_mem_multi(bVolNum, (uint32_t)(ullSectorStart + ulSectorIdx),\r
-                                    (uint16_t)ulTransfer, &pbBuffer[ulSectorIdx * ulSectorSize]);\r
-        if(cs != CTRL_GOOD)\r
-        {\r
-            ret = -RED_EIO;\r
-            break;\r
-        }\r
-\r
-        ulSectorIdx += ulTransfer;\r
-    }\r
-\r
-    return ret;\r
-}\r
-\r
-\r
-/** @brief Flush any caches beneath the file system.\r
-\r
-    @param bVolNum  The volume number of the volume whose block device is being\r
-                    flushed.\r
-\r
-    @return A negated ::REDSTATUS code indicating the operation result.\r
-\r
-    @retval 0   Operation was successful.\r
-*/\r
-static REDSTATUS DiskFlush(\r
-    uint8_t     bVolNum)\r
-{\r
-    REDSTATUS   ret;\r
-    Ctrl_status cs;\r
-\r
-    /*  The ASF SD/MMC driver appears to write sectors synchronously, so it\r
-        should be fine to do nothing and return success.  However, Atmel's\r
-        implementation of the FatFs diskio.c file does the equivalent of the\r
-        below when the disk is flushed.  Just in case this is important for some\r
-        non-obvious reason, do the same.\r
-    */\r
-    cs = sd_mmc_test_unit_ready(bVolNum);\r
-    if(cs == CTRL_GOOD)\r
-    {\r
-        ret = 0;\r
-    }\r
-    else\r
-    {\r
-        ret = -RED_EIO;\r
-    }\r
-\r
-    return ret;\r
-}\r
-#endif /* REDCONF_READ_ONLY == 0 */\r
-\r
-\r
-#elif BDEV_EXAMPLE_IMPLEMENTATION == BDEV_RAM_DISK\r
-\r
-#include <stdlib.h> /* For ALLOCATE_CLEARED_MEMORY(), which expands to calloc(). */\r
-\r
-\r
-static uint8_t *gapbRamDisk[REDCONF_VOLUME_COUNT];\r
-\r
-\r
-/** @brief Initialize a disk.\r
-\r
-    @param bVolNum  The volume number of the volume whose block device is being\r
-                    initialized.\r
-    @param mode     The open mode, indicating the type of access required.\r
-\r
-    @return A negated ::REDSTATUS code indicating the operation result.\r
-\r
-    @retval 0           Operation was successful.\r
-    @retval -RED_EIO    A disk I/O error occurred.\r
-*/\r
-static REDSTATUS DiskOpen(\r
-    uint8_t         bVolNum,\r
-    BDEVOPENMODE    mode)\r
-{\r
-    REDSTATUS       ret = 0;\r
-\r
-    (void)mode;\r
-\r
-    if(gapbRamDisk[bVolNum] == NULL)\r
-    {\r
-        gapbRamDisk[bVolNum] = ALLOCATE_CLEARED_MEMORY(gaRedVolume[bVolNum].ulBlockCount, REDCONF_BLOCK_SIZE);\r
-        if(gapbRamDisk[bVolNum] == NULL)\r
-        {\r
-            ret = -RED_EIO;\r
-        }\r
-    }\r
-\r
-    return ret;\r
-}\r
-\r
-\r
-/** @brief Uninitialize a disk.\r
-\r
-    @param bVolNum  The volume number of the volume whose block device is being\r
-                    uninitialized.\r
-\r
-    @return A negated ::REDSTATUS code indicating the operation result.\r
-\r
-    @retval 0   Operation was successful.\r
-*/\r
-static REDSTATUS DiskClose(\r
-    uint8_t     bVolNum)\r
-{\r
-    REDSTATUS   ret;\r
-\r
-    if(gapbRamDisk[bVolNum] == NULL)\r
-    {\r
-        ret = -RED_EINVAL;\r
-    }\r
-    else\r
-    {\r
-        /*  This implementation uses dynamically allocated memory, but must\r
-            retain previously written data after the block device is closed, and\r
-            thus the memory cannot be freed and will remain allocated until\r
-            reboot.\r
-        */\r
-        ret = 0;\r
-    }\r
-\r
-    return ret;\r
-}\r
-\r
-\r
-/** @brief Read sectors from a disk.\r
-\r
-    @param bVolNum          The volume number of the volume whose block device\r
-                            is being read from.\r
-    @param ullSectorStart   The starting sector number.\r
-    @param ulSectorCount    The number of sectors to read.\r
-    @param pBuffer          The buffer into which to read the sector data.\r
-\r
-    @return A negated ::REDSTATUS code indicating the operation result.\r
-\r
-    @retval 0   Operation was successful.\r
-*/\r
-static REDSTATUS DiskRead(\r
-    uint8_t     bVolNum,\r
-    uint64_t    ullSectorStart,\r
-    uint32_t    ulSectorCount,\r
-    void       *pBuffer)\r
-{\r
-    REDSTATUS   ret;\r
-\r
-    if(gapbRamDisk[bVolNum] == NULL)\r
-    {\r
-        ret = -RED_EINVAL;\r
-    }\r
-    else\r
-    {\r
-        uint64_t ullByteOffset = ullSectorStart * gaRedVolConf[bVolNum].ulSectorSize;\r
-        uint32_t ulByteCount = ulSectorCount * gaRedVolConf[bVolNum].ulSectorSize;\r
-\r
-        RedMemCpy(pBuffer, &gapbRamDisk[bVolNum][ullByteOffset], ulByteCount);\r
-\r
-        ret = 0;\r
-    }\r
-\r
-    return ret;\r
-}\r
-\r
-\r
-#if REDCONF_READ_ONLY == 0\r
-/** @brief Write sectors to a disk.\r
-\r
-    @param bVolNum          The volume number of the volume whose block device\r
-                            is being written to.\r
-    @param ullSectorStart   The starting sector number.\r
-    @param ulSectorCount    The number of sectors to write.\r
-    @param pBuffer          The buffer from which to write the sector data.\r
-\r
-    @return A negated ::REDSTATUS code indicating the operation result.\r
-\r
-    @retval 0   Operation was successful.\r
-*/\r
-static REDSTATUS DiskWrite(\r
-    uint8_t     bVolNum,\r
-    uint64_t    ullSectorStart,\r
-    uint32_t    ulSectorCount,\r
-    const void *pBuffer)\r
-{\r
-    REDSTATUS   ret;\r
-\r
-    if(gapbRamDisk[bVolNum] == NULL)\r
-    {\r
-        ret = -RED_EINVAL;\r
-    }\r
-    else\r
-    {\r
-        uint64_t ullByteOffset = ullSectorStart * gaRedVolConf[bVolNum].ulSectorSize;\r
-        uint32_t ulByteCount = ulSectorCount * gaRedVolConf[bVolNum].ulSectorSize;\r
-\r
-        RedMemCpy(&gapbRamDisk[bVolNum][ullByteOffset], pBuffer, ulByteCount);\r
-\r
-        ret = 0;\r
-    }\r
-\r
-    return ret;\r
-}\r
-\r
-\r
-/** @brief Flush any caches beneath the file system.\r
-\r
-    @param bVolNum  The volume number of the volume whose block device is being\r
-                    flushed.\r
-\r
-    @return A negated ::REDSTATUS code indicating the operation result.\r
-\r
-    @retval 0   Operation was successful.\r
-*/\r
-static REDSTATUS DiskFlush(\r
-    uint8_t     bVolNum)\r
-{\r
-    REDSTATUS   ret;\r
-\r
-    if(gapbRamDisk[bVolNum] == NULL)\r
-    {\r
-        ret = -RED_EINVAL;\r
-    }\r
-    else\r
-    {\r
-        ret = 0;\r
-    }\r
-\r
-    return ret;\r
-}\r
-#endif /* REDCONF_READ_ONLY == 0 */\r
-\r
-\r
-#else\r
-\r
-#error "Invalid BDEV_EXAMPLE_IMPLEMENTATION value"\r
-\r
-#endif /* BDEV_EXAMPLE_IMPLEMENTATION == ... */\r
-\r
+/*             ----> DO NOT REMOVE THE FOLLOWING NOTICE <----
+
+                   Copyright (c) 2014-2015 Datalight, Inc.
+                       All Rights Reserved Worldwide.
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; use version 2 of the License.
+
+    This program is distributed in the hope that it will be useful,
+    but "AS-IS," WITHOUT ANY WARRANTY; without even the implied warranty
+    of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+/*  Businesses and individuals that for commercial or other reasons cannot
+    comply with the terms of the GPLv2 license may obtain a commercial license
+    before incorporating Reliance Edge into proprietary software for
+    distribution in any form.  Visit http://www.datalight.com/reliance-edge for
+    more information.
+*/
+/** @file
+    @brief Implements block device I/O.
+*/
+#include <FreeRTOS.h>
+
+#include <redfs.h>
+#include <redvolume.h>
+#include <redosdeviations.h>
+
+
+/*------------------------------------------------------------------------------
+    Porting Note:
+
+    Several example implementations of this module for FreeRTOS are available.
+    If you are lucky, you can use one of these implementations; otherwise, these
+    can serve as examples of how to implement this service.
+------------------------------------------------------------------------------*/
+
+/** @brief The F_DRIVER example implementation.
+
+    This implementation is designed to reuse an existing block device driver
+    that was written for FreeRTOS+FAT SL.  If you have such a driver, with
+    little work it can be "dropped in" and used for Reliance Edge.  The only
+    customization required is that gpfnRedOsBDevInit needs to be defined and
+    pointed at the F_DRIVERINIT function.  This can be done in this module or in
+    another C file.
+
+    The disadantage of using the FreeRTOS F_DRIVER functions is that they only
+    support single-sector reads and writes.  Reliance Edge will issue
+    multi-sector requests, and servicing these one sector at a time will
+    significantly slow down the file system.
+*/
+#define BDEV_F_DRIVER       (0U)
+
+/** @brief The FatFs example implementation.
+
+    This implementation is designed to reuse an existing block device driver
+    that was written for FatFs.  If you have such a driver, it can be linked
+    in and used immediately.  The FatFs `diskio.h` header must be in the include
+    directory path.
+*/
+#define BDEV_FATFS          (1U)
+
+/** @brief The Atmel Studio Framework SD/MMC driver example implementation.
+
+    This implementation uses a modified version of the open source SD/MMC driver
+    included in the Atmel Studio Framework (ASF) and will work as-is for many
+    varieties of Atmel hardware.  This example assumes relatively minor
+    modifications to the ASF SD/MMC driver to make it support multi-sector read
+    and write requests, which greatly improves performance.  The modified driver
+    is distributed with Reliance Edge and is included in FreeRTOS Atmel projects
+    (such as in projects/freertos/atmel/sam4e-ek/src/ASF).
+
+    This example can easily be modified to work with an unmodified version of
+    the ASF SD/MMC driver.  Simply replace sd_mmc_mem_2_ram_multi() and
+    sd_mmc_ram_2_mem_multi() with sd_mmc_mem_2_ram() and sd_mmc_ram_2_mem()
+    respectively, and add a for loop to loop over each sector in the request.
+    However, as described in the manual, there are considerable performance
+    advantages to issuing real multi-sector requests, so using the modified
+    driver is recommended.
+*/
+#define BDEV_ATMEL_SDMMC    (2U)
+
+/** @brief The ST Microelectronics STM32 SDIO driver example implementation.
+
+    This implementation accesses the microSD card through the BSP utilities
+    provided as part of the STM32Cube package, used with the STM32 HAL drivers.
+    The STM3240G-EVAL and STM32F746NG-Discovery boards are currently supported.
+*/
+#define BDEV_STM32_SDIO     (3U)
+
+/** @brief The RAM disk example implementation.
+
+    This implementation uses a RAM disk.  It will allow you to compile and test
+    Reliance Edge even if your storage driver is not yet ready.  On typical
+    target hardware, the amount of spare RAM will be limited so generally only
+    very small disks will be available.
+*/
+#define BDEV_RAM_DISK       (4U)
+
+/** @brief Pick which example implementation is compiled.
+
+    Must be one of:
+    - #BDEV_F_DRIVER
+    - #BDEV_FATFS
+    - #BDEV_ATMEL_SDMMC
+    - #BDEV_STM32_SDIO
+    - #BDEV_RAM_DISK
+*/
+#define BDEV_EXAMPLE_IMPLEMENTATION BDEV_RAM_DISK
+
+
+static REDSTATUS DiskOpen(uint8_t bVolNum, BDEVOPENMODE mode);
+static REDSTATUS DiskClose(uint8_t bVolNum);
+static REDSTATUS DiskRead(uint8_t bVolNum, uint64_t ullSectorStart, uint32_t ulSectorCount, void *pBuffer);
+#if REDCONF_READ_ONLY == 0
+static REDSTATUS DiskWrite(uint8_t bVolNum, uint64_t ullSectorStart, uint32_t ulSectorCount, const void *pBuffer);
+static REDSTATUS DiskFlush(uint8_t bVolNum);
+#endif
+
+
+/** @brief Initialize a block device.
+
+    This function is called when the file system needs access to a block
+    device.
+
+    Upon successful return, the block device should be fully initialized and
+    ready to service read/write/flush/close requests.
+
+    The behavior of calling this function on a block device which is already
+    open is undefined.
+
+    @param bVolNum  The volume number of the volume whose block device is being
+                    initialized.
+    @param mode     The open mode, indicating the type of access required.
+
+    @return A negated ::REDSTATUS code indicating the operation result.
+
+    @retval 0           Operation was successful.
+    @retval -RED_EINVAL @p bVolNum is an invalid volume number.
+    @retval -RED_EIO    A disk I/O error occurred.
+*/
+REDSTATUS RedOsBDevOpen(
+    uint8_t         bVolNum,
+    BDEVOPENMODE    mode)
+{
+    REDSTATUS       ret;
+
+    if(bVolNum >= REDCONF_VOLUME_COUNT)
+    {
+        ret = -RED_EINVAL;
+    }
+    else
+    {
+        ret = DiskOpen(bVolNum, mode);
+    }
+
+    return ret;
+}
+
+
+/** @brief Uninitialize a block device.
+
+    This function is called when the file system no longer needs access to a
+    block device.  If any resource were allocated by RedOsBDevOpen() to service
+    block device requests, they should be freed at this time.
+
+    Upon successful return, the block device must be in such a state that it
+    can be opened again.
+
+    The behavior of calling this function on a block device which is already
+    closed is undefined.
+
+    @param bVolNum  The volume number of the volume whose block device is being
+                    uninitialized.
+
+    @return A negated ::REDSTATUS code indicating the operation result.
+
+    @retval 0           Operation was successful.
+    @retval -RED_EINVAL @p bVolNum is an invalid volume number.
+*/
+REDSTATUS RedOsBDevClose(
+    uint8_t     bVolNum)
+{
+    REDSTATUS   ret;
+
+    if(bVolNum >= REDCONF_VOLUME_COUNT)
+    {
+        ret = -RED_EINVAL;
+    }
+    else
+    {
+        ret = DiskClose(bVolNum);
+    }
+
+    return ret;
+}
+
+
+/** @brief Read sectors from a physical block device.
+
+    The behavior of calling this function is undefined if the block device is
+    closed or if it was opened with ::BDEV_O_WRONLY.
+
+    @param bVolNum          The volume number of the volume whose block device
+                            is being read from.
+    @param ullSectorStart   The starting sector number.
+    @param ulSectorCount    The number of sectors to read.
+    @param pBuffer          The buffer into which to read the sector data.
+
+    @return A negated ::REDSTATUS code indicating the operation result.
+
+    @retval 0           Operation was successful.
+    @retval -RED_EINVAL @p bVolNum is an invalid volume number, @p pBuffer is
+                        `NULL`, or @p ullStartSector and/or @p ulSectorCount
+                        refer to an invalid range of sectors.
+    @retval -RED_EIO    A disk I/O error occurred.
+*/
+REDSTATUS RedOsBDevRead(
+    uint8_t     bVolNum,
+    uint64_t    ullSectorStart,
+    uint32_t    ulSectorCount,
+    void       *pBuffer)
+{
+    REDSTATUS   ret = 0;
+
+    if(    (bVolNum >= REDCONF_VOLUME_COUNT)
+        || (ullSectorStart >= gaRedVolConf[bVolNum].ullSectorCount)
+        || ((gaRedVolConf[bVolNum].ullSectorCount - ullSectorStart) < ulSectorCount)
+        || (pBuffer == NULL))
+    {
+        ret = -RED_EINVAL;
+    }
+    else
+    {
+        ret = DiskRead(bVolNum, ullSectorStart, ulSectorCount, pBuffer);
+    }
+
+    return ret;
+}
+
+
+#if REDCONF_READ_ONLY == 0
+/** @brief Write sectors to a physical block device.
+
+    The behavior of calling this function is undefined if the block device is
+    closed or if it was opened with ::BDEV_O_RDONLY.
+
+    @param bVolNum          The volume number of the volume whose block device
+                            is being written to.
+    @param ullSectorStart   The starting sector number.
+    @param ulSectorCount    The number of sectors to write.
+    @param pBuffer          The buffer from which to write the sector data.
+
+    @return A negated ::REDSTATUS code indicating the operation result.
+
+    @retval 0           Operation was successful.
+    @retval -RED_EINVAL @p bVolNum is an invalid volume number, @p pBuffer is
+                        `NULL`, or @p ullStartSector and/or @p ulSectorCount
+                        refer to an invalid range of sectors.
+    @retval -RED_EIO    A disk I/O error occurred.
+*/
+REDSTATUS RedOsBDevWrite(
+    uint8_t     bVolNum,
+    uint64_t    ullSectorStart,
+    uint32_t    ulSectorCount,
+    const void *pBuffer)
+{
+    REDSTATUS   ret = 0;
+
+    if(    (bVolNum >= REDCONF_VOLUME_COUNT)
+        || (ullSectorStart >= gaRedVolConf[bVolNum].ullSectorCount)
+        || ((gaRedVolConf[bVolNum].ullSectorCount - ullSectorStart) < ulSectorCount)
+        || (pBuffer == NULL))
+    {
+        ret = -RED_EINVAL;
+    }
+    else
+    {
+        ret = DiskWrite(bVolNum, ullSectorStart, ulSectorCount, pBuffer);
+    }
+
+    return ret;
+}
+
+
+/** @brief Flush any caches beneath the file system.
+
+    This function must synchronously flush all software and hardware caches
+    beneath the file system, ensuring that all sectors written previously are
+    committed to permanent storage.
+
+    If the environment has no caching beneath the file system, the
+    implementation of this function can do nothing and return success.
+
+    The behavior of calling this function is undefined if the block device is
+    closed or if it was opened with ::BDEV_O_RDONLY.
+
+    @param bVolNum  The volume number of the volume whose block device is being
+                    flushed.
+
+    @return A negated ::REDSTATUS code indicating the operation result.
+
+    @retval 0           Operation was successful.
+    @retval -RED_EINVAL @p bVolNum is an invalid volume number.
+    @retval -RED_EIO    A disk I/O error occurred.
+*/
+REDSTATUS RedOsBDevFlush(
+    uint8_t     bVolNum)
+{
+    REDSTATUS   ret;
+
+    if(bVolNum >= REDCONF_VOLUME_COUNT)
+    {
+        ret = -RED_EINVAL;
+    }
+    else
+    {
+        ret = DiskFlush(bVolNum);
+    }
+
+    return ret;
+}
+#endif /* REDCONF_READ_ONLY == 0 */
+
+
+#if BDEV_EXAMPLE_IMPLEMENTATION == BDEV_F_DRIVER
+
+#include <api_mdriver.h>
+
+
+/*  This must be declared and initialized elsewere (e.g., in project code) to
+    point at the initialization function for the F_DRIVER block device.
+*/
+extern const F_DRIVERINIT gpfnRedOsBDevInit;
+
+static F_DRIVER *gapFDriver[REDCONF_VOLUME_COUNT];
+
+
+/** @brief Initialize a disk.
+
+    @param bVolNum  The volume number of the volume whose block device is being
+                    initialized.
+    @param mode     The open mode, indicating the type of access required.
+
+    @return A negated ::REDSTATUS code indicating the operation result.
+
+    @retval 0           Operation was successful.
+    @retval -RED_EIO    A disk I/O error occurred.
+*/
+static REDSTATUS DiskOpen(
+    uint8_t         bVolNum,
+    BDEVOPENMODE    mode)
+{
+    REDSTATUS       ret;
+
+    (void)mode;
+
+    if((gpfnRedOsBDevInit == NULL) || (gapFDriver[bVolNum] != NULL))
+    {
+        ret = -RED_EINVAL;
+    }
+    else
+    {
+        F_DRIVER *pDriver;
+
+        pDriver = gpfnRedOsBDevInit(bVolNum);
+        if(pDriver != NULL)
+        {
+            F_PHY   geom;
+            int     iErr;
+
+            /*  Validate that the geometry is consistent with the volume
+                configuration.
+            */
+            iErr = pDriver->getphy(pDriver, &geom);
+            if(iErr == 0)
+            {
+                if(    (geom.bytes_per_sector != gaRedVolConf[bVolNum].ulSectorSize)
+                    || (geom.number_of_sectors < gaRedVolConf[bVolNum].ullSectorCount))
+                {
+                    ret = -RED_EINVAL;
+                }
+                else
+                {
+                    gapFDriver[bVolNum] = pDriver;
+                    ret = 0;
+                }
+            }
+            else
+            {
+                ret = -RED_EIO;
+            }
+
+            if(ret != 0)
+            {
+                pDriver->release(pDriver);
+            }
+        }
+        else
+        {
+            ret = -RED_EIO;
+        }
+    }
+
+    return ret;
+}
+
+
+/** @brief Uninitialize a disk.
+
+    @param bVolNum  The volume number of the volume whose block device is being
+                    uninitialized.
+
+    @return A negated ::REDSTATUS code indicating the operation result.
+
+    @retval 0   Operation was successful.
+*/
+static REDSTATUS DiskClose(
+    uint8_t     bVolNum)
+{
+    REDSTATUS   ret;
+
+    if(gapFDriver[bVolNum] == NULL)
+    {
+        ret = -RED_EINVAL;
+    }
+    else
+    {
+        gapFDriver[bVolNum]->release(gapFDriver[bVolNum]);
+        gapFDriver[bVolNum] = NULL;
+
+        ret = 0;
+    }
+
+    return ret;
+}
+
+
+/** @brief Read sectors from a disk.
+
+    @param bVolNum          The volume number of the volume whose block device
+                            is being read from.
+    @param ullSectorStart   The starting sector number.
+    @param ulSectorCount    The number of sectors to read.
+    @param pBuffer          The buffer into which to read the sector data.
+
+    @return A negated ::REDSTATUS code indicating the operation result.
+
+    @retval 0           Operation was successful.
+    @retval -RED_EIO    A disk I/O error occurred.
+*/
+static REDSTATUS DiskRead(
+    uint8_t     bVolNum,
+    uint64_t    ullSectorStart,
+    uint32_t    ulSectorCount,
+    void       *pBuffer)
+{
+    REDSTATUS   ret = 0;
+    F_DRIVER   *pDriver = gapFDriver[bVolNum];
+
+    if(pDriver == NULL)
+    {
+        ret = -RED_EINVAL;
+    }
+    else
+    {
+        uint8_t    *pbBuffer = CAST_VOID_PTR_TO_UINT8_PTR(pBuffer);
+        uint32_t    ulSectorSize = gaRedVolConf[bVolNum].ulSectorSize;
+        uint32_t    ulSectorIdx;
+        int         iErr;
+
+        for(ulSectorIdx = 0U; ulSectorIdx < ulSectorCount; ulSectorIdx++)
+        {
+            iErr = pDriver->readsector(pDriver, &pbBuffer[ulSectorIdx * ulSectorSize],
+                                       CAST_ULONG(ullSectorStart + ulSectorIdx));
+            if(iErr != 0)
+            {
+                ret = -RED_EIO;
+                break;
+            }
+        }
+    }
+
+    return ret;
+}
+
+
+#if REDCONF_READ_ONLY == 0
+/** @brief Write sectors to a disk.
+
+    @param bVolNum          The volume number of the volume whose block device
+                            is being written to.
+    @param ullSectorStart   The starting sector number.
+    @param ulSectorCount    The number of sectors to write.
+    @param pBuffer          The buffer from which to write the sector data.
+
+    @return A negated ::REDSTATUS code indicating the operation result.
+
+    @retval 0           Operation was successful.
+    @retval -RED_EINVAL The block device is not open.
+    @retval -RED_EIO    A disk I/O error occurred.
+*/
+static REDSTATUS DiskWrite(
+    uint8_t     bVolNum,
+    uint64_t    ullSectorStart,
+    uint32_t    ulSectorCount,
+    const void *pBuffer)
+{
+    REDSTATUS   ret = 0;
+    F_DRIVER   *pDriver = gapFDriver[bVolNum];
+
+    if(pDriver == NULL)
+    {
+        ret = -RED_EINVAL;
+    }
+    else
+    {
+        const uint8_t  *pbBuffer = CAST_VOID_PTR_TO_CONST_UINT8_PTR(pBuffer);
+        uint32_t        ulSectorSize = gaRedVolConf[bVolNum].ulSectorSize;
+        uint32_t        ulSectorIdx;
+        int             iErr;
+
+        for(ulSectorIdx = 0U; ulSectorIdx < ulSectorCount; ulSectorIdx++)
+        {
+            /*  We have to cast pbBuffer to non-const since the writesector
+                prototype is flawed, using a non-const pointer for the buffer.
+            */
+            iErr = pDriver->writesector(pDriver, CAST_AWAY_CONST(uint8_t, &pbBuffer[ulSectorIdx * ulSectorSize]),
+                                        CAST_ULONG(ullSectorStart + ulSectorIdx));
+            if(iErr != 0)
+            {
+                ret = -RED_EIO;
+                break;
+            }
+        }
+    }
+
+    return ret;
+}
+
+
+/** @brief Flush any caches beneath the file system.
+
+    @param bVolNum  The volume number of the volume whose block device is being
+                    flushed.
+
+    @return A negated ::REDSTATUS code indicating the operation result.
+
+    @retval 0   Operation was successful.
+*/
+static REDSTATUS DiskFlush(
+    uint8_t     bVolNum)
+{
+    REDSTATUS   ret;
+
+    if(gapFDriver[bVolNum] == NULL)
+    {
+        ret = -RED_EINVAL;
+    }
+    else
+    {
+        /*  The F_DRIVER interface does not include a flush function, so to be
+            reliable the F_DRIVER implementation must use synchronous writes.
+        */
+        ret = 0;
+    }
+
+    return ret;
+}
+#endif /* REDCONF_READ_ONLY == 0 */
+
+
+#elif BDEV_EXAMPLE_IMPLEMENTATION == BDEV_FATFS
+
+#include <task.h>
+#include <diskio.h>
+
+/*  disk_read() and disk_write() use an unsigned 8-bit value to specify the
+    sector count, so no transfer can be larger than 255 sectors.
+*/
+#define MAX_SECTOR_TRANSFER UINT8_MAX
+
+
+/** @brief Initialize a disk.
+
+    @param bVolNum  The volume number of the volume whose block device is being
+                    initialized.
+    @param mode     The open mode, indicating the type of access required.
+
+    @return A negated ::REDSTATUS code indicating the operation result.
+
+    @retval 0           Operation was successful.
+    @retval -RED_EIO    A disk I/O error occurred.
+*/
+static REDSTATUS DiskOpen(
+    uint8_t         bVolNum,
+    BDEVOPENMODE    mode)
+{
+    DSTATUS         status;
+    uint32_t        ulTries;
+    REDSTATUS       ret = 0;
+
+    /*  With some implementations of disk_initialize(), such as the one
+        implemented by Atmel for the ASF, the first time the disk is opened, the
+        SD card can take a while to get ready, in which time disk_initialize()
+        returns an error.  Try numerous times, waiting half a second after each
+        failure.  Empirically, this has been observed to succeed on the second
+        try, so trying 10x more than that provides a margin of error.
+    */
+    for(ulTries = 0U; ulTries < 20U; ulTries++)
+    {
+        /*  Assuming that the volume number is also the correct drive number.
+            If this is not the case in your environment, a static constant array
+            can be declared to map volume numbers to the correct driver number.
+        */
+        status = disk_initialize(bVolNum);
+        if(status == 0)
+        {
+            break;
+        }
+
+        vTaskDelay(500U / portTICK_PERIOD_MS);
+    }
+
+    if(status != 0)
+    {
+        ret = -RED_EIO;
+    }
+
+    /*  Retrieve the sector size and sector count to ensure they are compatible
+        with our compile-time geometry.
+    */
+    if(ret == 0)
+    {
+        WORD    wSectorSize;
+        DWORD   dwSectorCount;
+        DRESULT result;
+
+        result = disk_ioctl(bVolNum, GET_SECTOR_SIZE, &wSectorSize);
+        if(result == RES_OK)
+        {
+            result = disk_ioctl(bVolNum, GET_SECTOR_COUNT, &dwSectorCount);
+            if(result == RES_OK)
+            {
+                if(    (wSectorSize != gaRedVolConf[bVolNum].ulSectorSize)
+                    || (dwSectorCount < gaRedVolConf[bVolNum].ullSectorCount))
+                {
+                    ret = -RED_EINVAL;
+                }
+            }
+            else
+            {
+                ret = -RED_EIO;
+            }
+        }
+        else
+        {
+            ret = -RED_EIO;
+        }
+    }
+
+    return ret;
+}
+
+
+/** @brief Uninitialize a disk.
+
+    @param bVolNum  The volume number of the volume whose block device is being
+                    uninitialized.
+
+    @return A negated ::REDSTATUS code indicating the operation result.
+
+    @retval 0   Operation was successful.
+*/
+static REDSTATUS DiskClose(
+    uint8_t     bVolNum)
+{
+    (void)bVolNum;
+    return 0;
+}
+
+
+/** @brief Read sectors from a disk.
+
+    @param bVolNum          The volume number of the volume whose block device
+                            is being read from.
+    @param ullSectorStart   The starting sector number.
+    @param ulSectorCount    The number of sectors to read.
+    @param pBuffer          The buffer into which to read the sector data.
+
+    @return A negated ::REDSTATUS code indicating the operation result.
+
+    @retval 0           Operation was successful.
+    @retval -RED_EIO    A disk I/O error occurred.
+*/
+static REDSTATUS DiskRead(
+    uint8_t     bVolNum,
+    uint64_t    ullSectorStart,
+    uint32_t    ulSectorCount,
+    void       *pBuffer)
+{
+    REDSTATUS   ret = 0;
+    uint32_t    ulSectorIdx = 0U;
+    uint32_t    ulSectorSize = gaRedVolConf[bVolNum].ulSectorSize;
+    uint8_t    *pbBuffer = CAST_VOID_PTR_TO_UINT8_PTR(pBuffer);
+
+    while(ulSectorIdx < ulSectorCount)
+    {
+        uint32_t    ulTransfer = REDMIN(ulSectorCount - ulSectorIdx, MAX_SECTOR_TRANSFER);
+        DRESULT     result;
+
+        result = disk_read(bVolNum, &pbBuffer[ulSectorIdx * ulSectorSize], (DWORD)(ullSectorStart + ulSectorIdx), (BYTE)ulTransfer);
+        if(result != RES_OK)
+        {
+            ret = -RED_EIO;
+            break;
+        }
+
+        ulSectorIdx += ulTransfer;
+    }
+
+    return ret;
+}
+
+
+#if REDCONF_READ_ONLY == 0
+/** @brief Write sectors to a disk.
+
+    @param bVolNum          The volume number of the volume whose block device
+                            is being written to.
+    @param ullSectorStart   The starting sector number.
+    @param ulSectorCount    The number of sectors to write.
+    @param pBuffer          The buffer from which to write the sector data.
+
+    @return A negated ::REDSTATUS code indicating the operation result.
+
+    @retval 0           Operation was successful.
+    @retval -RED_EIO    A disk I/O error occurred.
+*/
+static REDSTATUS DiskWrite(
+    uint8_t         bVolNum,
+    uint64_t        ullSectorStart,
+    uint32_t        ulSectorCount,
+    const void     *pBuffer)
+{
+    REDSTATUS       ret = 0;
+    uint32_t        ulSectorIdx = 0U;
+    uint32_t        ulSectorSize = gaRedVolConf[bVolNum].ulSectorSize;
+    const uint8_t  *pbBuffer = CAST_VOID_PTR_TO_CONST_UINT8_PTR(pBuffer);
+
+    while(ulSectorIdx < ulSectorCount)
+    {
+        uint32_t    ulTransfer = REDMIN(ulSectorCount - ulSectorIdx, MAX_SECTOR_TRANSFER);
+        DRESULT     result;
+
+        result = disk_write(bVolNum, &pbBuffer[ulSectorIdx * ulSectorSize], (DWORD)(ullSectorStart + ulSectorIdx), (BYTE)ulTransfer);
+        if(result != RES_OK)
+        {
+            ret = -RED_EIO;
+            break;
+        }
+
+        ulSectorIdx += ulTransfer;
+    }
+
+    return ret;
+}
+
+
+/** @brief Flush any caches beneath the file system.
+
+    @param bVolNum  The volume number of the volume whose block device is being
+                    flushed.
+
+    @return A negated ::REDSTATUS code indicating the operation result.
+
+    @retval 0   Operation was successful.
+*/
+static REDSTATUS DiskFlush(
+    uint8_t     bVolNum)
+{
+    REDSTATUS   ret;
+    DRESULT     result;
+
+    result = disk_ioctl(bVolNum, CTRL_SYNC, NULL);
+    if(result == RES_OK)
+    {
+        ret = 0;
+    }
+    else
+    {
+        ret = -RED_EIO;
+    }
+
+    return ret;
+}
+#endif /* REDCONF_READ_ONLY == 0 */
+
+
+#elif BDEV_EXAMPLE_IMPLEMENTATION == BDEV_ATMEL_SDMMC
+
+#include <task.h>
+
+#include <conf_sd_mmc.h>
+#include <sd_mmc.h>
+#include <sd_mmc_mem.h>
+#include <ctrl_access.h>
+
+/*  sd_mmc_mem_2_ram_multi() and sd_mmc_ram_2_mem_multi() use an unsigned
+    16-bit value to specify the sector count, so no transfer can be larger
+    than UINT16_MAX sectors.
+*/
+#define MAX_SECTOR_TRANSFER UINT16_MAX
+
+
+/** @brief Initialize a disk.
+
+    @param bVolNum  The volume number of the volume whose block device is being
+                    initialized.
+    @param mode     The open mode, indicating the type of access required.
+
+    @return A negated ::REDSTATUS code indicating the operation result.
+
+    @retval 0           Operation was successful.
+    @retval -RED_EIO    A disk I/O error occurred.
+    @retval -RED_EROFS  The device is read-only media and write access was
+                        requested.
+*/
+static REDSTATUS DiskOpen(
+    uint8_t         bVolNum,
+    BDEVOPENMODE    mode)
+{
+    REDSTATUS       ret = 0;
+    uint32_t        ulTries;
+    Ctrl_status     cs;
+
+    /*  Note: Assuming the volume number is the same as the SD card slot.  The
+        ASF SD/MMC driver supports two SD slots.  This implementation will need
+        to be modified if multiple volumes share a single SD card.
+    */
+
+    /*  The first time the disk is opened, the SD card can take a while to get
+        ready, in which time sd_mmc_test_unit_ready() returns either CTRL_BUSY
+        or CTRL_NO_PRESENT.  Try numerous times, waiting half a second after
+        each failure.  Empirically, this has been observed to succeed on the
+        second try, so trying 10x more than that provides a margin of error.
+    */
+    for(ulTries = 0U; ulTries < 20U; ulTries++)
+    {
+        cs = sd_mmc_test_unit_ready(bVolNum);
+        if((cs != CTRL_NO_PRESENT) && (cs != CTRL_BUSY))
+        {
+            break;
+        }
+
+        vTaskDelay(500U / portTICK_PERIOD_MS);
+    }
+
+    if(cs == CTRL_GOOD)
+    {
+      #if REDCONF_READ_ONLY == 0
+        if(mode != BDEV_O_RDONLY)
+        {
+            if(sd_mmc_wr_protect(bVolNum))
+            {
+                ret = -RED_EROFS;
+            }
+        }
+
+        if(ret == 0)
+      #endif
+        {
+            uint32_t ulSectorLast;
+
+            IGNORE_ERRORS(sd_mmc_read_capacity(bVolNum, &ulSectorLast));
+
+            /*  The ASF SD/MMC driver only supports 512-byte sectors.
+            */
+            if(    (gaRedVolConf[bVolNum].ulSectorSize != 512U)
+                || (((uint64_t)ulSectorLast + 1U) < gaRedVolConf[bVolNum].ullSectorCount))
+            {
+                ret = -RED_EINVAL;
+            }
+        }
+    }
+    else
+    {
+        ret = -RED_EIO;
+    }
+
+    return ret;
+}
+
+
+/** @brief Uninitialize a disk.
+
+    @param bVolNum  The volume number of the volume whose block device is being
+                    uninitialized.
+
+    @return A negated ::REDSTATUS code indicating the operation result.
+
+    @retval 0   Operation was successful.
+*/
+static REDSTATUS DiskClose(
+    uint8_t     bVolNum)
+{
+    (void)bVolNum;
+    return 0;
+}
+
+
+/** @brief Read sectors from a disk.
+
+    @param bVolNum          The volume number of the volume whose block device
+                            is being read from.
+    @param ullSectorStart   The starting sector number.
+    @param ulSectorCount    The number of sectors to read.
+    @param pBuffer          The buffer into which to read the sector data.
+
+    @return A negated ::REDSTATUS code indicating the operation result.
+
+    @retval 0   Operation was successful.
+*/
+static REDSTATUS DiskRead(
+    uint8_t     bVolNum,
+    uint64_t    ullSectorStart,
+    uint32_t    ulSectorCount,
+    void       *pBuffer)
+{
+    REDSTATUS   ret = 0;
+    uint32_t    ulSectorIdx = 0U;
+    uint32_t    ulSectorSize = gaRedVolConf[bVolNum].ulSectorSize;
+    uint8_t    *pbBuffer = CAST_VOID_PTR_TO_UINT8_PTR(pBuffer);
+
+    while(ulSectorIdx < ulSectorCount)
+    {
+        uint32_t    ulTransfer = REDMIN(ulSectorCount - ulSectorIdx, MAX_SECTOR_TRANSFER);
+        Ctrl_status cs;
+
+        cs = sd_mmc_mem_2_ram_multi(bVolNum, (uint32_t)(ullSectorStart + ulSectorIdx),
+                                    (uint16_t)ulTransfer, &pbBuffer[ulSectorIdx * ulSectorSize]);
+        if(cs != CTRL_GOOD)
+        {
+            ret = -RED_EIO;
+            break;
+        }
+
+        ulSectorIdx += ulTransfer;
+    }
+
+    return ret;
+}
+
+
+#if REDCONF_READ_ONLY == 0
+/** @brief Write sectors to a disk.
+
+    @param bVolNum          The volume number of the volume whose block device
+                            is being written to.
+    @param ullSectorStart   The starting sector number.
+    @param ulSectorCount    The number of sectors to write.
+    @param pBuffer          The buffer from which to write the sector data.
+
+    @return A negated ::REDSTATUS code indicating the operation result.
+
+    @retval 0   Operation was successful.
+*/
+static REDSTATUS DiskWrite(
+    uint8_t         bVolNum,
+    uint64_t        ullSectorStart,
+    uint32_t        ulSectorCount,
+    const void     *pBuffer)
+{
+    REDSTATUS       ret = 0;
+    uint32_t        ulSectorIdx = 0U;
+    uint32_t        ulSectorSize = gaRedVolConf[bVolNum].ulSectorSize;
+    const uint8_t  *pbBuffer = CAST_VOID_PTR_TO_CONST_UINT8_PTR(pBuffer);
+
+    while(ulSectorIdx < ulSectorCount)
+    {
+        uint32_t    ulTransfer = REDMIN(ulSectorCount - ulSectorIdx, MAX_SECTOR_TRANSFER);
+        Ctrl_status cs;
+
+        cs = sd_mmc_ram_2_mem_multi(bVolNum, (uint32_t)(ullSectorStart + ulSectorIdx),
+                                    (uint16_t)ulTransfer, &pbBuffer[ulSectorIdx * ulSectorSize]);
+        if(cs != CTRL_GOOD)
+        {
+            ret = -RED_EIO;
+            break;
+        }
+
+        ulSectorIdx += ulTransfer;
+    }
+
+    return ret;
+}
+
+
+/** @brief Flush any caches beneath the file system.
+
+    @param bVolNum  The volume number of the volume whose block device is being
+                    flushed.
+
+    @return A negated ::REDSTATUS code indicating the operation result.
+
+    @retval 0   Operation was successful.
+*/
+static REDSTATUS DiskFlush(
+    uint8_t     bVolNum)
+{
+    REDSTATUS   ret;
+    Ctrl_status cs;
+
+    /*  The ASF SD/MMC driver appears to write sectors synchronously, so it
+        should be fine to do nothing and return success.  However, Atmel's
+        implementation of the FatFs diskio.c file does the equivalent of the
+        below when the disk is flushed.  Just in case this is important for some
+        non-obvious reason, do the same.
+    */
+    cs = sd_mmc_test_unit_ready(bVolNum);
+    if(cs == CTRL_GOOD)
+    {
+        ret = 0;
+    }
+    else
+    {
+        ret = -RED_EIO;
+    }
+
+    return ret;
+}
+#endif /* REDCONF_READ_ONLY == 0 */
+
+#elif BDEV_EXAMPLE_IMPLEMENTATION == BDEV_STM32_SDIO
+
+#ifdef USE_STM324xG_EVAL
+  #include <stm324xg_eval.h>
+  #include <stm324xg_eval_sd.h>
+#elif defined(USE_STM32746G_DISCO)
+  #include <stm32746g_discovery.h>
+  #include <stm32746g_discovery_sd.h>
+#else
+  /*  If you are using a compatible STM32 device other than the two listed above
+      and you have SD card driver headers, you can try adding them to the above
+      list.
+  */
+  #error "Unsupported device."
+#endif
+
+#if REDCONF_VOLUME_COUNT > 1
+  #error "The STM32 SDIO block device implementation does not support multiple volumes."
+#endif
+
+
+#ifndef USE_HAL_DRIVER
+  #error "The STM32 StdPeriph driver is not supported. Please use the HAL driver or modify the Reliance Edge block device interface."
+#endif
+
+/** @brief Number of times to call BSP_SD_GetStatus() before timing out and
+           returning an error.
+
+    See ::CheckStatus().
+
+    NOTE: Datalight has not observed a scenario where BSP_SD_GetStatus()
+    returns SD_TRANSFER_BUSY after a transfer command returns successfully.
+    Set SD_STATUS_TIMEOUT to 0U to skip checking BSP_SD_GetStatus().
+*/
+#define SD_STATUS_TIMEOUT (100000U)
+
+/** @brief 4-byte aligned buffer to use for DMA transfers when passed in
+           an unaligned buffer.
+*/
+static uint32_t gaulAlignedBuffer[512U / sizeof(uint32_t)];
+
+
+#if SD_STATUS_TIMEOUT > 0U
+static REDSTATUS CheckStatus(void);
+#endif
+
+
+/** @brief Initialize a disk.
+
+    @param bVolNum  The volume number of the volume whose block device is being
+                    initialized.
+    @param mode     The open mode, indicating the type of access required.
+
+    @return A negated ::REDSTATUS code indicating the operation result.
+
+    @retval 0           Operation was successful.
+    @retval -RED_EIO    No SD card was found; or BSP_SD_Init() failed.
+    @retval -RED_EINVAL The SD card's block size is not the same as the
+                        configured sector size; or the SD card is not large
+                        enough for the volume; or the volume size is above
+                        4GiB, meaning that part of it cannot be accessed
+                        through the STM32 SDIO driver.
+*/
+static REDSTATUS DiskOpen(
+    uint8_t         bVolNum,
+    BDEVOPENMODE    mode)
+{
+    REDSTATUS       ret = 0;
+    static bool     fSdInitted = false;
+
+    (void) mode;
+
+    if(!fSdInitted)
+    {
+        if(BSP_SD_Init() == MSD_OK)
+        {
+            fSdInitted = true;
+        }
+    }
+
+    if(!fSdInitted)
+    {
+        /*  Above initialization attempt failed.
+        */
+        ret = -RED_EIO;
+    }
+    else if(BSP_SD_IsDetected() == SD_NOT_PRESENT)
+    {
+        ret = -RED_EIO;
+    }
+    else
+    {
+        uint32_t                ulSectorSize = gaRedVolConf[bVolNum].ulSectorSize;
+        HAL_SD_CardInfoTypedef  sdCardInfo = {{0}};
+
+        BSP_SD_GetCardInfo(&sdCardInfo);
+
+        /*  Note: the actual card block size is sdCardInfo.CardBlockSize,
+            but the interface only supports a 512 byte block size. Further,
+            one card has been observed to report a 1024-byte block size,
+            but it worked fine with a 512-byte Reliance Edge ulSectorSize.
+        */
+        if(    (ulSectorSize != 512U)
+            || (sdCardInfo.CardCapacity < (gaRedVolConf[bVolNum].ullSectorCount * ulSectorSize)))
+        {
+            ret = -RED_EINVAL;
+        }
+    }
+
+    return ret;
+}
+
+
+/** @brief Uninitialize a disk.
+
+    @param bVolNum  The volume number of the volume whose block device is being
+                    uninitialized.
+
+    @return A negated ::REDSTATUS code indicating the operation result.
+
+    @retval 0   Operation was successful.
+*/
+static REDSTATUS DiskClose(
+    uint8_t     bVolNum)
+{
+    (void)bVolNum;
+    return 0;
+}
+
+
+/** @brief Read sectors from a disk.
+
+    @param bVolNum          The volume number of the volume whose block device
+                            is being read from.
+    @param ullSectorStart   The starting sector number.
+    @param ulSectorCount    The number of sectors to read.
+    @param pBuffer          The buffer into which to read the sector data.
+
+    @return A negated ::REDSTATUS code indicating the operation result.
+
+    @retval 0           Operation was successful.
+    @retval -RED_EIO    A disk I/O error occurred.
+*/
+static REDSTATUS DiskRead(
+    uint8_t     bVolNum,
+    uint64_t    ullSectorStart,
+    uint32_t    ulSectorCount,
+    void       *pBuffer)
+{
+    REDSTATUS   redStat = 0;
+    uint32_t    ulSectorSize = gaRedVolConf[bVolNum].ulSectorSize;
+    uint8_t     bSdError;
+
+    if(IS_UINT32_ALIGNED_PTR(pBuffer))
+    {
+        bSdError = BSP_SD_ReadBlocks_DMA(CAST_UINT32_PTR(pBuffer), ullSectorStart * ulSectorSize, ulSectorSize, ulSectorCount);
+
+        if(bSdError != MSD_OK)
+        {
+            redStat = -RED_EIO;
+        }
+      #if SD_STATUS_TIMEOUT > 0U
+        else
+        {
+            redStat = CheckStatus();
+        }
+      #endif
+    }
+    else
+    {
+        uint32_t ulSectorIdx;
+
+        for(ulSectorIdx = 0U; ulSectorIdx < ulSectorCount; ulSectorIdx++)
+        {
+            bSdError = BSP_SD_ReadBlocks_DMA(gaulAlignedBuffer, (ullSectorStart + ulSectorIdx) * ulSectorSize, ulSectorSize, 1U);
+
+            if(bSdError != MSD_OK)
+            {
+                redStat = -RED_EIO;
+            }
+          #if SD_STATUS_TIMEOUT > 0U
+            else
+            {
+                redStat = CheckStatus();
+            }
+          #endif
+
+            if(redStat == 0)
+            {
+                uint8_t *pbBuffer = CAST_VOID_PTR_TO_UINT8_PTR(pBuffer);
+
+                RedMemCpy(&pbBuffer[ulSectorIdx * ulSectorSize], gaulAlignedBuffer, ulSectorSize);
+            }
+            else
+            {
+                break;
+            }
+        }
+    }
+
+    return redStat;
+}
+
+
+#if REDCONF_READ_ONLY == 0
+/** @brief Write sectors to a disk.
+
+    @param bVolNum          The volume number of the volume whose block device
+                            is being written to.
+    @param ullSectorStart   The starting sector number.
+    @param ulSectorCount    The number of sectors to write.
+    @param pBuffer          The buffer from which to write the sector data.
+
+    @return A negated ::REDSTATUS code indicating the operation result.
+
+    @retval 0           Operation was successful.
+    @retval -RED_EIO    A disk I/O error occurred.
+*/
+static REDSTATUS DiskWrite(
+    uint8_t         bVolNum,
+    uint64_t        ullSectorStart,
+    uint32_t        ulSectorCount,
+    const void     *pBuffer)
+{
+    REDSTATUS       redStat = 0;
+    uint32_t        ulSectorSize = gaRedVolConf[bVolNum].ulSectorSize;
+    uint8_t         bSdError;
+
+    if(IS_UINT32_ALIGNED_PTR(pBuffer))
+    {
+        bSdError = BSP_SD_WriteBlocks_DMA(CAST_UINT32_PTR(CAST_AWAY_CONST(void, pBuffer)), ullSectorStart * ulSectorSize,
+                                          ulSectorSize, ulSectorCount);
+
+        if(bSdError != MSD_OK)
+        {
+            redStat = -RED_EIO;
+        }
+      #if SD_STATUS_TIMEOUT > 0U
+        else
+        {
+            redStat = CheckStatus();
+        }
+      #endif
+    }
+    else
+    {
+        uint32_t ulSectorIdx;
+
+        for(ulSectorIdx = 0U; ulSectorIdx < ulSectorCount; ulSectorIdx++)
+        {
+            const uint8_t *pbBuffer = CAST_VOID_PTR_TO_CONST_UINT8_PTR(pBuffer);
+
+            RedMemCpy(gaulAlignedBuffer, &pbBuffer[ulSectorIdx * ulSectorSize], ulSectorSize);
+
+            bSdError = BSP_SD_WriteBlocks_DMA(gaulAlignedBuffer, (ullSectorStart + ulSectorIdx) * ulSectorSize, ulSectorSize, 1U);
+
+            if(bSdError != MSD_OK)
+            {
+                redStat = -RED_EIO;
+            }
+          #if SD_STATUS_TIMEOUT > 0U
+            else
+            {
+                redStat = CheckStatus();
+            }
+          #endif
+
+            if(redStat != 0)
+            {
+                break;
+            }
+        }
+    }
+
+    return redStat;
+}
+
+
+/** @brief Flush any caches beneath the file system.
+
+    @param bVolNum  The volume number of the volume whose block device is being
+                    flushed.
+
+    @return A negated ::REDSTATUS code indicating the operation result.
+
+    @retval 0   Operation was successful.
+*/
+static REDSTATUS DiskFlush(
+    uint8_t     bVolNum)
+{
+    /*  Disk transfer is synchronous; nothing to flush.
+    */
+    (void) bVolNum;
+    return 0;
+}
+
+
+#if SD_STATUS_TIMEOUT > 0U
+/** @brief Wait until BSP_SD_GetStatus returns SD_TRANSFER_OK.
+
+    This function calls BSP_SD_GetStatus repeatedly as long as it returns
+    SD_TRANSFER_BUSY up to SD_STATUS_TIMEOUT times.
+
+    @return A negated ::REDSTATUS code indicating the operation result.
+
+    @retval 0           SD_TRANSFER_OK was returned.
+    @retval -RED_EIO    SD_TRANSFER_ERROR received, or timed out waiting for
+                        SD_TRANSFER_OK.
+*/
+static REDSTATUS CheckStatus(void)
+{
+    REDSTATUS                   redStat = 0;
+    uint32_t                    ulTimeout = SD_STATUS_TIMEOUT;
+    HAL_SD_TransferStateTypedef transferState;
+
+    do
+    {
+        transferState = BSP_SD_GetStatus();
+        ulTimeout--;
+    } while((transferState == SD_TRANSFER_BUSY) && (ulTimeout > 0U));
+
+    if(transferState != SD_TRANSFER_OK)
+    {
+        redStat = -RED_EIO;
+    }
+
+    return redStat;
+}
+#endif
+
+#endif /* REDCONF_READ_ONLY == 0 */
+
+#elif BDEV_EXAMPLE_IMPLEMENTATION == BDEV_RAM_DISK
+
+#include <stdlib.h> /* For ALLOCATE_CLEARED_MEMORY(), which expands to calloc(). */
+
+
+static uint8_t *gapbRamDisk[REDCONF_VOLUME_COUNT];
+
+
+/** @brief Initialize a disk.
+
+    @param bVolNum  The volume number of the volume whose block device is being
+                    initialized.
+    @param mode     The open mode, indicating the type of access required.
+
+    @return A negated ::REDSTATUS code indicating the operation result.
+
+    @retval 0           Operation was successful.
+    @retval -RED_EIO    A disk I/O error occurred.
+*/
+static REDSTATUS DiskOpen(
+    uint8_t         bVolNum,
+    BDEVOPENMODE    mode)
+{
+    REDSTATUS       ret = 0;
+
+    (void)mode;
+
+    if(gapbRamDisk[bVolNum] == NULL)
+    {
+        gapbRamDisk[bVolNum] = ALLOCATE_CLEARED_MEMORY(gaRedVolume[bVolNum].ulBlockCount, REDCONF_BLOCK_SIZE);
+        if(gapbRamDisk[bVolNum] == NULL)
+        {
+            ret = -RED_EIO;
+        }
+    }
+
+    return ret;
+}
+
+
+/** @brief Uninitialize a disk.
+
+    @param bVolNum  The volume number of the volume whose block device is being
+                    uninitialized.
+
+    @return A negated ::REDSTATUS code indicating the operation result.
+
+    @retval 0   Operation was successful.
+*/
+static REDSTATUS DiskClose(
+    uint8_t     bVolNum)
+{
+    REDSTATUS   ret;
+
+    if(gapbRamDisk[bVolNum] == NULL)
+    {
+        ret = -RED_EINVAL;
+    }
+    else
+    {
+        /*  This implementation uses dynamically allocated memory, but must
+            retain previously written data after the block device is closed, and
+            thus the memory cannot be freed and will remain allocated until
+            reboot.
+        */
+        ret = 0;
+    }
+
+    return ret;
+}
+
+
+/** @brief Read sectors from a disk.
+
+    @param bVolNum          The volume number of the volume whose block device
+                            is being read from.
+    @param ullSectorStart   The starting sector number.
+    @param ulSectorCount    The number of sectors to read.
+    @param pBuffer          The buffer into which to read the sector data.
+
+    @return A negated ::REDSTATUS code indicating the operation result.
+
+    @retval 0   Operation was successful.
+*/
+static REDSTATUS DiskRead(
+    uint8_t     bVolNum,
+    uint64_t    ullSectorStart,
+    uint32_t    ulSectorCount,
+    void       *pBuffer)
+{
+    REDSTATUS   ret;
+
+    if(gapbRamDisk[bVolNum] == NULL)
+    {
+        ret = -RED_EINVAL;
+    }
+    else
+    {
+        uint64_t ullByteOffset = ullSectorStart * gaRedVolConf[bVolNum].ulSectorSize;
+        uint32_t ulByteCount = ulSectorCount * gaRedVolConf[bVolNum].ulSectorSize;
+
+        RedMemCpy(pBuffer, &gapbRamDisk[bVolNum][ullByteOffset], ulByteCount);
+
+        ret = 0;
+    }
+
+    return ret;
+}
+
+
+#if REDCONF_READ_ONLY == 0
+/** @brief Write sectors to a disk.
+
+    @param bVolNum          The volume number of the volume whose block device
+                            is being written to.
+    @param ullSectorStart   The starting sector number.
+    @param ulSectorCount    The number of sectors to write.
+    @param pBuffer          The buffer from which to write the sector data.
+
+    @return A negated ::REDSTATUS code indicating the operation result.
+
+    @retval 0   Operation was successful.
+*/
+static REDSTATUS DiskWrite(
+    uint8_t     bVolNum,
+    uint64_t    ullSectorStart,
+    uint32_t    ulSectorCount,
+    const void *pBuffer)
+{
+    REDSTATUS   ret;
+
+    if(gapbRamDisk[bVolNum] == NULL)
+    {
+        ret = -RED_EINVAL;
+    }
+    else
+    {
+        uint64_t ullByteOffset = ullSectorStart * gaRedVolConf[bVolNum].ulSectorSize;
+        uint32_t ulByteCount = ulSectorCount * gaRedVolConf[bVolNum].ulSectorSize;
+
+        RedMemCpy(&gapbRamDisk[bVolNum][ullByteOffset], pBuffer, ulByteCount);
+
+        ret = 0;
+    }
+
+    return ret;
+}
+
+
+/** @brief Flush any caches beneath the file system.
+
+    @param bVolNum  The volume number of the volume whose block device is being
+                    flushed.
+
+    @return A negated ::REDSTATUS code indicating the operation result.
+
+    @retval 0   Operation was successful.
+*/
+static REDSTATUS DiskFlush(
+    uint8_t     bVolNum)
+{
+    REDSTATUS   ret;
+
+    if(gapbRamDisk[bVolNum] == NULL)
+    {
+        ret = -RED_EINVAL;
+    }
+    else
+    {
+        ret = 0;
+    }
+
+    return ret;
+}
+#endif /* REDCONF_READ_ONLY == 0 */
+
+#else
+
+#error "Invalid BDEV_EXAMPLE_IMPLEMENTATION value"
+
+#endif /* BDEV_EXAMPLE_IMPLEMENTATION == ... */
+
index 0bc2c701b11e07e8b8179170860eaf0ca0e2f56f..68cd0eda0f4b6dea4d613a4b1a62e1ee7477fc99 100644 (file)
-/*             ----> DO NOT REMOVE THE FOLLOWING NOTICE <----\r
-\r
-                   Copyright (c) 2014-2015 Datalight, Inc.\r
-                       All Rights Reserved Worldwide.\r
-\r
-    This program is free software; you can redistribute it and/or modify\r
-    it under the terms of the GNU General Public License as published by\r
-    the Free Software Foundation; use version 2 of the License.\r
-\r
-    This program is distributed in the hope that it will be useful,\r
-    but "AS-IS," WITHOUT ANY WARRANTY; without even the implied warranty\r
-    of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
-    GNU General Public License for more details.\r
-\r
-    You should have received a copy of the GNU General Public License along\r
-    with this program; if not, write to the Free Software Foundation, Inc.,\r
-    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
-*/\r
-/*  Businesses and individuals that for commercial or other reasons cannot\r
-    comply with the terms of the GPLv2 license may obtain a commercial license\r
-    before incorporating Reliance Edge into proprietary software for\r
-    distribution in any form.  Visit http://www.datalight.com/reliance-edge for\r
-    more information.\r
-*/\r
-/** @file\r
-    @brief Implements a synchronization object to provide mutual exclusion.\r
-*/\r
-#include <FreeRTOS.h>\r
-#include <semphr.h>\r
-\r
-#include <redfs.h>\r
-\r
-#if REDCONF_TASK_COUNT > 1U\r
-\r
-\r
-static SemaphoreHandle_t xMutex;\r
-\r
-\r
-/** @brief Initialize the mutex.\r
-\r
-    After initialization, the mutex is in the released state.\r
-\r
-    The behavior of calling this function when the mutex is still initialized\r
-    is undefined.\r
-\r
-    @return A negated ::REDSTATUS code indicating the operation result.\r
-\r
-    @retval 0   Operation was successful.\r
-*/\r
-REDSTATUS RedOsMutexInit(void)\r
-{\r
-    REDSTATUS ret;\r
-\r
-    xMutex = xSemaphoreCreateMutex();\r
-    if(xMutex != NULL)\r
-    {\r
-        ret = 0;\r
-    }\r
-    else\r
-    {\r
-        ret = -RED_ENOMEM;\r
-    }\r
-\r
-    return ret;\r
-}\r
-\r
-\r
-/** @brief Uninitialize the mutex.\r
-\r
-    The behavior of calling this function when the mutex is not initialized is\r
-    undefined; likewise, the behavior of uninitializing the mutex when it is\r
-    in the acquired state is undefined.\r
-\r
-    @return A negated ::REDSTATUS code indicating the operation result.\r
-\r
-    @retval 0   Operation was successful.\r
-*/\r
-REDSTATUS RedOsMutexUninit(void)\r
-{\r
-    vSemaphoreDelete(xMutex);\r
-    xMutex = NULL;\r
-\r
-    return 0;\r
-}\r
-\r
-\r
-/** @brief Acquire the mutex.\r
-\r
-    The behavior of calling this function when the mutex is not initialized is\r
-    undefined; likewise, the behavior of recursively acquiring the mutex is\r
-    undefined.\r
-*/\r
-void RedOsMutexAcquire(void)\r
-{\r
-    while(xSemaphoreTake(xMutex, portMAX_DELAY) != pdTRUE)\r
-    {\r
-    }\r
-}\r
-\r
-\r
-/** @brief Release the mutex.\r
-\r
-    The behavior is undefined in the following cases:\r
-\r
-    - Releasing the mutex when the mutex is not initialized.\r
-    - Releasing the mutex when it is not in the acquired state.\r
-    - Releasing the mutex from a task or thread other than the one which\r
-      acquired the mutex.\r
-*/\r
-void RedOsMutexRelease(void)\r
-{\r
-    BaseType_t xSuccess;\r
-\r
-    xSuccess = xSemaphoreGive(xMutex);\r
-    REDASSERT(xSuccess == pdTRUE);\r
-    (void)xSuccess;\r
-}\r
-\r
-#endif\r
-\r
+/*             ----> DO NOT REMOVE THE FOLLOWING NOTICE <----
+
+                   Copyright (c) 2014-2015 Datalight, Inc.
+                       All Rights Reserved Worldwide.
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; use version 2 of the License.
+
+    This program is distributed in the hope that it will be useful,
+    but "AS-IS," WITHOUT ANY WARRANTY; without even the implied warranty
+    of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+/*  Businesses and individuals that for commercial or other reasons cannot
+    comply with the terms of the GPLv2 license may obtain a commercial license
+    before incorporating Reliance Edge into proprietary software for
+    distribution in any form.  Visit http://www.datalight.com/reliance-edge for
+    more information.
+*/
+/** @file
+    @brief Implements a synchronization object to provide mutual exclusion.
+*/
+#include <FreeRTOS.h>
+#include <semphr.h>
+
+#include <redfs.h>
+#include <redosdeviations.h>
+
+#if REDCONF_TASK_COUNT > 1U
+
+
+static SemaphoreHandle_t xMutex;
+#if defined(configSUPPORT_STATIC_ALLOCATION) && (configSUPPORT_STATIC_ALLOCATION == 1)
+static StaticSemaphore_t xMutexBuffer;
+#endif
+
+
+/** @brief Initialize the mutex.
+
+    After initialization, the mutex is in the released state.
+
+    The behavior of calling this function when the mutex is still initialized
+    is undefined.
+
+    @return A negated ::REDSTATUS code indicating the operation result.
+
+    @retval 0   Operation was successful.
+*/
+REDSTATUS RedOsMutexInit(void)
+{
+    REDSTATUS ret = 0;
+    
+  #if defined(configSUPPORT_STATIC_ALLOCATION) && (configSUPPORT_STATIC_ALLOCATION == 1)
+    xMutex = xSemaphoreCreateMutexStatic(&xMutexBuffer);
+    
+    if(xMutex == NULL)
+    {
+        /*  The only error case for xSemaphoreCreateMutexStatic is that the mutex
+            buffer parameter is NULL, which is not the case.
+        */
+        REDERROR();
+        ret = -RED_EINVAL;
+    }        
+    
+  #else
+    xMutex = xSemaphoreCreateMutex();
+    if(xMutex == NULL)
+    {
+        ret = -RED_ENOMEM;
+    }
+  #endif
+
+    return ret;
+}
+
+
+/** @brief Uninitialize the mutex.
+
+    The behavior of calling this function when the mutex is not initialized is
+    undefined; likewise, the behavior of uninitializing the mutex when it is
+    in the acquired state is undefined.
+
+    @return A negated ::REDSTATUS code indicating the operation result.
+
+    @retval 0   Operation was successful.
+*/
+REDSTATUS RedOsMutexUninit(void)
+{
+    vSemaphoreDelete(xMutex);
+    xMutex = NULL;
+
+    return 0;
+}
+
+
+/** @brief Acquire the mutex.
+
+    The behavior of calling this function when the mutex is not initialized is
+    undefined; likewise, the behavior of recursively acquiring the mutex is
+    undefined.
+*/
+void RedOsMutexAcquire(void)
+{
+    while(xSemaphoreTake(xMutex, portMAX_DELAY) != pdTRUE)
+    {
+    }
+}
+
+
+/** @brief Release the mutex.
+
+    The behavior is undefined in the following cases:
+
+    - Releasing the mutex when the mutex is not initialized.
+    - Releasing the mutex when it is not in the acquired state.
+    - Releasing the mutex from a task or thread other than the one which
+      acquired the mutex.
+*/
+void RedOsMutexRelease(void)
+{
+    BaseType_t xSuccess;
+
+    xSuccess = xSemaphoreGive(xMutex);
+    REDASSERT(xSuccess == pdTRUE);
+    IGNORE_ERRORS(xSuccess);
+}
+
+#endif
+
diff --git a/FreeRTOS-Plus/Source/Reliance-Edge/posix/redpath.h b/FreeRTOS-Plus/Source/Reliance-Edge/posix/redpath.h
deleted file mode 100644 (file)
index b7337e1..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-/*             ----> DO NOT REMOVE THE FOLLOWING NOTICE <----\r
-\r
-                   Copyright (c) 2014-2015 Datalight, Inc.\r
-                       All Rights Reserved Worldwide.\r
-\r
-    This program is free software; you can redistribute it and/or modify\r
-    it under the terms of the GNU General Public License as published by\r
-    the Free Software Foundation; use version 2 of the License.\r
-\r
-    This program is distributed in the hope that it will be useful,\r
-    but "AS-IS," WITHOUT ANY WARRANTY; without even the implied warranty\r
-    of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
-    GNU General Public License for more details.\r
-\r
-    You should have received a copy of the GNU General Public License along\r
-    with this program; if not, write to the Free Software Foundation, Inc.,\r
-    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
-*/\r
-/*  Businesses and individuals that for commercial or other reasons cannot\r
-    comply with the terms of the GPLv2 license may obtain a commercial license\r
-    before incorporating Reliance Edge into proprietary software for\r
-    distribution in any form.  Visit http://www.datalight.com/reliance-edge for\r
-    more information.\r
-*/\r
-/** @file\r
-    @brief Interfaces of path utilities for the POSIX-like API layer.\r
-*/\r
-#ifndef REDPATH_H\r
-#define REDPATH_H\r
-\r
-\r
-REDSTATUS RedPathSplit(const char *pszPath, uint8_t *pbVolNum, const char **ppszLocalPath);\r
-REDSTATUS RedPathLookup(const char *pszLocalPath, uint32_t *pulInode);\r
-REDSTATUS RedPathToName(const char *pszLocalPath, uint32_t *pulPInode, const char **ppszName);\r
-\r
-\r
-#endif\r
-\r
index 3ae98a4b31fc2909964367226e95bf5b67c40480..146b1fc84fc37523c2c0e1bd0c67b7d3f4e0f9e9 100644 (file)
@@ -891,6 +891,7 @@ static PRINTTYPE ParseFormatType(
                         case 'u':\r
                             fmtType = MAPULONGLONG;\r
                             break;\r
+                        case 'x':\r
                         case 'X':\r
                             fmtType = MAPHEXULONGLONG;\r
                             break;\r