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