From 55448ef2d60896d98f6d0d7326cb67b03b59412f Mon Sep 17 00:00:00 2001 From: rtel Date: Mon, 21 Nov 2016 04:30:49 +0000 Subject: [PATCH] Update version of Reliance Edge. git-svn-id: https://svn.code.sf.net/p/freertos/code/trunk@2472 1d2547de-c912-0410-9cb9-b8ca96c0e9e2 --- .../ConfigurationFiles/redconf.c | 2 +- .../WIN32.vcxproj | 1 - .../WIN32.vcxproj.filters | 3 - .../Reliance-Edge/core/driver/blockio.c | 362 ++- .../Source/Reliance-Edge/core/driver/buffer.c | 2424 +++++++-------- .../Source/Reliance-Edge/doc/release_notes.md | 163 +- .../Reliance-Edge/doc/release_notes.txt | 189 +- .../{posix => include}/redpath.h | 76 +- .../Source/Reliance-Edge/include/redtests.h | 514 +-- .../Source/Reliance-Edge/include/redver.h | 14 +- .../Source/Reliance-Edge/include/redvolume.h | 275 +- .../os/freertos/include/redosdeviations.h | 409 ++- .../os/freertos/services/osassert.c | 110 +- .../os/freertos/services/osbdev.c | 2765 +++++++++-------- .../os/freertos/services/osmutex.c | 254 +- .../Source/Reliance-Edge/tests/util/printf.c | 1 + 16 files changed, 4069 insertions(+), 3493 deletions(-) rename FreeRTOS-Plus/Source/Reliance-Edge/{posix => include}/redpath.h (97%) diff --git a/FreeRTOS-Plus/Demo/FreeRTOS_Plus_Reliance_Edge_and_CLI_Windows_Simulator/ConfigurationFiles/redconf.c b/FreeRTOS-Plus/Demo/FreeRTOS_Plus_Reliance_Edge_and_CLI_Windows_Simulator/ConfigurationFiles/redconf.c index 1a6a6e19d..65feb5546 100644 --- a/FreeRTOS-Plus/Demo/FreeRTOS_Plus_Reliance_Edge_and_CLI_Windows_Simulator/ConfigurationFiles/redconf.c +++ b/FreeRTOS-Plus/Demo/FreeRTOS_Plus_Reliance_Edge_and_CLI_Windows_Simulator/ConfigurationFiles/redconf.c @@ -11,5 +11,5 @@ const VOLCONF gaRedVolConf[REDCONF_VOLUME_COUNT] = { - { 512U, 65536U, false, 256U, "" } + { 512U, 65536U, false, 256U, 0, "" } }; diff --git a/FreeRTOS-Plus/Demo/FreeRTOS_Plus_Reliance_Edge_and_CLI_Windows_Simulator/WIN32.vcxproj b/FreeRTOS-Plus/Demo/FreeRTOS_Plus_Reliance_Edge_and_CLI_Windows_Simulator/WIN32.vcxproj index dbbae63cd..6d377dc35 100644 --- a/FreeRTOS-Plus/Demo/FreeRTOS_Plus_Reliance_Edge_and_CLI_Windows_Simulator/WIN32.vcxproj +++ b/FreeRTOS-Plus/Demo/FreeRTOS_Plus_Reliance_Edge_and_CLI_Windows_Simulator/WIN32.vcxproj @@ -189,7 +189,6 @@ - diff --git a/FreeRTOS-Plus/Demo/FreeRTOS_Plus_Reliance_Edge_and_CLI_Windows_Simulator/WIN32.vcxproj.filters b/FreeRTOS-Plus/Demo/FreeRTOS_Plus_Reliance_Edge_and_CLI_Windows_Simulator/WIN32.vcxproj.filters index fd68b27c1..7d355d1e2 100644 --- a/FreeRTOS-Plus/Demo/FreeRTOS_Plus_Reliance_Edge_and_CLI_Windows_Simulator/WIN32.vcxproj.filters +++ b/FreeRTOS-Plus/Demo/FreeRTOS_Plus_Reliance_Edge_and_CLI_Windows_Simulator/WIN32.vcxproj.filters @@ -209,9 +209,6 @@ Configuration Files - - FreeRTOS+\FreeRTOS+Reliance Edge\port - Configuration Files diff --git a/FreeRTOS-Plus/Source/Reliance-Edge/core/driver/blockio.c b/FreeRTOS-Plus/Source/Reliance-Edge/core/driver/blockio.c index d815ca824..897143de0 100644 --- a/FreeRTOS-Plus/Source/Reliance-Edge/core/driver/blockio.c +++ b/FreeRTOS-Plus/Source/Reliance-Edge/core/driver/blockio.c @@ -1,164 +1,198 @@ -/* ----> 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. -*/ -#include -#include - - -/** @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; - - 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; - - REDASSERT(bSectorShift < 32U); - REDASSERT((ulSectorCount >> bSectorShift) == ulBlockCount); - - ret = RedOsBDevRead(bVolNum, ullSectorStart, ulSectorCount, pBuffer); - } - - 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; - - 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; - - REDASSERT(bSectorShift < 32U); - REDASSERT((ulSectorCount >> bSectorShift) == ulBlockCount); - - ret = RedOsBDevWrite(bVolNum, ullSectorStart, ulSectorCount, pBuffer); - } - - 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; - - if(bVolNum >= REDCONF_VOLUME_COUNT) - { - REDERROR(); - ret = -RED_EINVAL; - } - else - { - ret = RedOsBDevFlush(bVolNum); - } - - CRITICAL_ASSERT(ret == 0); - - return ret; -} -#endif /* REDCONF_READ_ONLY == 0 */ - +/* ----> 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 +#include + + +/** @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 */ + diff --git a/FreeRTOS-Plus/Source/Reliance-Edge/core/driver/buffer.c b/FreeRTOS-Plus/Source/Reliance-Edge/core/driver/buffer.c index b1cfea281..17717e0f4 100644 --- a/FreeRTOS-Plus/Source/Reliance-Edge/core/driver/buffer.c +++ b/FreeRTOS-Plus/Source/Reliance-Edge/core/driver/buffer.c @@ -1,1212 +1,1212 @@ -/* ----> 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 -#include - - -#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) != 0U); - 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; -} - +/* ----> 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 +#include + + +#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; +} + diff --git a/FreeRTOS-Plus/Source/Reliance-Edge/doc/release_notes.md b/FreeRTOS-Plus/Source/Reliance-Edge/doc/release_notes.md index fc94f5ca2..bcc9bff71 100644 --- a/FreeRTOS-Plus/Source/Reliance-Edge/doc/release_notes.md +++ b/FreeRTOS-Plus/Source/Reliance-Edge/doc/release_notes.md @@ -1,61 +1,102 @@ -# 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, 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. - +# Reliance Edge Release Notes + +This file contains a list of updates made to Reliance Edge over the course of +recent releases and a list of known issues. + +## Release History and Changes + +### Reliance Edge v1.0.4, July 2016 + +- Added ARM mbed and ARM mbed OS support in the commercial kit, with an example + projects for ARM mbed OS on the NXP FRDM-K64F board. +- Some minor deficiencies in the POSIX-like API test suite have been addressed. + +### Reliance Edge v1.0.3, June 2016 + +- Added support for static memory allocation configuration in FreeRTOS + version 9. No common code changes. + +### Reliance Edge v1.0.2, February 2016 + +#### Common Code Changes +- A new per-volume configuration option has been added: users can specify a + number of times to retry a block device read, write or flush operation before + returning a failure. The configuration tool has been updated to version 1.0.2 + with this change. + - This added a new field to the volume configuration in to redconf.c: existing + redconf.c files from v1.0.1 and earlier must be updated to work with v1.0.2. + Open redconf.h and redconf.c with the configuration tool, enable + "Retry block device I/O on failure" for any volumes if desired, and save the + redconf files. + +#### FreeRTOS Port Changes +- Added support for the STM32 HAL SD card driver in the FreeRTOS block device + interface. Two boards are supported out-of-the-box: the STM324xG-EVAL and the + STM32F746NG-Discovery. A sample project is included for the STM324xG-EVAL. + +#### MQX Port Changes +- Fixed a bug which prevented Reliance Edge from compiling if the File System + Essentials API was selected in the configuration. +- Fixed a bug which would have returned an uninitialized value from + `RedOsBDevFlush()` for block devices that support flushing. + +### Reliance Edge v1.0.1, October 2015 + +- Added MQX RTOS support in the commercial kit, with example projects for + the Kinetis Design Studio. +- Bug fix in the F_DRIVER implementation of the FreeRTOS block device service. + +### Reliance Edge v1.0, July 2015 + +#### Common Code Changes + +- First release of commercial kit and MISRA C:2012 Design Assurance Package. + The commercial kit includes many new tools and tests which were not previously + available. +- Overhauled parsing of command-line parameters to be consistent for all tools + and tests. Command-line tools now use Unix-style short and long options (such + as `-H` and `--help`) instead of DOS-style switches (such as `/?`). +- Renamed all os/\*/include/ostypes.h headers to os/\*/include/redostypes.h, so + that all headers use the product prefix. If you created a port using v0.9, + this header needs to be renamed and its header guard (#ifndef OSTYPES_H etc.) + should also be updated. +- Add a new header for OS-specific MISRA C:2012 deviation macros, located at + os/\*/include/redosdeviations.h. If you created a port using v0.9, copy the + template from os/stub/include/redosdeviations.h into the include directory. +- Eliminated support for sector sizes less than 256. If using a smaller sector + size (say for a RAM disk), this must now be emulated in the implementation of + the block device OS service. +- Added RedFseFormat() as an optional FSE API, allowing FSE applications to + format the volume at run-time. + - This added a new macro to redconf.h: existing redconf.h files from v0.9 must + be updated to work with v1.0. Open redconf.h with the configuration tool, + ignore the warning about the missing macro, and save it. +- Internal restructuring has renamed the macros for the string and memory + functions used in redconf.h. An existing redconf.h file from v0.9 will need + to be updated; for a file containing the old names, the new config tool will + default to using the (slow) Reliance Edge string/memory functions; to use the + C library or custom versions, this will need to be selected in the + configuration utility. +- Fix a bug which would result in an error when attempting to create a name with + one or more trailing path separators (such as `red_mkdir("/foo/bar/")`). +- Fix a bug where an open handle for an inode on one volume would prevent the + same inode number from being deleted on a different volume. + +#### FreeRTOS Port Changes + +- The implementation of the timestamp OS service no longer requires that + `configUSE_TIMERS` be set to `1`. + +### Reliance Edge v0.9 (Beta), April 2015 + +First public release. + +## Known Issues + +### Visual Studio 2005 + +The Reliance Edge Win32 port (used for the host tools and the Win32 test +project) cannot be compiled by Visual Studio 2005. This is not going to be +fixed since VS2005 is an old toolset. Newer versions of Visual Studio, starting +with Visual Studio 2008, work just fine. + diff --git a/FreeRTOS-Plus/Source/Reliance-Edge/doc/release_notes.txt b/FreeRTOS-Plus/Source/Reliance-Edge/doc/release_notes.txt index e12f1f290..dedfdbb2a 100644 --- a/FreeRTOS-Plus/Source/Reliance-Edge/doc/release_notes.txt +++ b/FreeRTOS-Plus/Source/Reliance-Edge/doc/release_notes.txt @@ -1,71 +1,118 @@ - - -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, 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. + + +RELIANCE EDGE RELEASE NOTES + + +This file contains a list of updates made to Reliance Edge over the +course of recent releases and a list of known issues. + + +Release History and Changes + +Reliance Edge v1.0.4, July 2016 + +- Added ARM mbed and ARM mbed OS support in the commercial kit, with + an example projects for ARM mbed OS on the NXP FRDM-K64F board. +- Some minor deficiencies in the POSIX-like API test suite have + been addressed. + +Reliance Edge v1.0.3, June 2016 + +- Added support for static memory allocation configuration in FreeRTOS + version 9. No common code changes. + +Reliance Edge v1.0.2, February 2016 + +Common Code Changes + +- A new per-volume configuration option has been added: users can + specify a number of times to retry a block device read, write or + flush operation before returning a failure. The configuration tool + has been updated to version 1.0.2 with this change. +- This added a new field to the volume configuration in to redconf.c: + existing redconf.c files from v1.0.1 and earlier must be updated to + work with v1.0.2. Open redconf.h and redconf.c with the + configuration tool, enable "Retry block device I/O on failure" for + any volumes if desired, and save the redconf files. + +FreeRTOS Port Changes + +- Added support for the STM32 HAL SD card driver in the FreeRTOS block + device interface. Two boards are supported out-of-the-box: the + STM324xG-EVAL and the STM32F746NG-Discovery. A sample project is + included for the STM324xG-EVAL. + +MQX Port Changes + +- Fixed a bug which prevented Reliance Edge from compiling if the File + System Essentials API was selected in the configuration. +- Fixed a bug which would have returned an uninitialized value from + RedOsBDevFlush() for block devices that support flushing. + +Reliance Edge v1.0.1, October 2015 + +- Added MQX RTOS support in the commercial kit, with example projects + for the Kinetis Design Studio. +- Bug fix in the F_DRIVER implementation of the FreeRTOS block + device service. + +Reliance Edge v1.0, July 2015 + +Common Code Changes + +- First release of commercial kit and MISRA C:2012 Design + Assurance Package. The commercial kit includes many new tools and + tests which were not previously available. +- Overhauled parsing of command-line parameters to be consistent for + all tools and tests. Command-line tools now use Unix-style short and + long options (such as -H and --help) instead of DOS-style switches + (such as /?). +- Renamed all os/*/include/ostypes.h headers to + os/*/include/redostypes.h, so that all headers use the + product prefix. If you created a port using v0.9, this header needs + to be renamed and its header guard (#ifndef OSTYPES_H etc.) should + also be updated. +- Add a new header for OS-specific MISRA C:2012 deviation macros, + located at os/*/include/redosdeviations.h. If you created a port + using v0.9, copy the template from os/stub/include/redosdeviations.h + into the include directory. +- Eliminated support for sector sizes less than 256. If using a + smaller sector size (say for a RAM disk), this must now be emulated + in the implementation of the block device OS service. +- Added RedFseFormat() as an optional FSE API, allowing FSE + applications to format the volume at run-time. +- This added a new macro to redconf.h: existing redconf.h files from + v0.9 must be updated to work with v1.0. Open redconf.h with the + configuration tool, ignore the warning about the missing macro, and + save it. +- Internal restructuring has renamed the macros for the string and + memory functions used in redconf.h. An existing redconf.h file from + v0.9 will need to be updated; for a file containing the old names, + the new config tool will default to using the (slow) Reliance Edge + string/memory functions; to use the C library or custom versions, + this will need to be selected in the configuration utility. +- Fix a bug which would result in an error when attempting to create a + name with one or more trailing path separators (such as + red_mkdir("/foo/bar/")). +- Fix a bug where an open handle for an inode on one volume would + prevent the same inode number from being deleted on a + different volume. + +FreeRTOS Port Changes + +- The implementation of the timestamp OS service no longer requires + that configUSE_TIMERS be set to 1. + +Reliance Edge v0.9 (Beta), April 2015 + +First public release. + + +Known Issues + +Visual Studio 2005 + +The Reliance Edge Win32 port (used for the host tools and the Win32 test +project) cannot be compiled by Visual Studio 2005. This is not going to +be fixed since VS2005 is an old toolset. Newer versions of Visual +Studio, starting with Visual Studio 2008, work just fine. diff --git a/FreeRTOS-Plus/Source/Reliance-Edge/posix/redpath.h b/FreeRTOS-Plus/Source/Reliance-Edge/include/redpath.h similarity index 97% rename from FreeRTOS-Plus/Source/Reliance-Edge/posix/redpath.h rename to FreeRTOS-Plus/Source/Reliance-Edge/include/redpath.h index b7337e17c..06f7c3637 100644 --- a/FreeRTOS-Plus/Source/Reliance-Edge/posix/redpath.h +++ b/FreeRTOS-Plus/Source/Reliance-Edge/include/redpath.h @@ -1,38 +1,38 @@ -/* ----> DO NOT REMOVE THE FOLLOWING NOTICE <---- - - Copyright (c) 2014-2015 Datalight, Inc. - All Rights Reserved Worldwide. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; use version 2 of the License. - - This program is distributed in the hope that it will be useful, - but "AS-IS," WITHOUT ANY WARRANTY; without even the implied warranty - of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along - with this program; if not, write to the Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -*/ -/* Businesses and individuals that for commercial or other reasons cannot - comply with the terms of the GPLv2 license may obtain a commercial license - before incorporating Reliance Edge into proprietary software for - distribution in any form. Visit http://www.datalight.com/reliance-edge for - more information. -*/ -/** @file - @brief Interfaces of path utilities for the POSIX-like API layer. -*/ -#ifndef REDPATH_H -#define REDPATH_H - - -REDSTATUS RedPathSplit(const char *pszPath, uint8_t *pbVolNum, const char **ppszLocalPath); -REDSTATUS RedPathLookup(const char *pszLocalPath, uint32_t *pulInode); -REDSTATUS RedPathToName(const char *pszLocalPath, uint32_t *pulPInode, const char **ppszName); - - -#endif - +/* ----> 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 + diff --git a/FreeRTOS-Plus/Source/Reliance-Edge/include/redtests.h b/FreeRTOS-Plus/Source/Reliance-Edge/include/redtests.h index 017061e41..cbe768336 100644 --- a/FreeRTOS-Plus/Source/Reliance-Edge/include/redtests.h +++ b/FreeRTOS-Plus/Source/Reliance-Edge/include/redtests.h @@ -1,249 +1,265 @@ -/* ----> 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 -#include "redtestutils.h" -#include "redver.h" - -/* This macro is only defined by the error injection project. -*/ -#ifdef REDCONF_ERROR_INJECTION -#include -#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 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 - +/* ----> 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 +#include "redtestutils.h" +#include "redver.h" + +/* This macro is only defined by the error injection project. +*/ +#ifdef REDCONF_ERROR_INJECTION +#include +#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 + diff --git a/FreeRTOS-Plus/Source/Reliance-Edge/include/redver.h b/FreeRTOS-Plus/Source/Reliance-Edge/include/redver.h index de4a1a1a1..9cfb84259 100644 --- a/FreeRTOS-Plus/Source/Reliance-Edge/include/redver.h +++ b/FreeRTOS-Plus/Source/Reliance-Edge/include/redver.h @@ -33,7 +33,7 @@ */ -#define RED_BUILD_NUMBER "664" +#define RED_BUILD_NUMBER "677" #define RED_KIT_GPL 0U /* Open source GPL kit. */ #define RED_KIT_COMMERCIAL 1U /* Commercially-licensed kit. */ @@ -48,7 +48,7 @@ /** @brief Version number to display in output. */ -#define RED_VERSION "v1.0" +#define RED_VERSION "v1.0.4" /** @brief On-disk version number. @@ -78,12 +78,12 @@ /** @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. @@ -94,11 +94,11 @@ /** @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 diff --git a/FreeRTOS-Plus/Source/Reliance-Edge/include/redvolume.h b/FreeRTOS-Plus/Source/Reliance-Edge/include/redvolume.h index 3b1758ed4..266cf5e15 100644 --- a/FreeRTOS-Plus/Source/Reliance-Edge/include/redvolume.h +++ b/FreeRTOS-Plus/Source/Reliance-Edge/include/redvolume.h @@ -1,134 +1,141 @@ -/* ----> 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; - - #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 <---- + + 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 + diff --git a/FreeRTOS-Plus/Source/Reliance-Edge/os/freertos/include/redosdeviations.h b/FreeRTOS-Plus/Source/Reliance-Edge/os/freertos/include/redosdeviations.h index 6a8a5920f..cfde3a708 100644 --- a/FreeRTOS-Plus/Source/Reliance-Edge/os/freertos/include/redosdeviations.h +++ b/FreeRTOS-Plus/Source/Reliance-Edge/os/freertos/include/redosdeviations.h @@ -1,165 +1,244 @@ -/* ----> 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 -#endif - - -#if REDCONF_ASSERTS == 1 -#if 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) \ - (void)printf("Assertion failed in \"%s\" at line %u\n\r", ((file) == NULL) ? "" : (file), (unsigned)(line)) -#else -#define PRINT_ASSERT(file, line) do { (void)(file); (void)(line); } while(false) -#endif /* REDCONF_OUTPUT == 1 */ -#endif /* REDCONF_ASSERTS == 1 */ - - -/** 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 - - -#endif - +/* ----> 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 +#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 + diff --git a/FreeRTOS-Plus/Source/Reliance-Edge/os/freertos/services/osassert.c b/FreeRTOS-Plus/Source/Reliance-Edge/os/freertos/services/osassert.c index 3b322cee8..9822caf5c 100644 --- a/FreeRTOS-Plus/Source/Reliance-Edge/os/freertos/services/osassert.c +++ b/FreeRTOS-Plus/Source/Reliance-Edge/os/freertos/services/osassert.c @@ -1,54 +1,56 @@ -/* ----> 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 - -#if REDCONF_ASSERTS == 1 - -#include - - -/** @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) -{ - PRINT_ASSERT(pszFileName, ulLineNum); - - for( ;; ) - { - } -} - -#endif - +/* ----> 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 + +#if REDCONF_ASSERTS == 1 + +#include + + +/** @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 + diff --git a/FreeRTOS-Plus/Source/Reliance-Edge/os/freertos/services/osbdev.c b/FreeRTOS-Plus/Source/Reliance-Edge/os/freertos/services/osbdev.c index c486e7c7e..8fa1fcdaf 100644 --- a/FreeRTOS-Plus/Source/Reliance-Edge/os/freertos/services/osbdev.c +++ b/FreeRTOS-Plus/Source/Reliance-Edge/os/freertos/services/osbdev.c @@ -1,1213 +1,1552 @@ -/* ----> 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 - -#include -#include -#include - - -/*------------------------------------------------------------------------------ - 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 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 3U - -/** @brief Pick which example implementation is compiled. - - Must be one of: - - #BDEV_F_DRIVER - - #BDEV_FATFS - - #BDEV_ATMEL_SDMMC - - #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 - - -/* 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 + ulSectorCount)); - 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 + ulSectorCount)); - 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 -#include - -/* 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 - -#include -#include -#include -#include - -/* 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; - - (void)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_RAM_DISK - -#include /* 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 <---- + + 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 + +#include +#include +#include + + +/*------------------------------------------------------------------------------ + 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 + + +/* 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 +#include + +/* 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 + +#include +#include +#include +#include + +/* 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 + #include +#elif defined(USE_STM32746G_DISCO) + #include + #include +#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 /* 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 == ... */ + diff --git a/FreeRTOS-Plus/Source/Reliance-Edge/os/freertos/services/osmutex.c b/FreeRTOS-Plus/Source/Reliance-Edge/os/freertos/services/osmutex.c index 0bc2c701b..68cd0eda0 100644 --- a/FreeRTOS-Plus/Source/Reliance-Edge/os/freertos/services/osmutex.c +++ b/FreeRTOS-Plus/Source/Reliance-Edge/os/freertos/services/osmutex.c @@ -1,120 +1,134 @@ -/* ----> 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 -#include - -#include - -#if REDCONF_TASK_COUNT > 1U - - -static SemaphoreHandle_t xMutex; - - -/** @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; - - xMutex = xSemaphoreCreateMutex(); - if(xMutex != NULL) - { - ret = 0; - } - else - { - ret = -RED_ENOMEM; - } - - 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); - (void)xSuccess; -} - -#endif - +/* ----> 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 +#include + +#include +#include + +#if REDCONF_TASK_COUNT > 1U + + +static SemaphoreHandle_t xMutex; +#if defined(configSUPPORT_STATIC_ALLOCATION) && (configSUPPORT_STATIC_ALLOCATION == 1) +static StaticSemaphore_t xMutexBuffer; +#endif + + +/** @brief Initialize the mutex. + + After initialization, the mutex is in the released state. + + The behavior of calling this function when the mutex is still initialized + is undefined. + + @return A negated ::REDSTATUS code indicating the operation result. + + @retval 0 Operation was successful. +*/ +REDSTATUS RedOsMutexInit(void) +{ + REDSTATUS ret = 0; + + #if defined(configSUPPORT_STATIC_ALLOCATION) && (configSUPPORT_STATIC_ALLOCATION == 1) + xMutex = xSemaphoreCreateMutexStatic(&xMutexBuffer); + + if(xMutex == NULL) + { + /* The only error case for xSemaphoreCreateMutexStatic is that the mutex + buffer parameter is NULL, which is not the case. + */ + REDERROR(); + ret = -RED_EINVAL; + } + + #else + xMutex = xSemaphoreCreateMutex(); + if(xMutex == NULL) + { + ret = -RED_ENOMEM; + } + #endif + + return ret; +} + + +/** @brief Uninitialize the mutex. + + The behavior of calling this function when the mutex is not initialized is + undefined; likewise, the behavior of uninitializing the mutex when it is + in the acquired state is undefined. + + @return A negated ::REDSTATUS code indicating the operation result. + + @retval 0 Operation was successful. +*/ +REDSTATUS RedOsMutexUninit(void) +{ + vSemaphoreDelete(xMutex); + xMutex = NULL; + + return 0; +} + + +/** @brief Acquire the mutex. + + The behavior of calling this function when the mutex is not initialized is + undefined; likewise, the behavior of recursively acquiring the mutex is + undefined. +*/ +void RedOsMutexAcquire(void) +{ + while(xSemaphoreTake(xMutex, portMAX_DELAY) != pdTRUE) + { + } +} + + +/** @brief Release the mutex. + + The behavior is undefined in the following cases: + + - Releasing the mutex when the mutex is not initialized. + - Releasing the mutex when it is not in the acquired state. + - Releasing the mutex from a task or thread other than the one which + acquired the mutex. +*/ +void RedOsMutexRelease(void) +{ + BaseType_t xSuccess; + + xSuccess = xSemaphoreGive(xMutex); + REDASSERT(xSuccess == pdTRUE); + IGNORE_ERRORS(xSuccess); +} + +#endif + diff --git a/FreeRTOS-Plus/Source/Reliance-Edge/tests/util/printf.c b/FreeRTOS-Plus/Source/Reliance-Edge/tests/util/printf.c index 3ae98a4b3..146b1fc84 100644 --- a/FreeRTOS-Plus/Source/Reliance-Edge/tests/util/printf.c +++ b/FreeRTOS-Plus/Source/Reliance-Edge/tests/util/printf.c @@ -891,6 +891,7 @@ static PRINTTYPE ParseFormatType( case 'u': fmtType = MAPULONGLONG; break; + case 'x': case 'X': fmtType = MAPHEXULONGLONG; break; -- 2.39.5