/*
 * my_usb.c
 *
 *  Created on: gg/mmm/ccaa
 *      Author: cognome.nome
 */

//==============================================================================
// Include files

#include "string.h"
#include "stdbool.h"
#include "stdint.h"
#include "stdlib.h"

#include "inc/hw_gpio.h"
#include "inc/hw_memmap.h"
#include "inc/hw_types.h"
#include "inc/hw_ints.h"
#include "inc/hw_flash.h"

#include "driverlib/gpio.h"
#include "driverlib/pin_map.h"
#include "driverlib/rom.h"
#include "driverlib/sysctl.h"

#include "drivers/pinout.h"

#include "driverlib/interrupt.h"
#include "driverlib/rom_map.h"
#include "driverlib/sysctl.h"
#include "driverlib/flash.h"
#include "driverlib/udma.h"

#include "usblib/usblib.h"
#include "usblib/host/usbhost.h"
#include "usblib/host/usbhmsc.h"

#include "third_party/fatfs/src/ff.h"
#include "third_party/fatfs/src/diskio.h"

#include "drivers/my_uart.h"
#include "drivers/my_USB.h"

extern uint16_t value_ip[4];
extern unsigned long g_ui32SysClock;
extern uint32_t g_ui32SysTickCount;
extern unsigned long ui32PLLRate;

//==============================================================================
// Constants

#define TICKS_PER_SECOND 100
#define MS_PER_SYSTICK (1000 / TICKS_PER_SECOND)
#define USBMSC_DRIVE_RETRY      4
#define HCD_MEMORY_SIZE         128
#define FLAGS_DEVICE_PRESENT    0x00000001
#define NUM_CLASS_DRIVERS       (sizeof(g_ppHostClassDrivers) / sizeof(g_ppHostClassDrivers[0]))
#define MAX_DIR_COUNT 16

// Declare the USB Events driver interface.
DECLARE_EVENT_DRIVER(g_sUSBEventDriver, 0, 0, USBHCDEvents);

//==============================================================================
// Types

// stato del device USB
typedef enum
{
	// No device is present.
	STATE_NO_DEVICE,
	// Mass storage device is being enumerated.
	STATE_DEVICE_ENUM,
	// Mass storage device is ready.
	STATE_DEVICE_READY,
	// An unsupported device has been attached.
	STATE_UNKNOWN_DEVICE,
	// A mass storage device was connected but failed to ever report ready.
	STATE_TIMEOUT_DEVICE,
	// A power fault has occurred.
	STATE_POWER_FAULT,
	// can't open FS
	STATE_DEVICE_WRONG_FS
} tState;

// The global that holds all of the host drivers of the MSC class.
static tUSBHostClassDriver const * const g_ppHostClassDrivers[] =
{
    &g_sUSBHostMSCClassDriver,
    &g_sUSBEventDriver
};

//==============================================================================
// Static global variables

static uint32_t g_ui32Flags = 0;
static volatile tState g_eState;

// The following are data structures used by FatFs.
//static DIR DirObject;
//static FILINFO FileInfo;
//static FIL FileObject;
//struct dir lss[MAX_DIR_COUNT];
static FATFS g_sFatFs;
//static DIR g_sDirObject;
static char pcTmpBuf[256] = "\0";
//static char g_pcCwdBuf[20] = "/";

// The following are data structures used by USB.
static uint8_t g_pHCDPool[HCD_MEMORY_SIZE];
//static uint32_t ui32DriveTimeout;
//static tUSBMode g_eCurrentUSBMode;
static tUSBHMSCInstance *g_psMSCInstance = 0;

// This global holds the number of class drivers in the g_ppHostClassDrivers list.
static const uint32_t g_ui32NumHostClassDrivers = sizeof(g_ppHostClassDrivers) / sizeof(tUSBHostClassDriver *);

// The control table used by the uDMA controller.  This table must be aligned
// to a 1024 byte boundary.  In this application uDMA is only used for USB,
// so only the first 6 channels are needed.
#pragma DATA_ALIGN(g_sDMAControlTable, 1024)
static tDMAControlTable g_sDMAControlTable[6];

//==============================================================================
// Static functions

// Funzione di libreria: chiamata ogni volta che inserisco/tolgo la chiavetta USB
static void MSCCallback(tUSBHMSCInstance *ps32Instance, uint32_t ui32Event, void *pvData)
{
// Determine the event.
    switch(ui32Event)
    {
        case MSC_EVENT_OPEN:

            // Proceed to the enumeration state.
            g_eState = STATE_DEVICE_ENUM;
        break;
        case MSC_EVENT_CLOSE:
            g_eState = STATE_NO_DEVICE;
            // Re-initialize the file system.
         break;
        default:
        break;
    }
}

// apre sulla chiavetta un file ip.txt che contiene l'ip da assegnare alla scheda (FAT FS) http://elm-chan.org/fsw/ff/00index_e.html
static int16_t USB_ip_load(void)
{
	FRESULT ifResult;
	static FIL FileObject;
	ifResult= f_open(&FileObject,"/ip.txt", FA_READ);
   	if (ifResult != FR_OK)
   	{
    	  		return (ifResult);
    }
   f_gets(pcTmpBuf, sizeof(pcTmpBuf),&FileObject);
   f_close(&FileObject);
   ip_conv(pcTmpBuf, value_ip);
   return (ifResult);
}



//==============================================================================
// Global variables

uint8_t ip_loaded = 0;  // flag utilizzato per la demo: 0 ip da caricare,  l ip da settare, 2 ip OK

//==============================================================================
// Global functions

// converte una stringa con il valore ip ###.###.###.### in 4 interi
int ip_conv(char* stringa, uint16_t* valore_ip)
{
    uint8_t i,p,n;
    char aux[5];
    i=0;
    p=0;
    n=0;
    while (stringa[i]) // scansiono la stringa
    {
        if(stringa[i]>0x2F && stringa[i]<0x3A)
        {
            aux[p]=stringa[i];
            p++;
        }
        if(stringa[i]=='.')
        {
            aux[p]=0;
            valore_ip[n]=atoi(aux);
            n++;
            p=0;
        }
        i++;
    }
    aux[p]=0;
    if(aux[0])  valore_ip[n]=atoi(aux);// l'ultimo numero non termina con la virgola
    for(i=0;i<3;i++)
    {
        if (valore_ip[i]> 0xFF)return(-1);
    }
    return(0);
}

// Funzione di libreria
void USBHCDEvents(void *pvData)
{
    tEventInfo *pEventInfo;
    // Cast this pointer to its actual type.
    pEventInfo = (tEventInfo *)pvData;

    switch(pEventInfo->ui32Event)
    {
        // An unknown device has been connected.
        case USB_EVENT_UNKNOWN_CONNECTED:
            // An unknown device was detected.
            g_eState = STATE_UNKNOWN_DEVICE;
        break;
        // The unknown device has been been unplugged.
        case USB_EVENT_DISCONNECTED:
            // Unknown device has been removed.
            g_eState = STATE_NO_DEVICE;
        break;
        // A bus power fault was detected.
        case USB_EVENT_POWER_FAULT:
            // No power means no device is present.
            g_eState = STATE_POWER_FAULT;
            break;
        default:
        break;
    }
}

// funzioni di gestione USB da mettere nel main loop
void USB_gest(void)
{

	FRESULT iFResult;
    uint32_t ui32DriveTimeout;

    // Initialize the drive timeout.
    ui32DriveTimeout = USBMSC_DRIVE_RETRY;

    // gli stati della USB. Qui si decide cosa fare con l USB
    switch(g_eState)
    {
    	case STATE_DEVICE_ENUM:
    		  if(USBHMSCDriveReady(g_psMSCInstance) != 0)
    		  {
					SysCtlDelay(SysCtlClockGet()/(3*2));  // ritardo di 500mS
					ui32DriveTimeout--; // conto i tentativi prima di dichiarare il timeout
					if(!ui32DriveTimeout) g_eState = STATE_TIMEOUT_DEVICE;
    		  }
    		  //A livello drive il dispositivo è pronto quindi monto il filesystem
    		  iFResult = f_mount(&g_sFatFs, "", 0);
    		  if (iFResult != FR_OK)
    		  {//
    			  g_eState=STATE_DEVICE_WRONG_FS;
    		  }

    		  //g_pcCwdBuf[0] = '/';
    		  //g_pcCwdBuf[1] = 0;
    		  g_eState = STATE_DEVICE_READY;
    	break;
    	case STATE_NO_DEVICE:

    		 ip_loaded=0;   // tutte le volte che inserico la chiavetta cambio l'IP
    	     if(g_ui32Flags == FLAGS_DEVICE_PRESENT)
    	     {
    	          g_ui32Flags &= ~FLAGS_DEVICE_PRESENT;
    	          break;
    	      }
    	break;
    	case STATE_DEVICE_READY:
		 // Il dispositivo è pronto qui vanno messe le cose da fare
            if(!ip_loaded) // Carico l'ip una volta sola
            {
            	if(!USB_ip_load())   ip_loaded=1; // adesso posso fare il setting dell IP
            }
       	break;
        case STATE_UNKNOWN_DEVICE:
        break;
        case STATE_TIMEOUT_DEVICE:
        break;
        case STATE_POWER_FAULT:
        break;
        }

    	USBHCDMain();  //funzione pprincipale dell USB library
}

void MyUSBInit()
{

     HWREG(GPIO_PORTD_BASE + GPIO_O_LOCK) = GPIO_LOCK_KEY;
     HWREG(GPIO_PORTD_BASE + GPIO_O_CR) = 0xff;
     ROM_GPIOPinConfigure(GPIO_PD6_USB0EPEN);
     ROM_GPIOPinTypeUSBAnalog(GPIO_PORTB_BASE, GPIO_PIN_0 | GPIO_PIN_1);
     //ROM_GPIOPinTypeUSBDigital(GPIO_PORTD_BASE, GPIO_PIN_6);
 	 ROM_GPIOPinTypeUSBAnalog(GPIO_PORTL_BASE, GPIO_PIN_6 | GPIO_PIN_7);
	 //ROM_GPIOPinTypeGPIOInput(GPIO_PORTQ_BASE, GPIO_PIN_4);

 	 // abilita il DMA
	 ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_UDMA);
     ROM_uDMAEnable();
	 ROM_uDMAControlBaseSet(g_sDMAControlTable);

	 // abilita USB
 	 USBStackModeSet(0, eUSBModeHost, 0);
 	 USBHCDRegisterDrivers(0, g_ppHostClassDrivers, g_ui32NumHostClassDrivers);
 	 g_psMSCInstance = USBHMSCDriveOpen(0, MSCCallback);
 	 //ui32DriveTimeout = USBMSC_DRIVE_RETRY;
 	 SysCtlVCOGet(SYSCTL_XTAL_25MHZ,(uint32_t *) &ui32PLLRate);
 	 USBHCDFeatureSet(0, USBLIB_FEATURE_CPUCLK, &g_ui32SysClock);
 	 USBHCDFeatureSet(0, USBLIB_FEATURE_USBPLL, &ui32PLLRate);
 	 SysCtlDelay(g_ui32SysClock/100);
     USBHCDInit(0, g_pHCDPool, HCD_MEMORY_SIZE);
}
