/***********************************************************************************************************************
 * DISCLAIMER
 * This software is supplied by Renesas Electronics Corporation and is only intended for use with Renesas products. No
 * other uses are authorized. This software is owned by Renesas Electronics Corporation and is protected under all
 * applicable laws, including copyright laws.
 * THIS SOFTWARE IS PROVIDED "AS IS" AND RENESAS MAKES NO WARRANTIES REGARDING
 * THIS SOFTWARE, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING BUT NOT LIMITED TO WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. ALL SUCH WARRANTIES ARE EXPRESSLY DISCLAIMED. TO THE MAXIMUM
 * EXTENT PERMITTED NOT PROHIBITED BY LAW, NEITHER RENESAS ELECTRONICS CORPORATION NOR ANY OF ITS AFFILIATED COMPANIES
 * SHALL BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES FOR ANY REASON RELATED TO THIS
 * SOFTWARE, EVEN IF RENESAS OR ITS AFFILIATES HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
 * Renesas reserves the right, without notice, to make changes to this software and to discontinue the availability of
 * this software. By using this software, you agree to the additional terms and conditions found by accessing the
 * following link:
 * http://www.renesas.com/disclaimer
 *
 * Copyright (C) 2014(2017) Renesas Electronics Corporation. All rights reserved.
 ***********************************************************************************************************************/
/***********************************************************************************************************************
 * File Name     : r_ram_disk.c
 * Description   : Implements top level of API compliant block media driver for 'RAM-disk'. 
 ***********************************************************************************************************************/
/***********************************************************************************************************************
 * History : DD.MM.YYYY Version Description
 *         : 08.01.2014 1.00 First Release
 *         : 26.12.2014 1.10 RX71M is added
 *         : 30.09.2015 1.11 RX63N/RX631 is added.
 *         : 30.09.2016 1.20 RX65N/RX651 is added.
 *         : 30.09.2017 1.22 Delete LUN List.
 *                           Delete usb_pmsc_ramdisk_section_init(),g_ram_get_block_size(),ram_get_num_blocks().
 *                           Fix ram_disk_open(),ram_disk_close(),ram_disk_read(),ram_disk_write(),ram_disk_ioctl().
 ***********************************************************************************************************************/

/***********************************************************************************************************************
 Includes   <System Includes> , "Project Includes"
 ***********************************************************************************************************************/
#include <string.h>
#include "r_ram_disk.h"
#include "r_cg_userdefine.h"
#include "r_flash_rx_if.h"


/*******************************************************************************
 * Private global variables and functions
 *******************************************************************************/
static usb_media_ret_t ram_disk_open (void);
static usb_media_ret_t ram_disk_close (void);
static usb_media_ret_t ram_disk_read (uint8_t* read_buffer, uint32_t sector, uint8_t sector_count);
static usb_media_ret_t ram_disk_write (uint8_t* write_buffer, uint32_t sector, uint8_t sector_count);
static usb_media_ret_t ram_disk_ioctl (uint8_t command_code, uint32_t* data_buffer);

/******************************************************************************
Exported global variables (to be accessed by other files)
******************************************************************************/
/* to store the filesystem driver function pointers */
usb_media_driver_t g_ram_mediadriver = 
{
    &ram_disk_open,
    &ram_disk_close,
    &ram_disk_read,
    &ram_disk_write,
    &ram_disk_ioctl
};

/***********************************************************************************************************************
 * Function Name: ram_disk_open
 * Description  :  
 * Argument     : none
 * Return value :     R_RES_OK  = 0     Successful
 *
 ***********************************************************************************************************************/
static usb_media_ret_t ram_disk_open (void)
{
	flash_err_t err;
	static uint32_t		addrWrite;

	err = R_FLASH_Open();
	if (FLASH_SUCCESS != err)
	{
		LED1 = 1;
	}
	for( addrWrite = USBC_PMSC_STORAGE_FLASH_ADDR;
		 addrWrite <= (USBC_PMSC_STORAGE_FLASH_ADDR + STRG_MEDIASIZE - 1); ){

		R_BSP_InterruptsDisable();
		R_FLASH_Erase(addrWrite, 1);
		R_BSP_InterruptsEnable();
		addrWrite += USBC_PMSC_STORAGE_FLASH_BLOCKSIZE;
	}
	LED0 = 1;
	LED1 = 1;

    return USB_MEDIA_RET_OK;
} /* End of function ram_disk_open */

/***********************************************************************************************************************
 * Function Name: ram_disk_close
 * Description  :  
 * Argument     : none
 * Return value :     R_RES_OK  = 0     Successful
 *
 ***********************************************************************************************************************/
static usb_media_ret_t ram_disk_close (void)
{
#if 0
    usb_cpu_sdram_close();          /* Sdram disable */
#endif
    return USB_MEDIA_RET_OK;
} /* End of function ram_disk_close */

/***********************************************************************************************************************
 * Function Name: ram_disk_read
 * Description  :  
 * Argument     :     uint8_t* read_buffer,     destination buffer for read data
 *                    uint32_t sector,         the starting sector number
 *                    uint8_t sector_count)    number of sectors to be read
 *
 * Return value :     R_RES_OK  = 0     Successful
 *                    R_RES_ERROR = 1  R/W Error
 * Note:    This function does not check for destination buffer overrun.
 *          Make absolutely sure that enough memory has been allocated to accept 
 *          the data that will be read. This is especially important when the 
 *          sector count is greater than 1, because typically only 1 sector is 
 *          read at a time.
 ***********************************************************************************************************************/
static usb_media_ret_t ram_disk_read (uint8_t* read_buffer, uint32_t sector, uint8_t sector_count)
{
	uint32_t i;
    if(sector >= LBA_BASE_STORAGE_CLUSTERS){
	    memcpy(read_buffer, (void *)(USBC_PMSC_STORAGE_FLASH_ADDR + ((sector-LBA_BASE_STORAGE_CLUSTERS) * STRG_SECTSIZE)), (STRG_SECTSIZE * sector_count));
    } else {
        for(i = 0; i < sector_count; i++){
       		if(sector == 0){ //
       			uint8_t*	buf = read_buffer;
       			uint8_t*	part = buf + 0x1BE;

       			memset( buf, 0, STRG_SECTSIZE );
       			part[0]	= 0x00;
       			part[1]	= 0x00;
       			part[2]	= MBR_SECTOR_COUNT + 1;
       			part[3]	= 0x00;
       			part[4]	= MBR_PARTITION_ID;
       			part[5]	= MBR_END_HEAD_NO;
       			part[6]	= MBR_END_SECTOR_NO | ((MBR_END_CYLINDER_NO>>2) & 0xC0);
       			part[7]	= MBR_END_CYLINDER_NO & 0xFF;
       			*((uint32_t*)(part+8))	= MBR_SECTOR_COUNT;
       			*((uint32_t*)(part+12))	= FATFS_VOLUME_TOTAL_SECTOR_COUNT;

       			buf[0x1FE] = 0x55;
       			buf[0x1FF] = 0xAA;

       		} else if(sector == LBA_BASE_VOLUME) { // BPB
       			memcpy(read_buffer+(i*STRG_SECTSIZE), g_ram_disk_boot_sector, STRG_SECTSIZE);
            } else if(sector < LBA_BASE_FAT1) { // Reserved Sector
       			memset(read_buffer+(i*STRG_SECTSIZE), 0, STRG_SECTSIZE);
        	} else if(sector < LBA_BASE_FAT2){ // FAT1
       			memcpy(read_buffer+(i*STRG_SECTSIZE), g_usb_pmsc_tablefat+((sector-LBA_BASE_FAT1)*STRG_SECTSIZE), STRG_SECTSIZE);
        	} else if(sector < LBA_BASE_ROOT_DIR){ // FAT2
       			memcpy(read_buffer+(i*STRG_SECTSIZE), g_usb_pmsc_tablefat+((sector-LBA_BASE_FAT2)*STRG_SECTSIZE), STRG_SECTSIZE);
        	} else if(sector < LBA_BASE_HTML_FILE_CLUSTER){ // Root Directory
       			memcpy(read_buffer+(i*STRG_SECTSIZE), g_usb_pmsc_rootdir+((sector-LBA_BASE_ROOT_DIR)*STRG_SECTSIZE), STRG_SECTSIZE);
        	} else if(sector < LBA_BASE_STORAGE_CLUSTERS){ // HTML file cluster
       			memcpy(read_buffer+(i*STRG_SECTSIZE), g_usb_pmsc_htmlfile+((sector-LBA_BASE_HTML_FILE_CLUSTER)*STRG_SECTSIZE), STRG_SECTSIZE);
        	} else {
        	}
       		sector++;
        }
    }
    return USB_MEDIA_RET_OK;
} /* End of function ram_disk_read */

/***********************************************************************************************************************
 * Function Name: ram_disk_write
 * Description  :  
 * Argument     :    uint8_t* write_buffer,  source for data to be written from
 *                    uint32_t sector,         the starting sector number
 *                    uint8_t sector_count     number of sectors to be written
 *                     
 * Return value :     R_RES_OK  = 0     Successful
 *                    R_RES_ERROR = 1  R/W Error
 ***********************************************************************************************************************/
static usb_media_ret_t ram_disk_write (uint8_t* write_buffer, uint32_t sector, uint8_t sector_count)
{
	flash_err_t err;
	uint32_t adr, i;
    if(sector >= LBA_BASE_STORAGE_CLUSTERS){
    	adr = USBC_PMSC_STORAGE_FLASH_ADDR + ((sector-LBA_BASE_STORAGE_CLUSTERS) * STRG_SECTSIZE);
    	R_BSP_InterruptsDisable();
    	err = R_FLASH_Write(write_buffer, adr, (STRG_SECTSIZE * sector_count));
    	if (FLASH_SUCCESS != err)
    	{
    		LED1 = 1;
    	}
    	R_BSP_InterruptsEnable();
    } else {
        for(i = 0; i < sector_count; i++){
       		if(sector == 0){ // MBR
       			LED0 = 1;
       		} else if(sector == LBA_BASE_VOLUME) { // BPB
       			LED0 = 1;
            } else if(sector < LBA_BASE_FAT1) { // Reserved Sector
            	LED0 = 1;
        	} else if(sector < LBA_BASE_FAT2){ // FAT1
       			memcpy(g_usb_pmsc_tablefat+((sector-LBA_BASE_FAT1)*STRG_SECTSIZE), write_buffer+(i*STRG_SECTSIZE), STRG_SECTSIZE);
        	} else if(sector < LBA_BASE_ROOT_DIR){ // FAT2
       			memcpy(g_usb_pmsc_tablefat+((sector-LBA_BASE_FAT2)*STRG_SECTSIZE), write_buffer+(i*STRG_SECTSIZE), STRG_SECTSIZE);
        	} else if(sector < LBA_BASE_HTML_FILE_CLUSTER){ // Root Directory
       			memcpy(g_usb_pmsc_rootdir+((sector-LBA_BASE_ROOT_DIR)*STRG_SECTSIZE), write_buffer+(i*STRG_SECTSIZE), STRG_SECTSIZE);
        	} else if(sector < LBA_BASE_STORAGE_CLUSTERS){ // HTML file cluster
       			memcpy(g_usb_pmsc_htmlfile+((sector-LBA_BASE_HTML_FILE_CLUSTER)*STRG_SECTSIZE), write_buffer+(i*STRG_SECTSIZE), STRG_SECTSIZE);
        	} else {
        	}
       		sector++;
        }
    }

    return USB_MEDIA_RET_OK;
} /* End of function ram_disk_write */

/***********************************************************************************************************************
 * Function Name: ram_disk_ioctl
 * Description  :  
 * Argument     : (uint8_t command_code, uint32_t* data_buffer)
 * Return value :     R_RES_OK  = 0     Successful
 *
 ***********************************************************************************************************************/
static usb_media_ret_t ram_disk_ioctl (uint8_t command_code, uint32_t* data_buffer)
{
    switch (command_code)
    {

        case USB_MEDIA_IOCTL_GET_NUM_BLOCKS :

            /* Gets the number of logical blocks in the RAM disk */
            *data_buffer = (uint32_t) USBC_ATAPI_TOTAL_SECTOR_COUNT;

        break;

        case USB_MEDIA_IOCTL_GET_SECTOR_SIZE :
        case USB_MEDIA_IOCTL_GET_BLOCK_SIZE :

            /* Get the size of the logical block in the RAM disk */
            *data_buffer = (uint32_t) STRG_SECTSIZE;

        break;

        case USB_MEDIA_IOCTL_SYNC :

            /* TODO: Flush write cache if necessary */

        break;

        default :

            return USB_MEDIA_RET_PARERR;

        break;
    }
    return USB_MEDIA_RET_OK;
} /* End of function ram_disk_ioctl */


/******************************************************************************
* Function Name	:	fatfs_FindUserApplicationFileInfo()
* Description	:	Search file information of user application.
* Argument		:	none
* Return		:	not NULL	= Found file information.
*				:	NULL		= not found.
******************************************************************************/
PDIRENT fatfs_FindUserApplicationFileInfo( void )
{
	uint16_t	i;

	//
	//	ルートディレクトリエントリを検索する
	//
	PDIRENT	pdeFound = NULL;
	PDIRENT	pde = (PDIRENT)g_usb_pmsc_rootdir;
	for( i = 0; i < FATFS_ROOT_DIR_COUNT; i ++, pde ++ )
	{
		if( pde->bAttributes == 0x0F )
		{
			continue;	// Long filename
		}
		if( pde->bAttributes == 0x08 )
		{
			continue;	// Volume label
		}
		if( pde->bAttributes & 0x16 )
		{
			continue;	// Sub directory, System file, Hidden file
		}
		if( pde->bFilename[0] == 0xE5 )
		{
			continue;	// Deleted
		}
		if( pde->bFilename[0] == 0x00 )
		{
			continue;	// unused
		}
		if( ((pde->bExtension[0]&0xDF) == 'B') &&
			((pde->bExtension[1]&0xDF) == 'I') &&
			((pde->bExtension[2]&0xDF) == 'N') )
		{
			if( pdeFound == NULL )
			{
				pdeFound = pde;
			}
			else if( pde->wLastWriteDate > pdeFound->wLastWriteDate )
			{
				pdeFound = pde;
			}
			else if( pde->wLastWriteDate == pdeFound->wLastWriteDate )
			{
				if( pde->wLastWriteTime >= pdeFound->wLastWriteTime )
				{
					pdeFound = pde;
				}
			}
		}

	} //end of for( i = 0; i < FATFS_ROOT_DIR_COUNT; i ++, pde ++ )

	return pdeFound;
}
/******************************************************************************
End of function
******************************************************************************/

/******************************************************************************
* Function Name	:	fatfs_GetNextClusterNo()
* Description	:	Get next cluster No.
* Argument		:	Current cluster No.
* Return		:	Next cluster No.
******************************************************************************/
uint16_t fatfs_GetNextClusterNo( const uint16_t clusCur )
{
#if 	ENABLE_FAT16

	{
		//
		//	FAT16の場合
		//
		const uint32_t	ofst = (clusCur * 2);
#if 	ENABLE_KEEP_FILE_INFO
		const uint16_t*	pwFat = (uint16_t*)(USBC_PMSC_DATA_FLASH_ADDR + ofst);
#else
		const uint16_t*	pwFat = (uint16_t*)&usb_gpmsc_AtapiFatBuffer[ofst];
#endif
		uint16_t		clusNext = *pwFat;

		if( clusNext >= 0xFFF8 )
		{
			clusNext = (uint16_t)-1;
		}
		return clusNext;
	}

#else	//ENABLE_FAT16

	{
		//
		//	FAT12の場合
		//
		const uint32_t	ofst = (clusCur / 2) * 3;
#if 	ENABLE_KEEP_FILE_INFO
		const uint8_t*	pbFat = (uint8_t*)(USBC_PMSC_DATA_FLASH_ADDR + ofst);
#else
		const uint8_t*	pbFat = &g_usb_pmsc_tablefat[ofst];
#endif
		uint16_t		clusNext;
		if( clusCur & 1 )
		{
			clusNext = ((uint16_t)pbFat[2] << 4) | ((uint16_t)(pbFat[1]&0xF0) >> 4);
		}
		else
		{
			clusNext = ((uint16_t)(pbFat[1]&0x0F) << 8) | (uint16_t)pbFat[0];
		}
		if( clusNext >= 0x0FF8 )
		{
			clusNext = (uint16_t)-1;
		}
		return clusNext;
	}

#endif	//ENABLE_FAT16
}
/******************************************************************************
End of function
******************************************************************************/


/******************************************************************************
* Function Name	:	fatfs_GetClusterData()
* Description	:	Get cluster data
* Argument		:	Cluster No.
* Return		:	Data buffer
******************************************************************************/
void* fatfs_GetClusterData( const uint16_t nClusNo )
{
	const uint32_t	nStorageClusNo = (uint32_t)(nClusNo - 2 - HTML_FILE_DATA_CLUSTER_COUNT);
	return (void*)(USBC_PMSC_STORAGE_FLASH_ADDR + nStorageClusNo * FATFS_CLUSTER_SIZE);
}
/******************************************************************************
End of function
******************************************************************************/
