/*
 * David Muller; Germán Alfaro
 * davehmuller@gmail.com; alfaro.germanevera@gmail.com
 * Thom Maughan thommaughan@gmail.com
 */

#include "microsd.h"
#include "ymodem.h"
#include "uartstdio.h"	// User local version with larger RX buffer

#define EXIT -5



extern int microSD_flg;

//*****************************************************************************
//
// Defines the size of the buffers that hold the path, or temporary data from
// the SD card.  There are two buffers allocated of this size.  The buffer size
// must be large enough to hold the longest expected full path name, including
// the file name, and a trailing null character.
//
//*****************************************************************************
#define PATH_BUF_SIZE           80

//*****************************************************************************
//
// Defines the size of the buffer that holds the command line.
//
//*****************************************************************************
#define CMD_BUF_SIZE            64

//*****************************************************************************
//
// This buffer holds the full path to the current working directory.  Initially
// it is root ("/").
//
//*****************************************************************************
static char g_cCwdBuf[PATH_BUF_SIZE] = "/";

//*****************************************************************************
//
// A temporary data buffer used when manipulating file paths, or reading data
// from the SD card.
//
//*****************************************************************************
static char g_cTmpBuf[PATH_BUF_SIZE];

//*****************************************************************************
//
// The buffer that holds the command line.
//
//*****************************************************************************
static char g_cCmdBuf[CMD_BUF_SIZE];

//*****************************************************************************
//
// The following are data structures used by FatFs.
//
//*****************************************************************************
static FATFS g_sFatFs;		//work area (file system object) for logical drivers
static DIR g_sDirObject;
static FILINFO g_sFileInfo;
static FIL g_sFileObject;

//*****************************************************************************
//
// A structure that holds a mapping between an FRESULT numerical code, and a
// string representation.  FRESULT codes are returned from the FatFs FAT file
// system driver.
//
//*****************************************************************************
typedef struct
{
	FRESULT fresult;
	char *pcResultStr;
}
tFresultString;

//*****************************************************************************
//
// A macro to make it easy to add result codes to the table.
//
//*****************************************************************************
#define FRESULT_ENTRY(f)        { (f), (#f) }

//*****************************************************************************
//
// A table that holds a mapping between the numerical FRESULT code and it's
// name as a string.  This is used for looking up error codes for printing to
// the console.
//
//*****************************************************************************
tFresultString g_sFresultStrings[] =
{
		FRESULT_ENTRY(FR_OK),
		FRESULT_ENTRY(FR_NOT_READY),
		FRESULT_ENTRY(FR_NO_FILE),
		FRESULT_ENTRY(FR_NO_PATH),
		FRESULT_ENTRY(FR_INVALID_NAME),
		FRESULT_ENTRY(FR_INVALID_DRIVE),
		FRESULT_ENTRY(FR_DENIED),
		FRESULT_ENTRY(FR_EXIST),
		FRESULT_ENTRY(FR_WRITE_PROTECTED),
		FRESULT_ENTRY(FR_NOT_ENABLED),
		FRESULT_ENTRY(FR_NO_FILESYSTEM),
		FRESULT_ENTRY(FR_INVALID_OBJECT),
		FRESULT_ENTRY(FR_MKFS_ABORTED)
};

//*****************************************************************************
//
// A macro that holds the number of result codes.
//
//*****************************************************************************
#define NUM_FRESULT_CODES (sizeof(g_sFresultStrings) / sizeof(tFresultString))

/*
 * Local function prototypes
 */
int Cmd_ls(int argc, char *argv[]);
int Cmd_mkdir(int argc, char *argv[]);
int Cmd_cd(int argc, char *argv[]);
int Cmd_pwd(int argc, char *argv[]);
int Cmd_cat(int argc, char *argv[]);
int Cmd_help(int argc, char *argv[]);

//*****************************************************************************
//
// This function returns a string representation of an error code that was
// returned from a function call to FatFs.  It can be used for printing human
// readable error messages.
//
//*****************************************************************************
const char *
StringFromFresult(FRESULT fresult)
{
	unsigned int uIdx;

	//
	// Enter a loop to search the error code table for a matching error code.
	//
	for(uIdx = 0; uIdx < NUM_FRESULT_CODES; uIdx++)
	{
		//
		// If a match is found, then return the string name of the error code.
		//
		if(g_sFresultStrings[uIdx].fresult == fresult)
		{
			return(g_sFresultStrings[uIdx].pcResultStr);
		}
	}

	//
	// At this point no matching code was found, so return a string indicating
	// an unknown error.
	//
	return("UNKNOWN ERROR CODE");
}

//*****************************************************************************
//
// This function implements the "ls" command.  It opens the current directory
// and enumerates through the contents, and prints a line for each item it
// finds.  It shows details such as file attributes, time and date, and the
// file size, along with the name.  It shows a summary of file sizes at the end
// along with free space.
//
//*****************************************************************************
int
Cmd_ls(int argc, char *argv[])
{
	uint32_t ulTotalSize;
	uint32_t ulFileCount;
	uint32_t ulDirCount;
	FRESULT fresult;
	FATFS *pFatFs;

	//
	// Open the current directory for access.
	//
	fresult = f_opendir(&g_sDirObject, g_cCwdBuf);

	//
	// Check for error and return if there is a problem.
	//
	if(fresult != FR_OK)
	{
		return(fresult);
	}

	ulTotalSize = 0;
	ulFileCount = 0;
	ulDirCount = 0;

	//
	// Give an extra blank line before the listing.
	//
	uprintf("\r\n");

	//
	// Enter loop to enumerate through all directory entries.
	//
	for(;;)
	{
		//
		// Read an entry from the directory.
		//
		fresult = f_readdir(&g_sDirObject, &g_sFileInfo);

		//
		// Check for error and return if there is a problem.
		//
		if(fresult != FR_OK)
		{
			return(fresult);
		}

		//
		// If the file name is blank, then this is the end of the listing.
		//
		if(!g_sFileInfo.fname[0])
		{
			break;
		}

		//
		// If the attribue is directory, then increment the directory count.
		//
		if(g_sFileInfo.fattrib & AM_DIR)
		{
			ulDirCount++;
		}

		//
		// Otherwise, it is a file.  Increment the file count, and add in the
		// file size to the total.
		//
		else
		{
			ulFileCount++;
			ulTotalSize += g_sFileInfo.fsize;
		}

		//
		// Print the entry information on a single line with formatting to show
		// the attributes, date, time, size, and name.
		//
		uprintf("%c%c%c%c%c %u/%02u/%02u %02u:%02u %9u  %s\r\n",
				(g_sFileInfo.fattrib & AM_DIR) ? 'D' : '-',
						(g_sFileInfo.fattrib & AM_RDO) ? 'R' : '-',
								(g_sFileInfo.fattrib & AM_HID) ? 'H' : '-',
										(g_sFileInfo.fattrib & AM_SYS) ? 'S' : '-',
												(g_sFileInfo.fattrib & AM_ARC) ? 'A' : '-',
														(g_sFileInfo.fdate >> 9) + 1980,
														(g_sFileInfo.fdate >> 5) & 15,
														g_sFileInfo.fdate & 31,
														(g_sFileInfo.ftime >> 11),
														(g_sFileInfo.ftime >> 5) & 63,
														g_sFileInfo.fsize,
#if _USE_LFN
														g_sFileInfo.lfname);
#else
														g_sFileInfo.fname);
#endif
	}

	//
	// Print summary lines showing the file, dir, and size totals.
	//
	uprintf("\r\n%4u File(s)\r\n%4u Dir(s)",
			ulFileCount, ulDirCount);

	//
	// Get the free space.
	//

	fresult = f_getfree("/", (DWORD*)&ulTotalSize, &pFatFs);
	if(fresult != FR_OK)
	{
		uprintf("\r\n\r\nf_getfree error! ");
		return(fresult);
	}

	uprintf("\r\n%10uK bytes free\r\n", pFatFs->free_clust * pFatFs->csize / 2);

	return(0);
}

//*****************************************************************************
//
// This function implements the "cd" command.  It takes an argument that
// specifies the directory to make the current working directory.  Path
// separators must use a forward slash "/".  The argument to cd can be one of
// the following:
//
// * root ("/")
// * a fully specified path ("/my/path/to/mydir")
// * a single directory name that is in the current directory ("mydir")
// * parent directory ("..")
//
// It does not understand relative paths, so dont try something like this:
// ("../my/new/path")
//
// Once the new directory is specified, it attempts to open the directory to
// make sure it exists.  If the new path is opened successfully, then the
// current working directory (cwd) is changed to the new path.
//
//*****************************************************************************
int
Cmd_cd(int argc, char *argv[])
{
	unsigned int uIdx;
	FRESULT fresult;

	//
	// Copy the current working path into a temporary buffer so it can be
	// manipulated.
	//
	strcpy(g_cTmpBuf, g_cCwdBuf);

	//
	// If the first character is /, then this is a fully specified path, and it
	// should just be used as-is.
	//
	if(argv[1][0] == '/')
	{
		//
		// Make sure the new path is not bigger than the cwd buffer.
		//
		if(strlen(argv[1]) + 1 > sizeof(g_cCwdBuf))
		{
			uprintf("Resulting path name is too long\r\n");
			return(0);
		}

		//
		// If the new path name (in argv[1])  is not too long, then copy it
		// into the temporary buffer so it can be checked.
		//
		else
		{
			strncpy(g_cTmpBuf, argv[1], sizeof(g_cTmpBuf));
		}
	}

	//
	// If the argument is .. then attempt to remove the lowest level on the
	// CWD.
	//
	else if(!strcmp(argv[1], ".."))
	{
		//
		// Get the index to the last character in the current path.
		//
		uIdx = strlen(g_cTmpBuf) - 1;

		//
		// Back up from the end of the path name until a separator (/) is
		// found, or until we bump up to the start of the path.
		//
		while((g_cTmpBuf[uIdx] != '/') && (uIdx > 1))
		{
			//
			// Back up one character.
			//
			uIdx--;
		}

		//
		// Now we are either at the lowest level separator in the current path,
		// or at the beginning of the string (root).  So set the new end of
		// string here, effectively removing that last part of the path.
		//
		g_cTmpBuf[uIdx] = 0;
	}

	//
	// Otherwise this is just a normal path name from the current directory,
	// and it needs to be appended to the current path.
	//
	else
	{
		//
		// Test to make sure that when the new additional path is added on to
		// the current path, there is room in the buffer for the full new path.
		// It needs to include a new separator, and a trailing null character.
		//
		if(strlen(g_cTmpBuf) + strlen(argv[1]) + 1 + 1 > sizeof(g_cCwdBuf))
		{
			uprintf("Resulting path name is too long\r\n");
			return(0);
		}

		//
		// The new path is okay, so add the separator and then append the new
		// directory to the path.
		//
		else
		{
			//
			// If not already at the root level, then append a /
			//
			if(strcmp(g_cTmpBuf, "/"))
			{
				strcat(g_cTmpBuf, "/");
			}

			//
			// Append the new directory to the path.
			//
			strcat(g_cTmpBuf, argv[1]);
		}
	}

	//
	// At this point, a candidate new directory path is in chTmpBuf.  Try to
	// open it to make sure it is valid.
	//
	fresult = f_opendir(&g_sDirObject, g_cTmpBuf);

	//
	// If it can't be opened, then it is a bad path.  Inform the user and
	// return.
	//
	if(fresult != FR_OK)
	{
		uprintf("cd: %s\r\n", g_cTmpBuf);
		return(fresult);
	}

	//
	// Otherwise, it is a valid new path, so copy it into the CWD.
	//
	else
	{
		strncpy(g_cCwdBuf, g_cTmpBuf, sizeof(g_cCwdBuf));
	}

	//
	// Return success.
	//
	return(0);
}

//*****************************************************************************
//
// This function implements the "pwd" command.  It simply prints the current
// working directory.
//
//*****************************************************************************
int
Cmd_pwd(int argc, char *argv[])
{
	//
	// Print the CWD to the console.
	//
	uprintf("%s\r\n", g_cCwdBuf);

	//
	// Return success.
	//
	return(0);
}

//*****************************************************************************
//
// This function implements the "cat" command.  It reads the contents of a file
// and prints it to the console.  This should only be used on text files.  If
// it is used on a binary file, then a bunch of garbage is likely to printed on
// the console.
//
//*****************************************************************************
int
Cmd_cat(int argc, char *argv[])
{
	FRESULT fresult;
	unsigned short usBytesRead;

	//
	// First, check to make sure that the current path (CWD), plus the file
	// name, plus a separator and trailing null, will all fit in the temporary
	// buffer that will be used to hold the file name.  The file name must be
	// fully specified, with path, to FatFs.
	//
	if(strlen(g_cCwdBuf) + strlen(argv[1]) + 1 + 1 > sizeof(g_cTmpBuf))
	{
		uprintf("Resulting path name is too long\r\n");
		return(0);
	}

	//
	// Copy the current path to the temporary buffer so it can be manipulated.
	//
	strcpy(g_cTmpBuf, g_cCwdBuf);

	//
	// If not already at the root level, then append a separator.
	//
	if(strcmp("/", g_cCwdBuf))
	{
		strcat(g_cTmpBuf, "/");
	}

	//
	// Now finally, append the file name to result in a fully specified file.
	//
	strcat(g_cTmpBuf, argv[1]);

	//
	// Open the file for reading.
	//
	fresult = f_open(&g_sFileObject, g_cTmpBuf, FA_READ);

	//
	// If there was some problem opening the file, then return an error.
	//
	if(fresult != FR_OK)
	{
		return(fresult);
	}

	//
	// Enter a loop to repeatedly read data from the file and display it, until
	// the end of the file is reached.
	//
	do
	{
		//
		// Read a block of data from the file.  Read as much as can fit in the
		// temporary buffer, including a space for the trailing null.
		//
		fresult = f_read(&g_sFileObject, g_cTmpBuf, sizeof(g_cTmpBuf) - 1,
				(UINT*)&usBytesRead);

		//
		// If there was an error reading, then print a newline and return the
		// error to the user.
		//
		if(fresult != FR_OK)
		{
			uprintf("\r\n");
			return(fresult);
		}

		//
		// Null terminate the last block that was read to make it a null
		// terminated string that can be used with printf.
		//
		g_cTmpBuf[usBytesRead] = 0;

		//
		// Print the last chunk of the file that was received.
		//
		while( UARTTxBytesFree() <= 80 );	// Wait till there's room in console TX buffer. RCG 5/14
		uprintf("%s", g_cTmpBuf);
	}
	while(usBytesRead == sizeof(g_cTmpBuf) - 1 && !kbhit());	// Added kbhit(). RCG 5/14

	//
	// Return success.
	//
	return(0);
}

//*****************************************************************************
//
// This function implements the "help" command.  It prints a simple list of the
// available commands with a brief description.
//
//*****************************************************************************
tCmdLineEntry g_psCmdTable[];

int
Cmd_help(int argc, char *argv[])
{
	tCmdLineEntry *pEntry;
//	tCmdLineEntry *g_sCmdTable;
	//
	// Print some header text.
	//
	uprintf("\r\nAvailable commands\r\n");
	uprintf("------------------\r\n");

	//
	// Point at the beginning of the command table.
	//
	pEntry = &g_psCmdTable[0];

	//
	// Enter a loop to read each entry from the command table.  The end of the
	// table has been reached when the command name is NULL.
	//
	while(pEntry->pcCmd)
	{
		//
		// Print the command name and the brief description.
		//
		uprintf("%s%s\r\n", pEntry->pcCmd, pEntry->pcHelp);

		//
		// Advance to the next entry in the table.
		//
		pEntry++;
	}

	//
	// Return success.
	//
	return(0);
}


//*****************************************************************************
//
// This function implements the "exit" command.  Returns EXIT (-5), to indicate
// to exit the file viewer loop.
//*****************************************************************************
int
Cmd_exit(int argc, char *argv[])
{
	return(EXIT);
}


/*
 * Cmd_rm()
 * Deletes the specified file.
 * RCG 5/14
 */
int Cmd_rm(int argc, char *argv[])
{
	FRESULT fresult;

	//
	// First, check to make sure that the current path (CWD), plus the file
	// name, plus a separator and trailing null, will all fit in the temporary
	// buffer that will be used to hold the file name.  The file name must be
	// fully specified, with path, to FatFs.
	//
	if(strlen(g_cCwdBuf) + strlen(argv[1]) + 1 + 1 > sizeof(g_cTmpBuf))
	{
		uprintf("Resulting path name is too long\r\n");
		return(0);
	}

	//
	// Copy the current path to the temporary buffer so it can be manipulated.
	//
	strcpy(g_cTmpBuf, g_cCwdBuf);

	//
	// If not already at the root level, then append a separator.
	//
	if(strcmp("/", g_cCwdBuf))
	{
		strcat(g_cTmpBuf, "/");
	}

	//
	// Now finally, append the file name to result in a fully specified file.
	//
	strcat(g_cTmpBuf, argv[1]);

	fresult = f_unlink (g_cTmpBuf);
	if(fresult != FR_OK)
	{
		uprintf("\r\n");
		return(fresult);
	}


	//
	// Return success.
	//
	return(0);
}

/*
 * Cmd_mkdir()
 * Creates a new diretory.
 * RCG 5/14
 */
int Cmd_mkdir(int argc, char *argv[])
{
	FRESULT fresult;

	//
	// First, check to make sure that the current path (CWD), plus the file
	// name, plus a separator and trailing null, will all fit in the temporary
	// buffer that will be used to hold the file name.  The file name must be
	// fully specified, with path, to FatFs.
	//
	if(strlen(g_cCwdBuf) + strlen(argv[1]) + 1 + 1 > sizeof(g_cTmpBuf))
	{
		uprintf("Resulting path name is too long\r\n");
		return(0);
	}

	//
	// Copy the current path to the temporary buffer so it can be manipulated.
	//
	strcpy(g_cTmpBuf, g_cCwdBuf);

	//
	// If not already at the root level, then append a separator.
	//
	if(strcmp("/", g_cCwdBuf))
	{
		strcat(g_cTmpBuf, "/");
	}

	//
	// Now finally, append the file name to result in a fully specified file.
	//
	strcat(g_cTmpBuf, argv[1]);

	fresult = f_mkdir(g_cTmpBuf);
	if(fresult != FR_OK)
	{
		uprintf("\r\n");
		return(fresult);
	}


	//
	// Return success.
	//
	return(0);
}

/*
 * Cmd_mv()
 * Renames or moves a file or directory.
 * RCG 5/14
 */
int Cmd_mv(int argc, char *argv[])
{
	FRESULT fresult;
	char oldname[PATH_BUF_SIZE], newname[PATH_BUF_SIZE];

	//
	// First, check to make sure that the current path (CWD), plus the file
	// name, plus a separator and trailing null, will all fit in the temporary
	// buffer that will be used to hold the file name.  The file name must be
	// fully specified, with path, to FatFs.
	//
	if(strlen(g_cCwdBuf) + strlen(argv[1]) + 2 > sizeof(g_cTmpBuf) || strlen(g_cCwdBuf) + strlen(argv[2]) + 2 > sizeof(g_cTmpBuf))
	{
		uprintf("Resulting path name is too long\r\n");
		return(0);
	}

	// Copy the current path to the temporary buffer so it can be manipulated.
	strcpy(oldname, g_cCwdBuf);
	strcpy(newname, g_cCwdBuf);

	// If not already at the root level, then append a separator.
	if(strcmp("/", g_cCwdBuf))
	{
		strcat(oldname, "/");
		strcat(newname, "/");
	}

	// Now finally, append the file name to result in a fully specified file.
	strcat(oldname, argv[1]);
	strcat(newname, argv[2]);

	// Rename (or move by renaming) the file
	fresult = f_rename(oldname, newname);
	if(fresult != FR_OK)
	{
		uprintf("\r\n");
		return(fresult);
	}

	// Return success.
	return(0);
}


//*****************************************************************************
//
// This is the table that holds the command names, implementing functions, and
// brief description.
//
//*****************************************************************************
tCmdLineEntry g_psCmdTable[] =
{
		{ "help",   Cmd_help,     "  : Display list of commands" },
		{ "h",      Cmd_help,     "  : Alias for help" },
		{ "?",      Cmd_help,     "  : Alias for help" },
		{ "ls",     Cmd_ls,       "  : Display list of files" },
		{ "mkdir",  Cmd_mkdir,    "  : Make new directory"},
		{ "chdir",  Cmd_cd,       "  : Change directory" },
		{ "cd",     Cmd_cd,       "  : Alias for chdir" },
		{ "pwd",    Cmd_pwd,      "  : Display current working directory" },
		{ "cat",    Cmd_cat,      "  : Display contents of a text file" },
		{ "rm",     Cmd_rm,       "  : Remove a file or directory" },
		{ "mv",     Cmd_mv,       "  : Rename or move a file or directory" },
		{ "ys", 	ymodem_send,  "  : Send file via YMODEM" },
		{ "yr", 	ymodem_receive,  "  : Receive file via YMODEM" },
		{ "exit",   Cmd_exit,     "  : Exit the file viewer" },
		{ 0, 0, 0 }
};




/*
 * Enter a command loop to view the file system on the SD card.
 * Helpful for accessing the following commands:
 * ls (display list of files),
 * cat (show contexts of a text file).
 */
void viewFiles(void)
{
	int nStatus;

	uprintf("\r\n\r\nUnix-like command line. Type 'help' for list of commands\r\n");


	// Enter an infinite loop for reading and processing commands from the user.
	while(1)
	{
		// Print a prompt to the console.  Show the CWD.
		uprintf("\r\n%s> ", g_cCwdBuf);

		// Get a line of text from the user.
		getUserInput(g_cCmdBuf, sizeof(g_cCmdBuf));

		// Pass the line from the user to the command processor.  It will be
		// parsed and valid commands executed.
		nStatus = CmdLineProcess(g_cCmdBuf);

		// Handle the case of bad command.
		if(nStatus == CMDLINE_BAD_CMD)
		{
			uprintf("Bad command!\r\n");
		}

		// Handle the case of too many arguments.
		else if(nStatus == CMDLINE_TOO_MANY_ARGS)
		{
			uprintf("Too many arguments for command processor!\r\n");
		}

		//exit this function ("viewFiles()") on command 'exit'
		else if(nStatus == EXIT)
		{
			return;
		}

		// Otherwise the command was executed.  Print the error code if one was returned.
		else if(nStatus != 0)
		{
			uprintf("Command returned error code %s\r\n", StringFromFresult((FRESULT)nStatus));
		}
	}
}

/*
 * Append a buffer to the end of our file (sys_data.fileName)
 * on the SD card.
 * ASSUMES THAT THE PASSED IN BUFFER IS A C STRING (uses strlen to determine
 * how much of the buffer to write in).
 */
void appendBufferToSDCard(char *buffer)
{
	FIL fileObject;		// File object
	FRESULT fresult;
	WORD bw;			// Bytes written

	// Open a file
	fresult = f_open(&fileObject, sys_data.fileName, FA_READ |FA_WRITE |FA_OPEN_ALWAYS);
	ROM_SysCtlDelay(500*MILLISECOND);
	if(fresult != FR_OK)
	{
		uprintf("f_open error: %s\r\n", StringFromFresult(fresult));
	}

	// Seek to the end, to append our file
	fresult = f_lseek(&fileObject, fileObject.fsize);
	if(fresult != FR_OK)
	{
		uprintf("f_lseek error: %s\r\n", StringFromFresult(fresult));
	}

	// Write the buffer to the SD card
	fresult = f_write( &fileObject, buffer, strlen(buffer), (UINT*)&bw);
	if(fresult != FR_OK)
	{
		uprintf("f_write error: %s\r\n", StringFromFresult(fresult));
	}

	// Close the file
	fresult = f_close( &fileObject);
	if(fresult != FR_OK)
	{
		uprintf("f_close error: %s\r\n", StringFromFresult(fresult));
	}
}


/*
 * Init_SDCard()
 * Initializes SD card to use with FatFS file system
 */
void Init_SDCard(void)
{
	FRESULT fresult;
	uint32_t ulDataRx[1];

#if BOARD_MFET >= 1 || BOARD_MPHOX >= 1 || BOARD_NANOFET >=1  || BOARD_MSC == 1
	ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_SSI0);
	ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);

	// Configure the pin muxing
	ROM_GPIOPinConfigure(GPIO_PA2_SSI0CLK);
	ROM_GPIOPinConfigure(GPIO_PA3_SSI0FSS);
	ROM_GPIOPinConfigure(GPIO_PA4_SSI0RX);
	ROM_GPIOPinConfigure(GPIO_PA5_SSI0TX);

	// Configure the GPIO settings for the SSI pins.
	ROM_GPIOPinTypeSSI(GPIO_PORTA_BASE, GPIO_PIN_2 | GPIO_PIN_3 | GPIO_PIN_4 | GPIO_PIN_5);

	// Configure and enable the SSI port for SPI master mode.
	//MOTO_MODE_0 is best for SDCards.
	ROM_SSIConfigSetExpClk(SSI0_BASE, ROM_SysCtlClockGet(), SSI_FRF_MOTO_MODE_0,
			SSI_MODE_MASTER, 1000000, 8);
	ROM_SSIEnable(SSI0_BASE);

	// Flush receive FIFO before starting to talk to SDCard.
	while(ROM_SSIDataGetNonBlocking(SSI0_BASE, &ulDataRx[0]))
	{
		;
	}
#endif




	// Mount the file system, using logical disk 0.
	//
	fresult = f_mount(&g_sFatFs, "", 0);
	if(fresult != FR_OK)
	{
		uprintf("f_mount error: %s\r\n", StringFromFresult(fresult));
	}
}

/*
 * sd_fprintf()
 * Prints text to SD card file with floating point support.
 * 255 characters maximum.
 * Returns number of characters written if less than 255 (lookup vsnprintf())
 * RCG 5/14
 */
int sd_fprintf(FIL *fil, const char *format, ...)
{
   va_list args;
   char buff[256];
   UINT bw;
   FRESULT fresult;

   va_start (args, format);

   // Print formatted string to buffer
   vsnprintf(buff, 256, format, args);

	// Write the buffer to the SD card
	fresult = f_write( fil, buff, strlen(buff), &bw );
	if(fresult != FR_OK)
	{
		uprintf("f_write error: %s\r\n", StringFromFresult(fresult));
	}

	va_end (args);


   return bw;
}

#if BOARD_NANOFET == 1
// Added by Thom Maughan to support Winbond W25N01GVZEIG  SPI Serial Flash
//
void sd_format(void)
{


    FATFS fs;           /* Filesystem object */
    FIL fil;            /* File object */
    FRESULT res;        /* API result code */
    UINT bw;            /* Bytes written */
    BYTE work[1024]; /* Work area (larger is better for processing time) */ //Valid values are 512, 1024, 2048 and 4096


    /* Format the default drive with default parameters */
    res = f_mkfs ("", 0, 0);
    //res = f_mkfs("", 0, work, sizeof work);
    if (res) {  }

    /* Give a work area to the default drive */
    f_mount(&fs, "", 0);

    /* Create a file as new */
    res = f_open(&fil, "howdy.txt", FA_CREATE_NEW | FA_WRITE);
    if (res) {  }

    /* Write a message */
    f_write(&fil, "Hello from Thom!\r\n", 18, &bw);
    if (bw != 18)  {  }

    /* Close the file */
    f_close(&fil);

    /* Unregister work area */
    f_mount(0, "", 0);
}
#endif
