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