\r
const VOLCONF gaRedVolConf[REDCONF_VOLUME_COUNT] =\r
{\r
- { 512U, 65536U, false, 256U, "" }\r
+ { 512U, 65536U, false, 256U, 0, "" }\r
};\r
<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
<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
-/* ----> 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 */
+
-/* ----> 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;
+}
+
-# 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.
+
-\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.
--- /dev/null
+/* ----> 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
+
-/* ----> 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
+
<!-- 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. */
/** @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
-/* ----> 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
+
-/* ----> 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
+
-/* ----> 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
+
-/* ----> 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 == ... */
+
-/* ----> 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
+
+++ /dev/null
-/* ----> 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
case 'u':\r
fmtType = MAPULONGLONG;\r
break;\r
+ case 'x':\r
case 'X':\r
fmtType = MAPHEXULONGLONG;\r
break;\r