Files
2026-01-12 18:46:01 +08:00

286 lines
8.5 KiB
C

#include <assert.h>
#include <debug.h>
#include <disk.h>
#include <stdlib.h>
#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;
}