You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
FreeRTOS/FreeRTOS-Plus/Source/Reliance-Edge/os/freertos/services/osbdev.c

1559 lines
49 KiB
C

/* ----> DO NOT REMOVE THE FOLLOWING NOTICE <----
*
* Copyright (c) 2014-2015 Datalight, Inc.
* All Rights Reserved Worldwide.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; use version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but "AS-IS," WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
/* Businesses and individuals that for commercial or other reasons cannot
* comply with the terms of the GPLv2 license may obtain a commercial license
* before incorporating Reliance Edge into proprietary software for
* distribution in any form. Visit http://www.datalight.com/reliance-edge for
* more information.
*/
/** @file
* @brief Implements block device I/O.
*/
#include <FreeRTOS.h>
#include <redfs.h>
#include <redvolume.h>
#include <redosdeviations.h>
/*------------------------------------------------------------------------------
* Porting Note:
*
* Several example implementations of this module for FreeRTOS are available.
* If you are lucky, you can use one of these implementations; otherwise, these
* can serve as examples of how to implement this service.
* ------------------------------------------------------------------------------*/
/** @brief The F_DRIVER example implementation.
*
* This implementation is designed to reuse an existing block device driver
* that was written for FreeRTOS+FAT SL. If you have such a driver, with
* little work it can be "dropped in" and used for Reliance Edge. The only
* customization required is that gpfnRedOsBDevInit needs to be defined and
* pointed at the F_DRIVERINIT function. This can be done in this module or in
* another C file.
*
* The disadvantage of using the FreeRTOS F_DRIVER functions is that they only
* support single-sector reads and writes. Reliance Edge will issue
* multi-sector requests, and servicing these one sector at a time will
* significantly slow down the file system.
*/
#define BDEV_F_DRIVER ( 0U )
/** @brief The FatFs example implementation.
*
* This implementation is designed to reuse an existing block device driver
* that was written for FatFs. If you have such a driver, it can be linked
* in and used immediately. The FatFs `diskio.h` header must be in the include
* directory path.
*/
#define BDEV_FATFS ( 1U )
/** @brief The Atmel Studio Framework SD/MMC driver example implementation.
*
* This implementation uses a modified version of the open source SD/MMC driver
* included in the Atmel Studio Framework (ASF) and will work as-is for many
* varieties of Atmel hardware. This example assumes relatively minor
* modifications to the ASF SD/MMC driver to make it support multi-sector read
* and write requests, which greatly improves performance. The modified driver
* is distributed with Reliance Edge and is included in FreeRTOS Atmel projects
* (such as in projects/freertos/atmel/sam4e-ek/src/ASF).
*
* This example can easily be modified to work with an unmodified version of
* the ASF SD/MMC driver. Simply replace sd_mmc_mem_2_ram_multi() and
* sd_mmc_ram_2_mem_multi() with sd_mmc_mem_2_ram() and sd_mmc_ram_2_mem()
* respectively, and add a for loop to loop over each sector in the request.
* However, as described in the manual, there are considerable performance
* advantages to issuing real multi-sector requests, so using the modified
* driver is recommended.
*/
#define BDEV_ATMEL_SDMMC ( 2U )
/** @brief The ST Microelectronics STM32 SDIO driver example implementation.
*
* This implementation accesses the microSD card through the BSP utilities
* provided as part of the STM32Cube package, used with the STM32 HAL drivers.
* The STM3240G-EVAL and STM32F746NG-Discovery boards are currently supported.
*/
#define BDEV_STM32_SDIO ( 3U )
/** @brief The RAM disk example implementation.
*
* This implementation uses a RAM disk. It will allow you to compile and test
* Reliance Edge even if your storage driver is not yet ready. On typical
* target hardware, the amount of spare RAM will be limited so generally only
* very small disks will be available.
*/
#define BDEV_RAM_DISK ( 4U )
/** @brief Pick which example implementation is compiled.
*
* Must be one of:
* - #BDEV_F_DRIVER
* - #BDEV_FATFS
* - #BDEV_ATMEL_SDMMC
* - #BDEV_STM32_SDIO
* - #BDEV_RAM_DISK
*/
#define BDEV_EXAMPLE_IMPLEMENTATION BDEV_RAM_DISK
static REDSTATUS DiskOpen( uint8_t bVolNum,
BDEVOPENMODE mode );
static REDSTATUS DiskClose( uint8_t bVolNum );
static REDSTATUS DiskRead( uint8_t bVolNum,
uint64_t ullSectorStart,
uint32_t ulSectorCount,
void * pBuffer );
#if REDCONF_READ_ONLY == 0
static REDSTATUS DiskWrite( uint8_t bVolNum,
uint64_t ullSectorStart,
uint32_t ulSectorCount,
const void * pBuffer );
static REDSTATUS DiskFlush( uint8_t bVolNum );
#endif
/** @brief Initialize a block device.
*
* This function is called when the file system needs access to a block
* device.
*
* Upon successful return, the block device should be fully initialized and
* ready to service read/write/flush/close requests.
*
* The behavior of calling this function on a block device which is already
* open is undefined.
*
* @param bVolNum The volume number of the volume whose block device is being
* initialized.
* @param mode The open mode, indicating the type of access required.
*
* @return A negated ::REDSTATUS code indicating the operation result.
*
* @retval 0 Operation was successful.
* @retval -RED_EINVAL @p bVolNum is an invalid volume number.
* @retval -RED_EIO A disk I/O error occurred.
*/
REDSTATUS RedOsBDevOpen( uint8_t bVolNum,
BDEVOPENMODE mode )
{
REDSTATUS ret;
if( bVolNum >= REDCONF_VOLUME_COUNT )
{
ret = -RED_EINVAL;
}
else
{
ret = DiskOpen( bVolNum, mode );
}
return ret;
}
/** @brief Uninitialize a block device.
*
* This function is called when the file system no longer needs access to a
* block device. If any resource were allocated by RedOsBDevOpen() to service
* block device requests, they should be freed at this time.
*
* Upon successful return, the block device must be in such a state that it
* can be opened again.
*
* The behavior of calling this function on a block device which is already
* closed is undefined.
*
* @param bVolNum The volume number of the volume whose block device is being
* uninitialized.
*
* @return A negated ::REDSTATUS code indicating the operation result.
*
* @retval 0 Operation was successful.
* @retval -RED_EINVAL @p bVolNum is an invalid volume number.
*/
REDSTATUS RedOsBDevClose( uint8_t bVolNum )
{
REDSTATUS ret;
if( bVolNum >= REDCONF_VOLUME_COUNT )
{
ret = -RED_EINVAL;
}
else
{
ret = DiskClose( bVolNum );
}
return ret;
}
/** @brief Read sectors from a physical block device.
*
* The behavior of calling this function is undefined if the block device is
* closed or if it was opened with ::BDEV_O_WRONLY.
*
* @param bVolNum The volume number of the volume whose block device
* is being read from.
* @param ullSectorStart The starting sector number.
* @param ulSectorCount The number of sectors to read.
* @param pBuffer The buffer into which to read the sector data.
*
* @return A negated ::REDSTATUS code indicating the operation result.
*
* @retval 0 Operation was successful.
* @retval -RED_EINVAL @p bVolNum is an invalid volume number, @p pBuffer is
* `NULL`, or @p ullStartSector and/or @p ulSectorCount
* refer to an invalid range of sectors.
* @retval -RED_EIO A disk I/O error occurred.
*/
REDSTATUS RedOsBDevRead( uint8_t bVolNum,
uint64_t ullSectorStart,
uint32_t ulSectorCount,
void * pBuffer )
{
REDSTATUS ret = 0;
if( ( bVolNum >= REDCONF_VOLUME_COUNT ) ||
( ullSectorStart >= gaRedVolConf[ bVolNum ].ullSectorCount ) ||
( ( gaRedVolConf[ bVolNum ].ullSectorCount - ullSectorStart ) < ulSectorCount ) ||
( pBuffer == NULL ) )
{
ret = -RED_EINVAL;
}
else
{
ret = DiskRead( bVolNum, ullSectorStart, ulSectorCount, pBuffer );
}
return ret;
}
#if REDCONF_READ_ONLY == 0
/** @brief Write sectors to a physical block device.
*
* The behavior of calling this function is undefined if the block device is
* closed or if it was opened with ::BDEV_O_RDONLY.
*
* @param bVolNum The volume number of the volume whose block device
* is being written to.
* @param ullSectorStart The starting sector number.
* @param ulSectorCount The number of sectors to write.
* @param pBuffer The buffer from which to write the sector data.
*
* @return A negated ::REDSTATUS code indicating the operation result.
*
* @retval 0 Operation was successful.
* @retval -RED_EINVAL @p bVolNum is an invalid volume number, @p pBuffer is
* `NULL`, or @p ullStartSector and/or @p ulSectorCount
* refer to an invalid range of sectors.
* @retval -RED_EIO A disk I/O error occurred.
*/
REDSTATUS RedOsBDevWrite( uint8_t bVolNum,
uint64_t ullSectorStart,
uint32_t ulSectorCount,
const void * pBuffer )
{
REDSTATUS ret = 0;
if( ( bVolNum >= REDCONF_VOLUME_COUNT ) ||
( ullSectorStart >= gaRedVolConf[ bVolNum ].ullSectorCount ) ||
( ( gaRedVolConf[ bVolNum ].ullSectorCount - ullSectorStart ) < ulSectorCount ) ||
( pBuffer == NULL ) )
{
ret = -RED_EINVAL;
}
else
{
ret = DiskWrite( bVolNum, ullSectorStart, ulSectorCount, pBuffer );
}
return ret;
}
/** @brief Flush any caches beneath the file system.
*
* This function must synchronously flush all software and hardware caches
* beneath the file system, ensuring that all sectors written previously are
* committed to permanent storage.
*
* If the environment has no caching beneath the file system, the
* implementation of this function can do nothing and return success.
*
* The behavior of calling this function is undefined if the block device is
* closed or if it was opened with ::BDEV_O_RDONLY.
*
* @param bVolNum The volume number of the volume whose block device is being
* flushed.
*
* @return A negated ::REDSTATUS code indicating the operation result.
*
* @retval 0 Operation was successful.
* @retval -RED_EINVAL @p bVolNum is an invalid volume number.
* @retval -RED_EIO A disk I/O error occurred.
*/
REDSTATUS RedOsBDevFlush( uint8_t bVolNum )
{
REDSTATUS ret;
if( bVolNum >= REDCONF_VOLUME_COUNT )
{
ret = -RED_EINVAL;
}
else
{
ret = DiskFlush( bVolNum );
}
return ret;
}
#endif /* REDCONF_READ_ONLY == 0 */
#if BDEV_EXAMPLE_IMPLEMENTATION == BDEV_F_DRIVER
#include <api_mdriver.h>
/* This must be declared and initialized elsewere (e.g., in project code) to
* point at the initialization function for the F_DRIVER block device.
*/
extern const F_DRIVERINIT gpfnRedOsBDevInit;
static F_DRIVER * gapFDriver[ REDCONF_VOLUME_COUNT ];
/** @brief Initialize a disk.
*
* @param bVolNum The volume number of the volume whose block device is being
* initialized.
* @param mode The open mode, indicating the type of access required.
*
* @return A negated ::REDSTATUS code indicating the operation result.
*
* @retval 0 Operation was successful.
* @retval -RED_EIO A disk I/O error occurred.
*/
static REDSTATUS DiskOpen( uint8_t bVolNum,
BDEVOPENMODE mode )
{
REDSTATUS ret;
( void ) mode;
if( ( gpfnRedOsBDevInit == NULL ) || ( gapFDriver[ bVolNum ] != NULL ) )
{
ret = -RED_EINVAL;
}
else
{
F_DRIVER * pDriver;
pDriver = gpfnRedOsBDevInit( bVolNum );
if( pDriver != NULL )
{
F_PHY geom;
int iErr;
/* Validate that the geometry is consistent with the volume
* configuration.
*/
iErr = pDriver->getphy( pDriver, &geom );
if( iErr == 0 )
{
if( ( geom.bytes_per_sector != gaRedVolConf[ bVolNum ].ulSectorSize ) ||
( geom.number_of_sectors < gaRedVolConf[ bVolNum ].ullSectorCount ) )
{
ret = -RED_EINVAL;
}
else
{
gapFDriver[ bVolNum ] = pDriver;
ret = 0;
}
}
else
{
ret = -RED_EIO;
}
if( ret != 0 )
{
pDriver->release( pDriver );
}
}
else
{
ret = -RED_EIO;
}
}
return ret;
}
/** @brief Uninitialize a disk.
*
* @param bVolNum The volume number of the volume whose block device is being
* uninitialized.
*
* @return A negated ::REDSTATUS code indicating the operation result.
*
* @retval 0 Operation was successful.
*/
static REDSTATUS DiskClose( uint8_t bVolNum )
{
REDSTATUS ret;
if( gapFDriver[ bVolNum ] == NULL )
{
ret = -RED_EINVAL;
}
else
{
gapFDriver[ bVolNum ]->release( gapFDriver[ bVolNum ] );
gapFDriver[ bVolNum ] = NULL;
ret = 0;
}
return ret;
}
/** @brief Read sectors from a disk.
*
* @param bVolNum The volume number of the volume whose block device
* is being read from.
* @param ullSectorStart The starting sector number.
* @param ulSectorCount The number of sectors to read.
* @param pBuffer The buffer into which to read the sector data.
*
* @return A negated ::REDSTATUS code indicating the operation result.
*
* @retval 0 Operation was successful.
* @retval -RED_EIO A disk I/O error occurred.
*/
static REDSTATUS DiskRead( uint8_t bVolNum,
uint64_t ullSectorStart,
uint32_t ulSectorCount,
void * pBuffer )
{
REDSTATUS ret = 0;
F_DRIVER * pDriver = gapFDriver[ bVolNum ];
if( pDriver == NULL )
{
ret = -RED_EINVAL;
}
else
{
uint8_t * pbBuffer = CAST_VOID_PTR_TO_UINT8_PTR( pBuffer );
uint32_t ulSectorSize = gaRedVolConf[ bVolNum ].ulSectorSize;
uint32_t ulSectorIdx;
int iErr;
for( ulSectorIdx = 0U; ulSectorIdx < ulSectorCount; ulSectorIdx++ )
{
iErr = pDriver->readsector( pDriver, &pbBuffer[ ulSectorIdx * ulSectorSize ],
CAST_ULONG( ullSectorStart + ulSectorIdx ) );
if( iErr != 0 )
{
ret = -RED_EIO;
break;
}
}
}
return ret;
}
#if REDCONF_READ_ONLY == 0
/** @brief Write sectors to a disk.
*
* @param bVolNum The volume number of the volume whose block device
* is being written to.
* @param ullSectorStart The starting sector number.
* @param ulSectorCount The number of sectors to write.
* @param pBuffer The buffer from which to write the sector data.
*
* @return A negated ::REDSTATUS code indicating the operation result.
*
* @retval 0 Operation was successful.
* @retval -RED_EINVAL The block device is not open.
* @retval -RED_EIO A disk I/O error occurred.
*/
static REDSTATUS DiskWrite( uint8_t bVolNum,
uint64_t ullSectorStart,
uint32_t ulSectorCount,
const void * pBuffer )
{
REDSTATUS ret = 0;
F_DRIVER * pDriver = gapFDriver[ bVolNum ];
if( pDriver == NULL )
{
ret = -RED_EINVAL;
}
else
{
const uint8_t * pbBuffer = CAST_VOID_PTR_TO_CONST_UINT8_PTR( pBuffer );
uint32_t ulSectorSize = gaRedVolConf[ bVolNum ].ulSectorSize;
uint32_t ulSectorIdx;
int iErr;
for( ulSectorIdx = 0U; ulSectorIdx < ulSectorCount; ulSectorIdx++ )
{
/* We have to cast pbBuffer to non-const since the writesector
* prototype is flawed, using a non-const pointer for the buffer.
*/
iErr = pDriver->writesector( pDriver, CAST_AWAY_CONST( uint8_t, &pbBuffer[ ulSectorIdx * ulSectorSize ] ),
CAST_ULONG( ullSectorStart + ulSectorIdx ) );
if( iErr != 0 )
{
ret = -RED_EIO;
break;
}
}
}
return ret;
}
/** @brief Flush any caches beneath the file system.
*
* @param bVolNum The volume number of the volume whose block device is being
* flushed.
*
* @return A negated ::REDSTATUS code indicating the operation result.
*
* @retval 0 Operation was successful.
*/
static REDSTATUS DiskFlush( uint8_t bVolNum )
{
REDSTATUS ret;
if( gapFDriver[ bVolNum ] == NULL )
{
ret = -RED_EINVAL;
}
else
{
/* The F_DRIVER interface does not include a flush function, so to be
* reliable the F_DRIVER implementation must use synchronous writes.
*/
ret = 0;
}
return ret;
}
#endif /* REDCONF_READ_ONLY == 0 */
#elif BDEV_EXAMPLE_IMPLEMENTATION == BDEV_FATFS
#include <task.h>
#include <diskio.h>
/* disk_read() and disk_write() use an unsigned 8-bit value to specify the
* sector count, so no transfer can be larger than 255 sectors.
*/
#define MAX_SECTOR_TRANSFER UINT8_MAX
/** @brief Initialize a disk.
*
* @param bVolNum The volume number of the volume whose block device is being
* initialized.
* @param mode The open mode, indicating the type of access required.
*
* @return A negated ::REDSTATUS code indicating the operation result.
*
* @retval 0 Operation was successful.
* @retval -RED_EIO A disk I/O error occurred.
*/
static REDSTATUS DiskOpen( uint8_t bVolNum,
BDEVOPENMODE mode )
{
DSTATUS status;
uint32_t ulTries;
REDSTATUS ret = 0;
/* With some implementations of disk_initialize(), such as the one
* implemented by Atmel for the ASF, the first time the disk is opened, the
* SD card can take a while to get ready, in which time disk_initialize()
* returns an error. Try numerous times, waiting half a second after each
* failure. Empirically, this has been observed to succeed on the second
* try, so trying 10x more than that provides a margin of error.
*/
for( ulTries = 0U; ulTries < 20U; ulTries++ )
{
/* Assuming that the volume number is also the correct drive number.
* If this is not the case in your environment, a static constant array
* can be declared to map volume numbers to the correct driver number.
*/
status = disk_initialize( bVolNum );
if( status == 0 )
{
break;
}
vTaskDelay( 500U / portTICK_PERIOD_MS );
}
if( status != 0 )
{
ret = -RED_EIO;
}
/* Retrieve the sector size and sector count to ensure they are compatible
* with our compile-time geometry.
*/
if( ret == 0 )
{
WORD wSectorSize;
DWORD dwSectorCount;
DRESULT result;
result = disk_ioctl( bVolNum, GET_SECTOR_SIZE, &wSectorSize );
if( result == RES_OK )
{
result = disk_ioctl( bVolNum, GET_SECTOR_COUNT, &dwSectorCount );
if( result == RES_OK )
{
if( ( wSectorSize != gaRedVolConf[ bVolNum ].ulSectorSize ) ||
( dwSectorCount < gaRedVolConf[ bVolNum ].ullSectorCount ) )
{
ret = -RED_EINVAL;
}
}
else
{
ret = -RED_EIO;
}
}
else
{
ret = -RED_EIO;
}
}
return ret;
}
/** @brief Uninitialize a disk.
*
* @param bVolNum The volume number of the volume whose block device is being
* uninitialized.
*
* @return A negated ::REDSTATUS code indicating the operation result.
*
* @retval 0 Operation was successful.
*/
static REDSTATUS DiskClose( uint8_t bVolNum )
{
( void ) bVolNum;
return 0;
}
/** @brief Read sectors from a disk.
*
* @param bVolNum The volume number of the volume whose block device
* is being read from.
* @param ullSectorStart The starting sector number.
* @param ulSectorCount The number of sectors to read.
* @param pBuffer The buffer into which to read the sector data.
*
* @return A negated ::REDSTATUS code indicating the operation result.
*
* @retval 0 Operation was successful.
* @retval -RED_EIO A disk I/O error occurred.
*/
static REDSTATUS DiskRead( uint8_t bVolNum,
uint64_t ullSectorStart,
uint32_t ulSectorCount,
void * pBuffer )
{
REDSTATUS ret = 0;
uint32_t ulSectorIdx = 0U;
uint32_t ulSectorSize = gaRedVolConf[ bVolNum ].ulSectorSize;
uint8_t * pbBuffer = CAST_VOID_PTR_TO_UINT8_PTR( pBuffer );
while( ulSectorIdx < ulSectorCount )
{
uint32_t ulTransfer = REDMIN( ulSectorCount - ulSectorIdx, MAX_SECTOR_TRANSFER );
DRESULT result;
result = disk_read( bVolNum, &pbBuffer[ ulSectorIdx * ulSectorSize ], ( DWORD ) ( ullSectorStart + ulSectorIdx ), ( BYTE ) ulTransfer );
if( result != RES_OK )
{
ret = -RED_EIO;
break;
}
ulSectorIdx += ulTransfer;
}
return ret;
}
#if REDCONF_READ_ONLY == 0
/** @brief Write sectors to a disk.
*
* @param bVolNum The volume number of the volume whose block device
* is being written to.
* @param ullSectorStart The starting sector number.
* @param ulSectorCount The number of sectors to write.
* @param pBuffer The buffer from which to write the sector data.
*
* @return A negated ::REDSTATUS code indicating the operation result.
*
* @retval 0 Operation was successful.
* @retval -RED_EIO A disk I/O error occurred.
*/
static REDSTATUS DiskWrite( uint8_t bVolNum,
uint64_t ullSectorStart,
uint32_t ulSectorCount,
const void * pBuffer )
{
REDSTATUS ret = 0;
uint32_t ulSectorIdx = 0U;
uint32_t ulSectorSize = gaRedVolConf[ bVolNum ].ulSectorSize;
const uint8_t * pbBuffer = CAST_VOID_PTR_TO_CONST_UINT8_PTR( pBuffer );
while( ulSectorIdx < ulSectorCount )
{
uint32_t ulTransfer = REDMIN( ulSectorCount - ulSectorIdx, MAX_SECTOR_TRANSFER );
DRESULT result;
result = disk_write( bVolNum, &pbBuffer[ ulSectorIdx * ulSectorSize ], ( DWORD ) ( ullSectorStart + ulSectorIdx ), ( BYTE ) ulTransfer );
if( result != RES_OK )
{
ret = -RED_EIO;
break;
}
ulSectorIdx += ulTransfer;
}
return ret;
}
/** @brief Flush any caches beneath the file system.
*
* @param bVolNum The volume number of the volume whose block device is being
* flushed.
*
* @return A negated ::REDSTATUS code indicating the operation result.
*
* @retval 0 Operation was successful.
*/
static REDSTATUS DiskFlush( uint8_t bVolNum )
{
REDSTATUS ret;
DRESULT result;
result = disk_ioctl( bVolNum, CTRL_SYNC, NULL );
if( result == RES_OK )
{
ret = 0;
}
else
{
ret = -RED_EIO;
}
return ret;
}
#endif /* REDCONF_READ_ONLY == 0 */
#elif BDEV_EXAMPLE_IMPLEMENTATION == BDEV_ATMEL_SDMMC
#include <task.h>
#include <conf_sd_mmc.h>
#include <sd_mmc.h>
#include <sd_mmc_mem.h>
#include <ctrl_access.h>
/* sd_mmc_mem_2_ram_multi() and sd_mmc_ram_2_mem_multi() use an unsigned
* 16-bit value to specify the sector count, so no transfer can be larger
* than UINT16_MAX sectors.
*/
#define MAX_SECTOR_TRANSFER UINT16_MAX
/** @brief Initialize a disk.
*
* @param bVolNum The volume number of the volume whose block device is being
* initialized.
* @param mode The open mode, indicating the type of access required.
*
* @return A negated ::REDSTATUS code indicating the operation result.
*
* @retval 0 Operation was successful.
* @retval -RED_EIO A disk I/O error occurred.
* @retval -RED_EROFS The device is read-only media and write access was
* requested.
*/
static REDSTATUS DiskOpen( uint8_t bVolNum,
BDEVOPENMODE mode )
{
REDSTATUS ret = 0;
uint32_t ulTries;
Ctrl_status cs;
/* Note: Assuming the volume number is the same as the SD card slot. The
* ASF SD/MMC driver supports two SD slots. This implementation will need
* to be modified if multiple volumes share a single SD card.
*/
/* The first time the disk is opened, the SD card can take a while to get
* ready, in which time sd_mmc_test_unit_ready() returns either CTRL_BUSY
* or CTRL_NO_PRESENT. Try numerous times, waiting half a second after
* each failure. Empirically, this has been observed to succeed on the
* second try, so trying 10x more than that provides a margin of error.
*/
for( ulTries = 0U; ulTries < 20U; ulTries++ )
{
cs = sd_mmc_test_unit_ready( bVolNum );
if( ( cs != CTRL_NO_PRESENT ) && ( cs != CTRL_BUSY ) )
{
break;
}
vTaskDelay( 500U / portTICK_PERIOD_MS );
}
if( cs == CTRL_GOOD )
{
#if REDCONF_READ_ONLY == 0
if( mode != BDEV_O_RDONLY )
{
if( sd_mmc_wr_protect( bVolNum ) )
{
ret = -RED_EROFS;
}
}
if( ret == 0 )
#endif
{
uint32_t ulSectorLast;
IGNORE_ERRORS( sd_mmc_read_capacity( bVolNum, &ulSectorLast ) );
/* The ASF SD/MMC driver only supports 512-byte sectors.
*/
if( ( gaRedVolConf[ bVolNum ].ulSectorSize != 512U ) ||
( ( ( uint64_t ) ulSectorLast + 1U ) < gaRedVolConf[ bVolNum ].ullSectorCount ) )
{
ret = -RED_EINVAL;
}
}
}
else
{
ret = -RED_EIO;
}
return ret;
}
/** @brief Uninitialize a disk.
*
* @param bVolNum The volume number of the volume whose block device is being
* uninitialized.
*
* @return A negated ::REDSTATUS code indicating the operation result.
*
* @retval 0 Operation was successful.
*/
static REDSTATUS DiskClose( uint8_t bVolNum )
{
( void ) bVolNum;
return 0;
}
/** @brief Read sectors from a disk.
*
* @param bVolNum The volume number of the volume whose block device
* is being read from.
* @param ullSectorStart The starting sector number.
* @param ulSectorCount The number of sectors to read.
* @param pBuffer The buffer into which to read the sector data.
*
* @return A negated ::REDSTATUS code indicating the operation result.
*
* @retval 0 Operation was successful.
*/
static REDSTATUS DiskRead( uint8_t bVolNum,
uint64_t ullSectorStart,
uint32_t ulSectorCount,
void * pBuffer )
{
REDSTATUS ret = 0;
uint32_t ulSectorIdx = 0U;
uint32_t ulSectorSize = gaRedVolConf[ bVolNum ].ulSectorSize;
uint8_t * pbBuffer = CAST_VOID_PTR_TO_UINT8_PTR( pBuffer );
while( ulSectorIdx < ulSectorCount )
{
uint32_t ulTransfer = REDMIN( ulSectorCount - ulSectorIdx, MAX_SECTOR_TRANSFER );
Ctrl_status cs;
cs = sd_mmc_mem_2_ram_multi( bVolNum, ( uint32_t ) ( ullSectorStart + ulSectorIdx ),
( uint16_t ) ulTransfer, &pbBuffer[ ulSectorIdx * ulSectorSize ] );
if( cs != CTRL_GOOD )
{
ret = -RED_EIO;
break;
}
ulSectorIdx += ulTransfer;
}
return ret;
}
#if REDCONF_READ_ONLY == 0
/** @brief Write sectors to a disk.
*
* @param bVolNum The volume number of the volume whose block device
* is being written to.
* @param ullSectorStart The starting sector number.
* @param ulSectorCount The number of sectors to write.
* @param pBuffer The buffer from which to write the sector data.
*
* @return A negated ::REDSTATUS code indicating the operation result.
*
* @retval 0 Operation was successful.
*/
static REDSTATUS DiskWrite( uint8_t bVolNum,
uint64_t ullSectorStart,
uint32_t ulSectorCount,
const void * pBuffer )
{
REDSTATUS ret = 0;
uint32_t ulSectorIdx = 0U;
uint32_t ulSectorSize = gaRedVolConf[ bVolNum ].ulSectorSize;
const uint8_t * pbBuffer = CAST_VOID_PTR_TO_CONST_UINT8_PTR( pBuffer );
while( ulSectorIdx < ulSectorCount )
{
uint32_t ulTransfer = REDMIN( ulSectorCount - ulSectorIdx, MAX_SECTOR_TRANSFER );
Ctrl_status cs;
cs = sd_mmc_ram_2_mem_multi( bVolNum, ( uint32_t ) ( ullSectorStart + ulSectorIdx ),
( uint16_t ) ulTransfer, &pbBuffer[ ulSectorIdx * ulSectorSize ] );
if( cs != CTRL_GOOD )
{
ret = -RED_EIO;
break;
}
ulSectorIdx += ulTransfer;
}
return ret;
}
/** @brief Flush any caches beneath the file system.
*
* @param bVolNum The volume number of the volume whose block device is being
* flushed.
*
* @return A negated ::REDSTATUS code indicating the operation result.
*
* @retval 0 Operation was successful.
*/
static REDSTATUS DiskFlush( uint8_t bVolNum )
{
REDSTATUS ret;
Ctrl_status cs;
/* The ASF SD/MMC driver appears to write sectors synchronously, so it
* should be fine to do nothing and return success. However, Atmel's
* implementation of the FatFs diskio.c file does the equivalent of the
* below when the disk is flushed. Just in case this is important for some
* non-obvious reason, do the same.
*/
cs = sd_mmc_test_unit_ready( bVolNum );
if( cs == CTRL_GOOD )
{
ret = 0;
}
else
{
ret = -RED_EIO;
}
return ret;
}
#endif /* REDCONF_READ_ONLY == 0 */
#elif BDEV_EXAMPLE_IMPLEMENTATION == BDEV_STM32_SDIO
#ifdef USE_STM324xG_EVAL
#include <stm324xg_eval.h>
#include <stm324xg_eval_sd.h>
#elif defined( USE_STM32746G_DISCO )
#include <stm32746g_discovery.h>
#include <stm32746g_discovery_sd.h>
#else
/* If you are using a compatible STM32 device other than the two listed above
* and you have SD card driver headers, you can try adding them to the above
* list.
*/
#error "Unsupported device."
#endif
#if REDCONF_VOLUME_COUNT > 1
#error "The STM32 SDIO block device implementation does not support multiple volumes."
#endif
#ifndef USE_HAL_DRIVER
#error "The STM32 StdPeriph driver is not supported. Please use the HAL driver or modify the Reliance Edge block device interface."
#endif
/** @brief Number of times to call BSP_SD_GetStatus() before timing out and
* returning an error.
*
* See ::CheckStatus().
*
* NOTE: Datalight has not observed a scenario where BSP_SD_GetStatus()
* returns SD_TRANSFER_BUSY after a transfer command returns successfully.
* Set SD_STATUS_TIMEOUT to 0U to skip checking BSP_SD_GetStatus().
*/
#define SD_STATUS_TIMEOUT ( 100000U )
/** @brief 4-byte aligned buffer to use for DMA transfers when passed in
* an unaligned buffer.
*/
static uint32_t gaulAlignedBuffer[ 512U / sizeof( uint32_t ) ];
#if SD_STATUS_TIMEOUT > 0U
static REDSTATUS CheckStatus( void );
#endif
/** @brief Initialize a disk.
*
* @param bVolNum The volume number of the volume whose block device is being
* initialized.
* @param mode The open mode, indicating the type of access required.
*
* @return A negated ::REDSTATUS code indicating the operation result.
*
* @retval 0 Operation was successful.
* @retval -RED_EIO No SD card was found; or BSP_SD_Init() failed.
* @retval -RED_EINVAL The SD card's block size is not the same as the
* configured sector size; or the SD card is not large
* enough for the volume; or the volume size is above
* 4GiB, meaning that part of it cannot be accessed
* through the STM32 SDIO driver.
*/
static REDSTATUS DiskOpen( uint8_t bVolNum,
BDEVOPENMODE mode )
{
REDSTATUS ret = 0;
static bool fSdInitted = false;
( void ) mode;
if( !fSdInitted )
{
if( BSP_SD_Init() == MSD_OK )
{
fSdInitted = true;
}
}
if( !fSdInitted )
{
/* Above initialization attempt failed.
*/
ret = -RED_EIO;
}
else if( BSP_SD_IsDetected() == SD_NOT_PRESENT )
{
ret = -RED_EIO;
}
else
{
uint32_t ulSectorSize = gaRedVolConf[ bVolNum ].ulSectorSize;
HAL_SD_CardInfoTypedef sdCardInfo = { { 0 } };
BSP_SD_GetCardInfo( &sdCardInfo );
/* Note: the actual card block size is sdCardInfo.CardBlockSize,
* but the interface only supports a 512 byte block size. Further,
* one card has been observed to report a 1024-byte block size,
* but it worked fine with a 512-byte Reliance Edge ulSectorSize.
*/
if( ( ulSectorSize != 512U ) ||
( sdCardInfo.CardCapacity < ( gaRedVolConf[ bVolNum ].ullSectorCount * ulSectorSize ) ) )
{
ret = -RED_EINVAL;
}
}
return ret;
}
/** @brief Uninitialize a disk.
*
* @param bVolNum The volume number of the volume whose block device is being
* uninitialized.
*
* @return A negated ::REDSTATUS code indicating the operation result.
*
* @retval 0 Operation was successful.
*/
static REDSTATUS DiskClose( uint8_t bVolNum )
{
( void ) bVolNum;
return 0;
}
/** @brief Read sectors from a disk.
*
* @param bVolNum The volume number of the volume whose block device
* is being read from.
* @param ullSectorStart The starting sector number.
* @param ulSectorCount The number of sectors to read.
* @param pBuffer The buffer into which to read the sector data.
*
* @return A negated ::REDSTATUS code indicating the operation result.
*
* @retval 0 Operation was successful.
* @retval -RED_EIO A disk I/O error occurred.
*/
static REDSTATUS DiskRead( uint8_t bVolNum,
uint64_t ullSectorStart,
uint32_t ulSectorCount,
void * pBuffer )
{
REDSTATUS redStat = 0;
uint32_t ulSectorSize = gaRedVolConf[ bVolNum ].ulSectorSize;
uint8_t bSdError;
if( IS_UINT32_ALIGNED_PTR( pBuffer ) )
{
bSdError = BSP_SD_ReadBlocks_DMA( CAST_UINT32_PTR( pBuffer ), ullSectorStart * ulSectorSize, ulSectorSize, ulSectorCount );
if( bSdError != MSD_OK )
{
redStat = -RED_EIO;
}
#if SD_STATUS_TIMEOUT > 0U
else
{
redStat = CheckStatus();
}
#endif
}
else
{
uint32_t ulSectorIdx;
for( ulSectorIdx = 0U; ulSectorIdx < ulSectorCount; ulSectorIdx++ )
{
bSdError = BSP_SD_ReadBlocks_DMA( gaulAlignedBuffer, ( ullSectorStart + ulSectorIdx ) * ulSectorSize, ulSectorSize, 1U );
if( bSdError != MSD_OK )
{
redStat = -RED_EIO;
}
#if SD_STATUS_TIMEOUT > 0U
else
{
redStat = CheckStatus();
}
#endif
if( redStat == 0 )
{
uint8_t * pbBuffer = CAST_VOID_PTR_TO_UINT8_PTR( pBuffer );
RedMemCpy( &pbBuffer[ ulSectorIdx * ulSectorSize ], gaulAlignedBuffer, ulSectorSize );
}
else
{
break;
}
}
}
return redStat;
}
#if REDCONF_READ_ONLY == 0
/** @brief Write sectors to a disk.
*
* @param bVolNum The volume number of the volume whose block device
* is being written to.
* @param ullSectorStart The starting sector number.
* @param ulSectorCount The number of sectors to write.
* @param pBuffer The buffer from which to write the sector data.
*
* @return A negated ::REDSTATUS code indicating the operation result.
*
* @retval 0 Operation was successful.
* @retval -RED_EIO A disk I/O error occurred.
*/
static REDSTATUS DiskWrite( uint8_t bVolNum,
uint64_t ullSectorStart,
uint32_t ulSectorCount,
const void * pBuffer )
{
REDSTATUS redStat = 0;
uint32_t ulSectorSize = gaRedVolConf[ bVolNum ].ulSectorSize;
uint8_t bSdError;
if( IS_UINT32_ALIGNED_PTR( pBuffer ) )
{
bSdError = BSP_SD_WriteBlocks_DMA( CAST_UINT32_PTR( CAST_AWAY_CONST( void, pBuffer ) ), ullSectorStart * ulSectorSize,
ulSectorSize, ulSectorCount );
if( bSdError != MSD_OK )
{
redStat = -RED_EIO;
}
#if SD_STATUS_TIMEOUT > 0U
else
{
redStat = CheckStatus();
}
#endif
}
else
{
uint32_t ulSectorIdx;
for( ulSectorIdx = 0U; ulSectorIdx < ulSectorCount; ulSectorIdx++ )
{
const uint8_t * pbBuffer = CAST_VOID_PTR_TO_CONST_UINT8_PTR( pBuffer );
RedMemCpy( gaulAlignedBuffer, &pbBuffer[ ulSectorIdx * ulSectorSize ], ulSectorSize );
bSdError = BSP_SD_WriteBlocks_DMA( gaulAlignedBuffer, ( ullSectorStart + ulSectorIdx ) * ulSectorSize, ulSectorSize, 1U );
if( bSdError != MSD_OK )
{
redStat = -RED_EIO;
}
#if SD_STATUS_TIMEOUT > 0U
else
{
redStat = CheckStatus();
}
#endif
if( redStat != 0 )
{
break;
}
}
}
return redStat;
}
/** @brief Flush any caches beneath the file system.
*
* @param bVolNum The volume number of the volume whose block device is being
* flushed.
*
* @return A negated ::REDSTATUS code indicating the operation result.
*
* @retval 0 Operation was successful.
*/
static REDSTATUS DiskFlush( uint8_t bVolNum )
{
/* Disk transfer is synchronous; nothing to flush.
*/
( void ) bVolNum;
return 0;
}
#if SD_STATUS_TIMEOUT > 0U
/** @brief Wait until BSP_SD_GetStatus returns SD_TRANSFER_OK.
*
* This function calls BSP_SD_GetStatus repeatedly as long as it returns
* SD_TRANSFER_BUSY up to SD_STATUS_TIMEOUT times.
*
* @return A negated ::REDSTATUS code indicating the operation result.
*
* @retval 0 SD_TRANSFER_OK was returned.
* @retval -RED_EIO SD_TRANSFER_ERROR received, or timed out waiting for
* SD_TRANSFER_OK.
*/
static REDSTATUS CheckStatus( void )
{
REDSTATUS redStat = 0;
uint32_t ulTimeout = SD_STATUS_TIMEOUT;
HAL_SD_TransferStateTypedef transferState;
do
{
transferState = BSP_SD_GetStatus();
ulTimeout--;
} while( ( transferState == SD_TRANSFER_BUSY ) && ( ulTimeout > 0U ) );
if( transferState != SD_TRANSFER_OK )
{
redStat = -RED_EIO;
}
return redStat;
}
#endif /* if SD_STATUS_TIMEOUT > 0U */
#endif /* REDCONF_READ_ONLY == 0 */
#elif BDEV_EXAMPLE_IMPLEMENTATION == BDEV_RAM_DISK
#include <stdlib.h> /* For ALLOCATE_CLEARED_MEMORY(), which expands to calloc(). */
static uint8_t * gapbRamDisk[ REDCONF_VOLUME_COUNT ];
/** @brief Initialize a disk.
*
* @param bVolNum The volume number of the volume whose block device is being
* initialized.
* @param mode The open mode, indicating the type of access required.
*
* @return A negated ::REDSTATUS code indicating the operation result.
*
* @retval 0 Operation was successful.
* @retval -RED_EIO A disk I/O error occurred.
*/
static REDSTATUS DiskOpen( uint8_t bVolNum,
BDEVOPENMODE mode )
{
REDSTATUS ret = 0;
( void ) mode;
if( gapbRamDisk[ bVolNum ] == NULL )
{
gapbRamDisk[ bVolNum ] = ALLOCATE_CLEARED_MEMORY( gaRedVolume[ bVolNum ].ulBlockCount, REDCONF_BLOCK_SIZE );
if( gapbRamDisk[ bVolNum ] == NULL )
{
ret = -RED_EIO;
}
}
return ret;
}
/** @brief Uninitialize a disk.
*
* @param bVolNum The volume number of the volume whose block device is being
* uninitialized.
*
* @return A negated ::REDSTATUS code indicating the operation result.
*
* @retval 0 Operation was successful.
*/
static REDSTATUS DiskClose( uint8_t bVolNum )
{
REDSTATUS ret;
if( gapbRamDisk[ bVolNum ] == NULL )
{
ret = -RED_EINVAL;
}
else
{
/* This implementation uses dynamically allocated memory, but must
* retain previously written data after the block device is closed, and
* thus the memory cannot be freed and will remain allocated until
* reboot.
*/
ret = 0;
}
return ret;
}
/** @brief Read sectors from a disk.
*
* @param bVolNum The volume number of the volume whose block device
* is being read from.
* @param ullSectorStart The starting sector number.
* @param ulSectorCount The number of sectors to read.
* @param pBuffer The buffer into which to read the sector data.
*
* @return A negated ::REDSTATUS code indicating the operation result.
*
* @retval 0 Operation was successful.
*/
static REDSTATUS DiskRead( uint8_t bVolNum,
uint64_t ullSectorStart,
uint32_t ulSectorCount,
void * pBuffer )
{
REDSTATUS ret;
if( gapbRamDisk[ bVolNum ] == NULL )
{
ret = -RED_EINVAL;
}
else
{
uint64_t ullByteOffset = ullSectorStart * gaRedVolConf[ bVolNum ].ulSectorSize;
uint32_t ulByteCount = ulSectorCount * gaRedVolConf[ bVolNum ].ulSectorSize;
RedMemCpy( pBuffer, &gapbRamDisk[ bVolNum ][ ullByteOffset ], ulByteCount );
ret = 0;
}
return ret;
}
#if REDCONF_READ_ONLY == 0
/** @brief Write sectors to a disk.
*
* @param bVolNum The volume number of the volume whose block device
* is being written to.
* @param ullSectorStart The starting sector number.
* @param ulSectorCount The number of sectors to write.
* @param pBuffer The buffer from which to write the sector data.
*
* @return A negated ::REDSTATUS code indicating the operation result.
*
* @retval 0 Operation was successful.
*/
static REDSTATUS DiskWrite( uint8_t bVolNum,
uint64_t ullSectorStart,
uint32_t ulSectorCount,
const void * pBuffer )
{
REDSTATUS ret;
if( gapbRamDisk[ bVolNum ] == NULL )
{
ret = -RED_EINVAL;
}
else
{
uint64_t ullByteOffset = ullSectorStart * gaRedVolConf[ bVolNum ].ulSectorSize;
uint32_t ulByteCount = ulSectorCount * gaRedVolConf[ bVolNum ].ulSectorSize;
RedMemCpy( &gapbRamDisk[ bVolNum ][ ullByteOffset ], pBuffer, ulByteCount );
ret = 0;
}
return ret;
}
/** @brief Flush any caches beneath the file system.
*
* @param bVolNum The volume number of the volume whose block device is being
* flushed.
*
* @return A negated ::REDSTATUS code indicating the operation result.
*
* @retval 0 Operation was successful.
*/
static REDSTATUS DiskFlush( uint8_t bVolNum )
{
REDSTATUS ret;
if( gapbRamDisk[ bVolNum ] == NULL )
{
ret = -RED_EINVAL;
}
else
{
ret = 0;
}
return ret;
}
#endif /* REDCONF_READ_ONLY == 0 */
#else /* if BDEV_EXAMPLE_IMPLEMENTATION == BDEV_F_DRIVER */
#error "Invalid BDEV_EXAMPLE_IMPLEMENTATION value"
#endif /* BDEV_EXAMPLE_IMPLEMENTATION == ... */