#include #include #include #include #include "diskio.h" #include "ff.h" #define SECTOR_SIZE 512 struct disk_dev_cfg { struct disk_dev dev; uint64_t offset; }; static struct disk_dev_cfg Disk[FF_VOLUMES] = {0}; static DSTATUS Stat[FF_VOLUMES] = { STA_NOINIT, STA_NOINIT, STA_NOINIT, STA_NOINIT, STA_NOINIT, STA_NOINIT, STA_NOINIT, STA_NOINIT, STA_NOINIT, }; /* Disk status */ const char *VolumeStr[FF_VOLUMES] = { "mmc0", "mmc1", "norflash0", "norflash1", "memdisk0", DISK_USB_NAME(0), DISK_USB_NAME(1)}; /** * @brief Get the status of a disk drive. * * This function retrieves the current status of a specified disk drive. * The status is stored in the global `Stat` array, which is indexed by the * drive number. * * @param pdrv The physical drive number (0..FF_VOLUMES - 1). * @return The current status of the drive, represented as a `DSTATUS` type. * 0 Status ok * STA_NOINIT Drive not initialized * STA_NODISK No medium in the drive * STA_PROTECT Write protected * * @note In case Card detect signal is not connected, this function will not be *able to check if card is present. */ DSTATUS ff_disk_status(BYTE pdrv) { DSTATUS s = Stat[pdrv]; return s; } /** * @brief Initialize a disk drive. * * This function initializes a specified disk drive. If the drive is already * initialized, it resets the offset and returns the current status. Otherwise, * it attempts to open the drive and set the block size. If the drive cannot be * opened, it sets the 'no disk' status. * * @param pdrv The physical drive number (0..FF_VOLUMES - 1). * @return The status of the drive after initialization, represented as a * `DSTATUS` type. * STA_NODISK Disk is not present * STA_NOINIT Drive not initialized * STA_PROTECT Drive is write protected * 0 or only STA_PROTECT both indicate successful initialization. */ DSTATUS ff_disk_initialize(BYTE pdrv) { DSTATUS s; s = ff_disk_status(pdrv); /* If disk is already initialized */ if ((s & STA_NOINIT) == 0U) { Disk[pdrv].offset = 0; return s; } if (disk_open(VolumeStr[pdrv], &Disk[pdrv].dev) == 0) { disk_set_block_size(&Disk[pdrv].dev, SECTOR_SIZE); s &= (~STA_NOINIT); Stat[pdrv] = s; } else { s |= STA_NODISK; Stat[pdrv] = s; } return s; } /** * @brief Close a disk drive. * * This function closes a specified disk drive. If the drive is not initialized, * it simply returns the current status. Otherwise, it attempts to close the * drive and sets the 'not initialized' status flag. * * @param pdrv The physical drive number (0..FF_VOLUMES - 1). * @return The status of the drive after the close operation, represented as a * `DSTATUS` type. * STA_NODISK Disk is not present * STA_NOINIT Drive not initialized * STA_PROTECT Drive is write protected */ DSTATUS ff_disk_close(BYTE pdrv) { DSTATUS s; s = ff_disk_status(pdrv); /* If disk is not initialized */ if (s & STA_NOINIT) { return s; } disk_close(&Disk[pdrv].dev); s |= STA_NOINIT; Stat[pdrv] = s; return s; } /** * @brief Set the global offset of a disk drive. * * This function is used to set the global offset of a specified disk drive. * The offset is used to adjust the sector address when reading or writing data. * * @param pdrv The physical drive number (0..FF_VOLUMES - 1). * @param offset The start sector number (LBA) to be set as the global offset. * @return DRESULT Returns RES_OK to indicate the operation was successful. */ DRESULT disk_set_offset(BYTE pdrv, LBA_t offset) { Disk[pdrv].offset = offset; return RES_OK; } /** * @brief Read sectors from a disk drive. * * This function reads a specified number of sectors from a disk drive into a * buffer. It first checks the drive status and validates the sector count. If * the drive is not initialized or the sector count is zero, it returns an * appropriate error code. Otherwise, it calculates the destination address with * the global offset and reads the data. * * @param pdrv The physical drive number (0..FF_VOLUMES - 1). * @param buff Pointer to the data buffer to store the read data. * @param sector The start sector number (LBA). * @param count The number of sectors to read (1..128). * @return DRESULT Returns RES_OK if the read operation is successful, * RES_NOTRDY if the drive is not initialized, * RES_PARERR if the sector count is zero, * or RES_ERROR if the read operation fails. */ DRESULT ff_disk_read(BYTE pdrv, BYTE *buff, LBA_t sector, UINT count) { DSTATUS s; struct disk_dev *dev = &Disk[pdrv].dev; LBA_t dst_addr; ASSERT(dev); s = ff_disk_status(pdrv); if ((s & STA_NOINIT) != 0U) { return RES_NOTRDY; } if (count == 0U) { return RES_PARERR; } dst_addr = (sector + Disk[pdrv].offset) * SECTOR_SIZE; if (disk_read(dev, dst_addr, buff, count * SECTOR_SIZE)) return RES_ERROR; return RES_OK; } /** * @brief Perform disk I/O control operations. * * This function is used to perform various I/O control operations on a disk * drive. It checks the drive status and then executes the specified command. * * @param pdrv The physical drive number (0..FF_VOLUMES - 1). * @param cmd The control code specifying the operation to be performed. * @param buff A buffer to send or receive control data. * @return DRESULT Returns the result of the operation: * - RES_OK: Operation was successful. * - RES_NOTRDY: Drive is not initialized. * - RES_ERROR: An error occurred during the operation. * - RES_PARERR: An invalid command was provided. */ DRESULT ff_disk_ioctl(BYTE pdrv, BYTE cmd, void *buff) { DRESULT res = RES_OK; struct disk_dev *dev = &Disk[pdrv].dev; ASSERT(dev); void *LocBuff = buff; if ((ff_disk_status(pdrv) & STA_NOINIT) != 0U) { /* Check if card is in the socket */ return RES_NOTRDY; } res = RES_ERROR; switch (cmd) { case (BYTE)CTRL_SYNC: /* Make sure that no pending write process */ res = RES_OK; break; case (BYTE)GET_SECTOR_COUNT: /* Get number of sectors on the disk (DWORD) */ (*((DWORD *)(void *)LocBuff)) = disk_size(dev) / SECTOR_SIZE; res = RES_OK; break; case (BYTE) GET_BLOCK_SIZE: /* Get erase block size in unit of sector (DWORD) */ (*((DWORD *)((void *)LocBuff))) = disk_erase_size(dev) / SECTOR_SIZE; res = RES_OK; break; default: res = RES_PARERR; break; } return res; } /** * @brief Get the current time in FAT timestamp format. * * This function returns a timestamp in the format used by the FAT file system. * The timestamp is fixed to January 1, 2010, at 00:00:00. * * @return A DWORD representing the FAT timestamp. */ DWORD get_fattime(void) { return ((DWORD)(2010U - 1980U) << 25U) /* Fixed to Jan. 1, 2010 */ | ((DWORD)1 << 21) | ((DWORD)1 << 16) | ((DWORD)0 << 11) | ((DWORD)0 << 5) | ((DWORD)0 >> 1); } /** * @brief Write sectors to a disk drive. * * This function writes a specified number of sectors from a buffer to a disk * drive. It first checks the drive status and validates the sector count. If * the drive is not initialized or the sector count is zero, it returns an * appropriate error code. Otherwise, it calculates the destination address with * the global offset and writes the data. * * @param pdrv The physical drive number (0..FF_VOLUMES - 1). * @param buff Pointer to the data buffer containing the data to be written. * @param sector The start sector number (LBA). * @param count The number of sectors to write (1..128). * @return DRESULT Returns RES_OK if the write operation is successful, * RES_NOTRDY if the drive is not initialized, * RES_PARERR if the sector count is zero, * or RES_ERROR if the write operation fails. */ DRESULT ff_disk_write(BYTE pdrv, const BYTE *buff, LBA_t sector, UINT count) { DSTATUS s; struct disk_dev *dev = &Disk[pdrv].dev; LBA_t dst_addr; ASSERT(dev); s = ff_disk_status(pdrv); if ((s & STA_NOINIT) != 0U) { return RES_NOTRDY; } if (count == 0U) { return RES_PARERR; } dst_addr = (sector + Disk[pdrv].offset) * SECTOR_SIZE; if (disk_write(dev, dst_addr, buff, count * SECTOR_SIZE)) return RES_ERROR; return RES_OK; }