/*-----------------------------------------------------------------------*/
/* 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 */
#include "string.h"
#include "usbh_bsp_msc.h" /* 底层驱动 */

/* Definitions of physical drive number for each drive */
/* 为每个设备定义一个物理编号 */
#define FS_SD   0 // SD卡
#define FS_NAND 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 FS_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 status = STA_NOINIT;

    switch (pdrv)
    {
    case FS_SD:
        status &= ~STA_NOINIT;
        break;

    case FS_NAND:
        status = 0;
        break;
    case FS_USB:
        status = 0;
        break;
    }
    return status;
}

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

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

    switch (pdrv)
    {
    case FS_SD:
        if (SD_Init() == SD_OK)
        {
            status &= ~STA_NOINIT;
        }
        else
        {
            status = STA_NOINIT;
        }

        break;

    case FS_NAND:
        if (nand_flash_init() == NAND_OK)
        {
            status = RES_OK;
        }
        else
        {
            /* 如果初始化失败，请执行低级格式化 */
            printf("NAND_Init() Error!  \r\n");
            status = RES_ERROR;
        }
        break;
    case FS_USB:
        /* STM32 USB Host 口外接U盘 */
        if (HCD_IsDeviceConnected(&USB_OTG_Core))
        {
            status &= ~STA_NOINIT;
        }
        break;
    }
    return status;
}

/*-----------------------------------------------------------------------*/
/* 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  status   = RES_PARERR;
    SD_Error SD_state = SD_OK;

    switch (pdrv)
    {
    case FS_SD:
        if ((DWORD)buff & 3)
        {
            DRESULT res = RES_OK;
            DWORD   scratch[SD_BLOCKSIZE / 4];

            while (count--)
            {
                res = disk_read(FS_SD, (void *)scratch, sector++, 1);

                if (res != RES_OK)
                {
                    break;
                }
                memcpy(buff, scratch, SD_BLOCKSIZE);
                buff += SD_BLOCKSIZE;
            }
            return res;
        }

        SD_state = SD_ReadMultiBlocks(buff, sector * SD_BLOCKSIZE, SD_BLOCKSIZE, count);
        if (SD_state == SD_OK)
        {
            /* Check if the Transfer is finished */
            SD_state = SD_WaitReadOperation();
            while (SD_GetStatus() != SD_TRANSFER_OK)
                ;
        }
        if (SD_state != SD_OK)
            status = RES_PARERR;
        else
            status = RES_OK;
        break;

    case FS_NAND:
        if (NAND_OK == NAND_ReadMultiSectors(buff, sector, 512, count))
        {
            status = RES_OK;
        }
        else
        {
            printf("NAND_ReadMultiSectors() Error! sector = %d, count = %d \r\n", sector, count);
            status = RES_ERROR;
        }
        break;
    case FS_USB:
    {
        BYTE res = USBH_MSC_OK;

        if (HCD_IsDeviceConnected(&USB_OTG_Core))
        {
            do
            {
                res = USBH_MSC_Read10(&USB_OTG_Core, buff, sector, 512 * count);
                USBH_MSC_HandleBOTXfer(&USB_OTG_Core, &USB_Host);

                if (!HCD_IsDeviceConnected(&USB_OTG_Core))
                {
                    break;
                }
            } while (res == USBH_MSC_BUSY);
        }

        if (res == USBH_MSC_OK)
        {
            status = RES_OK;
        }
        else
        {
            status = RES_ERROR;
        }
    }
    break;
    }

    return status;
}

/*-----------------------------------------------------------------------*/
/* 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  status   = RES_PARERR;
    SD_Error SD_state = SD_OK;

    if (!count)
    {
        return RES_PARERR; /* Check parameter */
    }

    switch (pdrv)
    {
    case FS_SD: /* SD CARD */
        if ((DWORD)buff & 3)
        {
            DRESULT res = RES_OK;
            DWORD   scratch[SD_BLOCKSIZE / 4];

            while (count--)
            {
                memcpy(scratch, buff, SD_BLOCKSIZE);
                res = disk_write(FS_SD, (void *)scratch, sector++, 1);
                if (res != RES_OK)
                {
                    break;
                }
                buff += SD_BLOCKSIZE;
            }
            return res;
        }

        SD_state = SD_WriteMultiBlocks((uint8_t *)buff, sector * SD_BLOCKSIZE, SD_BLOCKSIZE, count);
        if (SD_state == SD_OK)
        {
            /* Check if the Transfer is finished */
            SD_state = SD_WaitWriteOperation();

            /* Wait until end of DMA transfer */
            while (SD_GetStatus() != SD_TRANSFER_OK)
                ;
        }
        if (SD_state != SD_OK)
            status = RES_PARERR;
        else
            status = RES_OK;
        break;

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

        if (HCD_IsDeviceConnected(&USB_OTG_Core))
        {
            do
            {
                res = USBH_MSC_Write10(&USB_OTG_Core, (BYTE *)buff, sector, 512 * count);
                USBH_MSC_HandleBOTXfer(&USB_OTG_Core, &USB_Host);

                if (!HCD_IsDeviceConnected(&USB_OTG_Core))
                {
                    break;
                }
            } while (res == USBH_MSC_BUSY);
        }

        if (res == USBH_MSC_OK)
        {
            status = RES_OK;
        }
        else
        {
            status = RES_ERROR;
        }
    }
    break;
    }

    return status;
}
#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 status = RES_PARERR;

    switch (pdrv)
    {
    case FS_SD: /* SD CARD */
        switch (cmd)
        {
        // Get R/W sector size (WORD)
        case GET_SECTOR_SIZE:
            *(WORD *)buff = SD_BLOCKSIZE;
            break;
        // Get erase block size in unit of sector (DWORD)
        case GET_BLOCK_SIZE:
            *(DWORD *)buff = 1; // SDCardInfo.CardBlockSize;
            break;

        case GET_SECTOR_COUNT:
            *(DWORD *)buff = SDCardInfo.CardCapacity / SDCardInfo.CardBlockSize;
            break;
        case CTRL_SYNC:
            break;
        }
        status = RES_OK;
        break;

    case FS_NAND:
        status = RES_OK;
        break;
    case FS_USB:
        // if (Stat & STA_NOINIT) return RES_NOTRDY;
        switch (cmd)
        {
        case CTRL_SYNC: /* Make sure that no pending write process */
            status = RES_OK;
            break;

        case GET_SECTOR_COUNT: /* Get number of sectors on the disk (DWORD) */
            *(DWORD *)buff = (DWORD)USBH_MSC_Param.MSCapacity;
            status         = RES_OK;
            break;

        case GET_SECTOR_SIZE: /* Get R/W sector size (WORD) */
            *(WORD *)buff = 512;
            status        = RES_OK;
            break;

        case GET_BLOCK_SIZE: /* Get erase block size in unit of sector (DWORD) */
            *(DWORD *)buff = 512;
            status         = RES_OK;
            break;

        default:
            status = RES_PARERR;
            break;
        }
        break;
    }

    return status;
}

/*
*********************************************************************************************************
*	函 数 名: 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
