|
|
|
/* ----> 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 == ... */
|