286 lines
8.5 KiB
C
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;
|
|
}
|