/*-----------------------------------------------------------------------*/
/* Low level disk I/O module skeleton for FatFs     (C)ChaN, 2014        */
/*-----------------------------------------------------------------------*/
/* If a working storage control module is available, it should be        */
/* attached to the FatFs via a glue function rather than modifying it.   */
/* This is an example of glue functions to attach various exsisting      */
/* storage control modules to the FatFs module with a defined API.       */
/*-----------------------------------------------------------------------*/

#include "diskio.h" /* FatFs lower layer API */
// #include "atadrive.h" /* Example: ATA drive control */
#include "nand_flash.h"
#include "sdcard.h" /* Example: MMC/SDC contorl */
// #include "usbdisk.h"  /* Example: USB drive control */

/* Definitions of physical drive number for each drive */
/* 为每个设备定义一个物理编号 */
#define ATA 0 // SD卡
#define MMC 1 // 预留外部SPI Flash使用
// #define ATA       0 /* Example: Map ATA drive to drive number 0 */
// #define MMC       1 /* Example: Map MMC/SD card to drive number 1 */
#define USB 2 /* Example: Map USB drive to drive number 2 */

#define SD_BLOCKSIZE 512

extern SD_CardInfo SDCardInfo;

/*-----------------------------------------------------------------------*/
/* 获取设备状态                                                          */
/*-----------------------------------------------------------------------*/
DSTATUS disk_status(
    BYTE pdrv /* Physical drive nmuber to identify the drive */
)
{
    DSTATUS stat = STA_NOINIT;
    int     result;

    switch (pdrv)
    {
    case ATA:
        stat = 0;

        return stat;

    case MMC:
        stat = 0;
        return stat;

    case USB:
        // result = USB_disk_status();

        // translate the reslut code here

        return stat;
    }
    return STA_NOINIT;
}

/*-----------------------------------------------------------------------*/
/* Inidialize a Drive                                                    */
/*-----------------------------------------------------------------------*/

DSTATUS disk_initialize(
    BYTE pdrv /* Physical drive nmuber to identify the drive */
)
{
    DSTATUS  stat = STA_NOINIT;
    SD_Error result;

    switch (pdrv)
    {
    case ATA:
        if (SD_Init() == SD_OK)
        {
            stat = RES_OK;
        }
        else
        {
            stat = STA_NODISK;
        }

        return stat;

    case MMC:
        if (nand_flash_init() == NAND_OK)
        {
            stat = RES_OK;
        }
        else
        {
            /* 如果初始化失败，请执行低级格式化 */
            printf("NAND_Init() Error!  \r\n");
            stat = RES_ERROR;
        }
        return stat;

    case USB:
        // result = USB_disk_initialize();

        // translate the reslut code here

        return stat;
    }
    return STA_NOINIT;
}

/*-----------------------------------------------------------------------*/
/* Read Sector(s)                                                        */
/*-----------------------------------------------------------------------*/

DRESULT disk_read(
    BYTE  pdrv,   /* Physical drive nmuber to identify the drive */
    BYTE *buff,   /* Data buffer to store read data */
    DWORD sector, /* Sector address in LBA */
    UINT  count   /* Number of sectors to read */
)
{
    DRESULT res;
    DRESULT status = RES_PARERR;
    int     result;

    switch (pdrv)
    {
    case ATA:
    {
        SD_Error Status = SD_OK;

        if (count == 1)
        {
            Status = SD_ReadBlock(buff, sector << 9, SD_BLOCKSIZE);
        }
        else
        {
            Status = SD_ReadMultiBlocks(buff, sector << 9, SD_BLOCKSIZE, count);
        }
        if (Status != SD_OK)
        {
            res = RES_ERROR;
            break;
        }

#ifdef SD_DMA_MODE
        /* SDIO工作在DMA模式，需要检查操作DMA传输是否完成 */
        Status = SD_WaitReadOperation();
        if (Status != SD_OK)
        {
            res = RES_ERROR;
            break;
        }

        while (SD_GetStatus() != SD_TRANSFER_OK)
            ;
#endif

        res = RES_OK;
        break;
    }

    case MMC:
        if (NAND_OK == NAND_ReadMultiSectors(buff, sector, 512, count))
        {
            res = RES_OK;
        }
        else
        {
            printf("NAND_ReadMultiSectors() Error! sector = %d, count = %d \r\n", sector, count);
            res = RES_ERROR;
        }
        return res;

    case USB:
        // translate the arguments here

        // result = USB_disk_read(buff, sector, count);

        // translate the reslut code here

        return res;
    }

    return RES_PARERR;
}

/*-----------------------------------------------------------------------*/
/* Write Sector(s)                                                       */
/*-----------------------------------------------------------------------*/

#if _USE_WRITE
DRESULT disk_write(
    BYTE        pdrv,   /* Physical drive nmuber to identify the drive */
    const BYTE *buff,   /* Data to be written */
    DWORD       sector, /* Sector address in LBA */
    UINT        count   /* Number of sectors to write */
)
{
    DRESULT  res;
    int      result;
    SD_Error SD_state = SD_OK;

    switch (pdrv)
    {
    case ATA: /* SD CARD */
    {
        SD_Error Status = SD_OK;

        if (count == 1)
        {
            Status = SD_WriteBlock((uint8_t *)buff, sector << 9, SD_BLOCKSIZE);

            if (Status != SD_OK)
            {
                res = RES_ERROR;
                break;
            }

#ifdef SD_DMA_MODE
            /* SDIO工作在DMA模式，需要检查操作DMA传输是否完成 */
            Status = SD_WaitReadOperation();
            if (Status != SD_OK)
            {
                res = RES_ERROR;
                break;
            }
            while (SD_GetStatus() != SD_TRANSFER_OK)
                ;
#endif
            res = RES_OK;
        }
        else
        {
            /* 此处存在疑问： 扇区个数如果写 count ，将导致最后1个block无法写入 */
            // Status = SD_WriteMultiBlocks((uint8_t *)buff, sector << 9 ,SECTOR_SIZE, count);
            Status = SD_WriteMultiBlocks((uint8_t *)buff, sector << 9, SD_BLOCKSIZE, count + 1);

            if (Status != SD_OK)
            {
                res = RES_ERROR;
                break;
            }

#ifdef SD_DMA_MODE
            /* SDIO工作在DMA模式，需要检查操作DMA传输是否完成 */
            Status = SD_WaitReadOperation();
            if (Status != SD_OK)
            {
                res = RES_ERROR;
                break;
            }
            while (SD_GetStatus() != SD_TRANSFER_OK)
                ;
#endif
            res = RES_OK;
        }
        break;
    }

    case MMC:
        if (NAND_OK == NAND_WriteMultiSectors((uint8_t *)buff, sector, 512, count))
        {
            res = RES_OK;
        }
        else
        {
            printf("NAND_ReadMultiSectors() Error! sector = %d, count = %d \r\n", sector, count);
            res = RES_ERROR;
        }
        return res;

    case USB:
        // translate the arguments here

        // result = USB_disk_write(buff, sector, count);

        // translate the reslut code here

        return res;
    }

    return RES_PARERR;
}
#endif

/*-----------------------------------------------------------------------*/
/* Miscellaneous Functions                                               */
/*-----------------------------------------------------------------------*/

#if _USE_IOCTL
DRESULT disk_ioctl(
    BYTE  pdrv, /* Physical drive nmuber (0..) */
    BYTE  cmd,  /* Control code */
    void *buff  /* Buffer to send/receive control data */
)
{
    DRESULT res;
    int     result;

    switch (pdrv)
    {
    case ATA: /* SD CARD */
        /* SD卡磁盘容量： SDCardInfo.CardCapacity */
        res = RES_OK;
        return res;

    case MMC:
        res = RES_OK;
        return res;

    case USB:

        // Process of the command the USB drive

        return res;
    }

    return RES_PARERR;
}

/*
*********************************************************************************************************
*	函 数 名: get_fattime
*	功能说明: 获得系统时间，用于改写文件的创建和修改时间。
*	形    参：无
*	返 回 值: 无
*********************************************************************************************************
*/
// weak DWORD get_fattime(void)
// {
//     /* 如果有全局时钟，可按下面的格式进行时钟转换. 这个例子是2013-01-01 00:00:00 */

//     return ((DWORD)(2013 - 1980) << 25) /* Year = 2013 */
//            | ((DWORD)1 << 21)           /* Month = 1 */
//            | ((DWORD)1 << 16)           /* Day_m = 1*/
//            | ((DWORD)0 << 11)           /* Hour = 0 */
//            | ((DWORD)0 << 5)            /* Min = 0 */
//            | ((DWORD)0 >> 1);           /* Sec = 0 */
// }

#endif
