123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515 |
- /* ----> DO NOT REMOVE THE FOLLOWING NOTICE <----
- Copyright (c) 2014-2017 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 "hal.h"
- #if (HAL_USE_SDMMC == TRUE)
- #include "sama_sdmmc_lld.h"
- #if SDMMC_USE_RELEDGE_LIB == 1
- #include "sama_sdmmc_lld.h"
- #include "ch_sdmmc_device.h"
- #include "ch_sdmmc_cmds.h"
- #include "ch_sdmmc_sdio.h"
- #include "ch_sdmmc_sd.h"
- #include "ch_sdmmc_mmc.h"
- #include "ch_sdmmc_reledge.h"
- #include <redfs.h>
- #include <redvolume.h>
- #include <redosdeviations.h>
- #if REDCONF_API_POSIX == 0
- #error "REDCONF_API_POSIX should be 1"
- #endif
- #if REDCONF_API_FSE == 1
- #error "REDCONF_API_FSE not supported, should be 0"
- #endif
- /* 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;
- eSDMMC_RC cs;
- SdmmcDriver *sdmmcp = NULL;
- if (!sdmmcGetInstance(bVolNum, &sdmmcp))
- return RED_EINVAL;
- /* 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(sdmmcp);
- if((cs != SDMMC_OK) && (cs != SDMMC_BUSY))
- {
- break;
- }
- // t_msleep(sdmmcp,500);
- }
- if(cs == SDMMC_OK)
- {
- #if REDCONF_READ_ONLY == 0
- if(mode != BDEV_O_RDONLY)
- {
- if(sd_mmc_is_write_protected(sdmmcp))
- {
- ret = -RED_EROFS;
- }
- }
- if(ret == 0)
- #endif
- {
- uint32_t ulSectorLast;
- IGNORE_ERRORS(sd_mmc_read_capacity(sdmmcp, &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;
- uint8_t *pbBuffer = CAST_VOID_PTR_TO_UINT8_PTR(pBuffer);
- SdmmcDriver *sdmmcp = NULL;
- eSDMMC_RC cs;
- if (!sdmmcGetInstance(bVolNum, &sdmmcp))
- return RED_EINVAL;
- cs = SD_ReadBlocks(sdmmcp, ullSectorStart, pbBuffer,ulSectorCount);
- if(cs != SDMMC_OK)
- {
- ret = -RED_EIO;
- }
- 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;
- const uint8_t *pbBuffer = CAST_VOID_PTR_TO_CONST_UINT8_PTR(pBuffer);
- SdmmcDriver *sdmmcp = NULL;
- eSDMMC_RC cs;
- if (!sdmmcGetInstance(bVolNum, &sdmmcp))
- return RED_EINVAL;
- cs = SD_WriteBlocks(sdmmcp, ullSectorStart, pbBuffer, ulSectorCount);
- if(cs != SDMMC_OK)
- {
- ret = -RED_EIO;
- }
- 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;
- eSDMMC_RC cs;
- SdmmcDriver *sdmmcp = NULL;
- if (!sdmmcGetInstance(bVolNum, &sdmmcp))
- return RED_EINVAL;
- /* 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(sdmmcp);
- if(cs == SDMMC_OK)
- {
- ret = 0;
- }
- else
- {
- ret = -RED_EIO;
- }
- return ret;
- }
- #if REDCONF_DISCARDS == 1
- /** @brief Discard sectors on a disk.
- @param bVolNum The volume number of the volume whose block device
- is being accessed.
- @param ullSectorStart The starting sector number.
- @param ullSectorCount The number of sectors to discard.
- */
- static void DiskDiscard(
- uint8_t bVolNum,
- uint64_t ullSectorStart,
- uint64_t ullSectorCount)
- {
- #error "this SD/MMC driver does not support discards."
- }
- #endif /* REDCONF_DISCARDS == 1 */
- #endif /* REDCONF_READ_ONLY == 0 */
- /** @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 */
- #endif
- #endif
|