/*******************************************************************************
 * (c) Copyright 2012-2013 Microsemi SoC Products Group.  All rights reserved.
 *
 *
 * USB MSC Class Host controller example application to demonstrate the
 * SmartFusion2 MSS USB operations in USB Host mode.
 *
 * This project can detect "Memory Stick" category devices and perform file
 * operations on it. To know more about how to use this project please refer
 * README.TXT in this project's folder.
 *
 * SVN $Revision:  $
 * SVN $Date:  $
 */

#include <string.h>
#include <stdio.h>
#include "ff.h"
#include "diskio.h"
#include "mss_usb_host.h"
#include "mss_usb_host_cif.h"
#include "mss_usb_host_msc.h"
#include "mss_usb_host.h"
#include "mss_usb_std_def.h"
#include "mss_uart.h"
#include "mss_gpio.h"
#include "flash_drive_app.h"
#include "mss_usb_common_reg_io.h"
#include "mss_usb_device_cif.h"
#include "mss_usb_device.h"
#include "mss_usb_device_msd.h"
#include "m2sxxx.h"
#include "winbondflash.h"
#define SYS_TICK_LOAD_VALUE                             50000u  /*For 1ms*/
#define MAX_ELEMENT_COUNT                               10u
#define ESC_KEY                                         27u
#define ENTER_KEY                                       13u
#define BUFFER_SIZE 512
FATFS fs;
FRESULT res;
FIL fsrc;
FIL fdest;
uint8_t buffer[512] = {0u};
uint8_t new_fname[13u];

uint8_t g_page_buffer[BUFFER_SIZE];
char path[] = "0:";
char* file_name[MAX_ELEMENT_COUNT][13] = {{0}};
BYTE file_attrib[MAX_ELEMENT_COUNT] = {0};
uint8_t g_copy_file_index = 0;
extern mss_usbd_user_descr_cb_t flash_drive_descriptors_cb;
extern mss_usbd_msc_media_t usb_flash_media;
FRESULT scan_root_dir (char* path);/*Start node to be scanned (also used as work area)*/

//*&************************************************************


mss_uart_instance_t * const gp_my_uart = &g_mss_uart1;


//***********************************************************

void USB_DEV_attached(mss_usb_device_speed_t speed);
void USB_DEV_dettached(void);
void USB_DEV_oc_error(void);
void USB_DEV_enumerated(mss_usbh_tdev_info_t tdev_info);
void USB_DEV_class_driver_assigned(void);
void USB_DEV_not_supported(int8_t error_code);
void USB_DEV_permanent_erro(void);

void MSC_DEV_valid_config(void);
void MSC_DEV_ready(void);
void MSC_DEV_driver_released(void);
void MSC_DEV_error(int8_t error_code);

mss_usbh_user_cb_t MSS_USBH_user_cb =
{
  USB_DEV_attached,
  USB_DEV_dettached,
  USB_DEV_oc_error,
  USB_DEV_enumerated,
  USB_DEV_class_driver_assigned,
  USB_DEV_not_supported,
  USB_DEV_permanent_erro
};

mss_usbh_msc_user_cb_t MSS_USBH_MSC_user_cb =
{
  MSC_DEV_valid_config,
  MSC_DEV_ready,
  MSC_DEV_driver_released,
  MSC_DEV_error,
};

/*Global variables*/
volatile uint8_t g_msc_driver_ready = 0u;
volatile uint8_t g_msc_driver_released = 0u;
void delay_loop(volatile uint32_t n)
{
	while(n)
		n--;
}
extern mss_usbd_cb_t  ;
extern mss_usb_host_cb_t g_mss_usb_host_cb;
int main()
{

    volatile uint8_t fmount = 0,fhost =1;

    SYSREG->WDOG_CR = 0x0000;
    MSS_GPIO_init();
    MSS_GPIO_config( MSS_GPIO_0 , MSS_GPIO_OUTPUT_MODE );

    /*Keep USB PHY out of Reset*/
    MSS_GPIO_set_output( MSS_GPIO_0 , 0 );
    MSS_UART_init( gp_my_uart,
    		MSS_UART_115200_BAUD,
                  MSS_UART_DATA_8_BITS | MSS_UART_NO_PARITY | MSS_UART_ONE_STOP_BIT);

    MSS_UART_polled_tx_string(gp_my_uart,
                              (uint8_t *)"\n\r\n\r*** SmartFusion2: Mass Storage USB OTG Demo ***\n\r");
    MSS_UART_polled_tx_string(gp_my_uart,
                                 (uint8_t *)" SmartFusion2 USB OTG changes it's role as per the connected device\n\r");



    FLASH_init();
	FLASH_global_unprotect();
    /*Initialize SysTick*/
    SysTick_Config(SYS_TICK_LOAD_VALUE);
    NVIC_EnableIRQ(SysTick_IRQn);
    /*Initialize the USBH driver*/
    MSS_USBH_init(&MSS_USBH_user_cb);

    /*Initialize the USBH-MSC driver*/
	MSS_USBH_MSC_init(&MSS_USBH_MSC_user_cb);
    /*Initialize the USBD driver*/
    MSS_USBD_init(MSS_USB_DEVICE_HS);

    /*
     Get Class driver handle from the USBH-MSC class driver and register it with
     the USBH driver.
     On Device attachment, USBH driver will look for Device information through
     Descriptors and if match it with the Class driver using this Handle.
     */
    MSS_USBH_register_class_driver(MSS_USBH_MSC_get_handle());
    MSS_USBD_set_descr_cb_handler(&flash_drive_descriptors_cb);

    while(1)
    {

    	if(MSS_USB_CORE_MODE_HOST == MSS_USB_CIF_get_mode() && !(USB->DEV_CTRL &0x80)  )
    	{
    		fhost = 1;
    		if(g_msc_driver_ready == 1u )
			{
				g_msc_driver_ready = 0u;
				g_msc_driver_released = 0u;
				delay_loop(50000);
				/*
				 Register the FatFS Work area.
				 Note:   Only one Logical unit (drive 0:) is supported.
				 If an Unformatted Drive is connected, this API will take long time to
				 complete its operation (Depending on drive size).
				 */
				f_mount(0, &fs);
				delay_loop(50000);
                fmount = 1;
			}

			if(g_msc_driver_released == 0 && fmount ==1)
			{
				scan_root_dir(path);
				fmount = 0;
			}
		}
    	else
    	{
    		if(MSS_USB_CORE_MODE_DEVICE == MSS_USB_CIF_get_mode())
			{

                if(fhost == 1 )
                {
                	fhost = 0;
                	NVIC_ClearPendingIRQ(USB_IRQn);
					MSS_USB_CIF_enable_usbirq(0xFFu);
                	MSS_USB_HCIF_read_vbus_level();
                	g_mss_usb_host_cb.usb_host_disconnect();
					MSS_USBD_init(MSS_USB_DEVICE_HS);
                	MSS_USBD_MSC_init(&usb_flash_media, MSS_USB_DEVICE_HS);

                }


			}
    	}
    	//st = MSS_USBH_host_get_tdev_state(0);
    }
}

void SysTick_Handler(void)
{
    /*
     This function must be called. This function provides the time stamp
     which is used to keep track of activities such as USB Reset, Settling time etc.
     in Enumeration process.
     The USBH driver and USBH-MSC class driver housekeeping task is also run based
     on this timestamp.

     This avoids the application to wait for the enumeration process to complete
     which might take time in 100s of miliseconds depending on target device.

     You may choose a general purpose timer instead of Systick.
     */
	MSS_USBH_task();
	MSS_USBH_MSC_task();
	MSS_USBH_1ms_tick();
}

FRESULT scan_root_dir (
    char* path        /* Start node to be scanned (also used as work area) */
)
{
    FRESULT res;
    FILINFO fno;
    DIR dir;
    volatile int elements = 0u;
    /* This function is assuming non-Unicode cfg. */

    MSS_UART_polled_tx_string(gp_my_uart,(uint8_t *)"\n\rElements in Root Directory (Maximum 10):");

    res = f_opendir(&dir, path);                       /* Open the directory */
    if (res == FR_OK)
    {
        while (elements < MAX_ELEMENT_COUNT)
        {
            res = f_readdir(&dir, &fno);                   // Read a directory item
            if (fno.fname[0] == '.') continue;             // Ignore dot entry

            if (res != FR_OK)
            {
                MSS_UART_polled_tx_string(gp_my_uart,(uint8_t *)"\n\r Error reading root directory\n\r");
                break;
            }
            else
            {
                if(fno.fname[0] == 0)
                {
                    if(elements == 0u)
                    {
                        MSS_UART_polled_tx_string(gp_my_uart,(uint8_t *)"\n\rRoot directory is empty\n\r");
                    }
                    break;
                }
                else
                {
                    uint8_t character = 0;
                    strncpy((char*)file_name[elements], fno.fname,13); //store file names and attributes for using it in File read menu.
                    file_attrib[elements] = fno.fattrib;
                    MSS_UART_polled_tx_string(gp_my_uart,(uint8_t *)"\n\r ");
                    character = elements + '0';
                    MSS_UART_polled_tx(gp_my_uart,(uint8_t *)&character, 1u);
                    MSS_UART_polled_tx_string(gp_my_uart,(uint8_t *)" -   ");
                    MSS_UART_polled_tx_string(gp_my_uart,(uint8_t *)fno.fname);
                }
            }
            elements++;
        }
    }
    return res;
}

void USB_DEV_attached(mss_usb_device_speed_t speed)
{
    if(speed == MSS_USB_DEVICE_HS)
        MSS_UART_polled_tx_string(gp_my_uart,(uint8_t *)"\n\rHS ");
    if(speed == MSS_USB_DEVICE_FS)
        MSS_UART_polled_tx_string(gp_my_uart,(uint8_t *)"\n\rFS ");
    if(speed == MSS_USB_DEVICE_LS)
        MSS_UART_polled_tx_string(  gp_my_uart,(uint8_t *)"\n\rLS ");

    MSS_UART_polled_tx_string(gp_my_uart,(uint8_t *)"Device detected\n\r");
}
void USB_DEV_dettached(void)
{
    //MSS_UART_polled_tx_string(gp_my_uart,(uint8_t *)"\n\n\rUSB Device removed\n");
}
void USB_DEV_oc_error(void)
{
    MSS_UART_polled_tx_string(gp_my_uart,(uint8_t *)"\n\rOverCurrent error");
}
void USB_DEV_enumerated(mss_usbh_tdev_info_t tdev_info)
{
    MSS_UART_polled_tx_string(gp_my_uart,(uint8_t *)"\n\rEnumeration successful");
}
void USB_DEV_class_driver_assigned(void)
{
    MSS_UART_polled_tx_string(gp_my_uart,(uint8_t *)"\n\rMSC Class driver assigned");
}
void USB_DEV_not_supported(int8_t error_code)
{
    switch(error_code)
    {
        case HUB_CLASS_NOT_SUPPORTED:
            MSS_UART_polled_tx_string(gp_my_uart,(uint8_t *)"\n\rHUB Not Supported");
            break;
        case CLASS_DRIVER_NOT_MATCHED:
            MSS_UART_polled_tx_string(gp_my_uart,(uint8_t *)"\n\rClass Not Supported");
            break;
        case CLASS_DRIVER_NOT_FREE:
            MSS_UART_polled_tx_string(gp_my_uart,(uint8_t *)"\n\rClass driver not free");
            break;
        case CONF_DESC_POWER_LIM_EXCEED:
            MSS_UART_polled_tx_string(gp_my_uart,(uint8_t *)"\n\rDevice needs more power");
            break;
        case CONF_DESC_WRONG_DESC_TYPE:
        case CONF_DESC_WRONG_LENGTH:
        case DEV_DESC_LS_MAXPKTSZ0_NOT8:
        case DEV_DESC_HS_MAXPKTSZ0_NOT64:
        case DEV_DESC_HS_USBBCD_NOT200:
        case DEV_DESC_WRONG_MAXPKTSZ0:
        case DEV_DESC_WRONG_USBBCD:
        case DEV_DESC_WRONG_DESC_TYPE:
        case DEV_DESC_WRONG_LENGTH:
            MSS_UART_polled_tx_string(gp_my_uart,(uint8_t *)"Device not supported");
            break;
        default:
        break;
    }
}
void USB_DEV_permanent_erro(void)
{

}

void MSC_DEV_valid_config(void)
{
    MSS_UART_polled_tx_string(gp_my_uart,(uint8_t *)"\n\rConfiguring MSC device...");
}

void MSC_DEV_ready(void)
{
   MSS_UART_polled_tx_string(gp_my_uart,(uint8_t *)"\n\rMSC device ready for transfers");
   g_msc_driver_ready = 1u;
}

void MSC_DEV_driver_released(void)
{
    g_msc_driver_released = 1u;
    MSS_UART_polled_tx_string(gp_my_uart,(uint8_t *)"\n\rUSBH MSC driver released\n\r");

}

void MSC_DEV_error(int8_t error_code)
{
    MSS_UART_polled_tx_string(gp_my_uart,(uint8_t *)"\n\rMSC device error");
}
