/* ------------------------------------------------------------------------------------------------ */
/* FILE :		Demo_9_STM3220G_IAR.c																*/
/*																									*/
/* CONTENTS :																						*/
/*				Demo for the ST microelectronics STM3220G evaluation board							*/
/*				Tiny file system shell for the micro-SD card on the STM3220G board.					*/
/*																									*/
/*				NOTE:																				*/
/*				Because the UART shares the same pins as the SD/MMC card cage, it is not			*/
/*				possible to use the UART.  So, the demo needs to operate through semi-hosting.		*/
/*				The terminal I/O is used as it is the input/output for semi-hosting.				*/
/*				In the Debug environment, pull down the "View: tab and select "Terminal I/O".		*/
/*				Keep the default settings of the terminal I/O.										*/
/*																									*/
/*																									*/
/* Copyright (c) 2013, Code-Time Technologies Inc. All rights reserved.								*/
/*																									*/
/* Code-Time Technologies retains all right, title, and interest in and to this work				*/
/*																									*/
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS							*/
/* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF										*/
/* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL							*/
/* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR								*/
/* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,							*/
/* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR							*/
/* OTHER DEALINGS IN THE SOFTWARE.																	*/
/*																									*/
/*																									*/
/*	$Revision: 1.1 $																				*/
/*	$Date: 2014/04/24 20:01:33 $																	*/
/*																									*/
/* ------------------------------------------------------------------------------------------------ */

#if ((OS_DEMO) != 9)
	#error "Wrong OS_DEMO value" 
#endif

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>

/* ------------------------------------------------ */

#include "stm32f2xx.h"
#include "stm322xg_eval.h"
#include "ff.h"
#include "diskio.h"

/* ------------------------------------------------ */

#define MAX_FS		1								/* # of volumes that can be mounted				*/
#define SYS_FREQ	120000000						/* Processor clock frequency					*/
#define VERBOSE		1								/* != 0 print more information					*/

#if ((VERBOSE) == 0)
  #define PRT_ERROR(x) puts(x)
#else
  #define PRT_ERROR(x) puts(x)
#endif

/* ------------------------------------------------ */

DIR   g_Dir;
FATFS g_FileSys;
char  g_DirNowPath[512];
USART_InitTypeDef USART_InitStructure;

typedef struct {
	char *Name;
	int (* FctPtr)(int argc, char *argv[]);
} Cmd_t;

static char g_Buffer[4096];							/* Buffer used for all files I/O				*/

/* ------------------------------------------------ */

extern void SD_ProcessDMAIRQ(void);
extern void SD_ProcessIRQSrc(void);

void CleanRead(char *Str);
int GetKey(void);

int cat(int argc, char *argv[]);
int cd(int argc, char *argv[]);
int chmod(int argc, char *argv[]);
int cp(int argc, char *argv[]);
int du(int argc, char *argv[]);
int fmt(int argc, char *argv[]);
int help(int argc, char *argv[]);
int ls(int argc, char *argv[]);
int mkdir(int argc, char *argv[]);
int mnt(int argc, char *argv[]);
int mv(int argc, char *argv[]);
int perf(int argc, char *argv[]);
int pwd(int argc, char *argv[]);
int rm(int argc, char *argv[]);
int rmdir(int argc, char *argv[]);
int umnt(int argc, char *argv[]);
Cmd_t CommandLst[] = {
	{ "cat",	&cat	},
	{ "cd",		&cd		},
	{ "chmod",	&chmod	},
	{ "cp",		&cp		},
	{ "fmt",	&fmt	},
	{ "du",		&du		},
	{ "help",   &help	},
	{ "ls",		&ls		},
	{ "mkdir",	&mkdir	},
	{ "mnt",	&mnt	},
	{ "mv",		&mv		},
	{ "perf",	&perf	},
	{ "pwd",	&pwd	},
	{ "rm",		&rm		},
	{ "rmdir",	&rmdir	},
	{ "umnt",	&umnt	},
	{ "?",      &help	}
};

/* ------------------------------------------------ */
main(int argc, char *argv[]) 
{
int  Arg_C;											/* Number of tokens on the command line			*/
int  ii;											/* General purpose								*/
int  IsStr1;										/* Toggle to decode quoted ' strings			*/
int  IsStr2;										/* Toggle to decode quoted " strings			*/
int  jj;											/* General purpose								*/
char PrevC;											/* Previous character during lexical analysis	*/
int  Prio = 0;										/* Priority of the timer tick interrupt			*/
RTC_InitTypeDef RTCsetup;
int *TimerBase;										/* Systick timer registers base address			*/

static char  CmdLine[256];							/* Command line typed by the user				*/
static char *Arg_V[10];								/* Individual tokens from the command line		*/

/* ------------------------------------------------ */
/* Processor clock set-up							*/
/* Is called at start-up in ASM file				*/
//	SystemInit();

/* ------------------------------------------------ */
/* 	Start the RTOS									*/

	OSstart();

/* ------------------------------------------------ */
/* SysTick Set-up									*/

	TimerBase = (int *)0xE000E000;
	((volatile int *)0xE000Ed20)[0] = (((volatile int *)0xE000Ed20)[0] & 0x1FFFFFFF)
	                                |   ((Prio & 7) << 29);
	TimerBase[5] = (((SYS_FREQ/100000) * OS_TIMER_US)/10)-1;
	TimerBase[4] = 0x7;								/* internal clock / int & enable				*/

/* ------------------------------------------------ */
/* RTC set-up										*/

	RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);
	PWR_BackupAccessCmd(ENABLE);
  #if ((RTC_USE_XTAL) == 0)
	RCC_LSICmd(ENABLE);
	while(RCC_GetFlagStatus(RCC_FLAG_LSIRDY) == RESET);
	RCC_RTCCLKConfig(RCC_RTCCLKSource_LSI);
	RCC_RTCCLKCmd(ENABLE);
	RTC_WaitForSynchro();
	RTCsetup.RTC_AsynchPrediv = 127;
	RTCsetup.RTC_SynchPrediv  = 249;
  #elif defined (RTC_CLOCK_SOURCE_LSE)
	RCC_LSEConfig(RCC_LSE_ON);
	while(RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET);
	RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);
	RCC_RTCCLKCmd(ENABLE);
	RTC_WaitForSynchro();
	RTCsetup.RTC_AsynchPrediv = 127;
	RTCsetup.RTC_SynchPrediv  = 255;
  #endif
	RTCsetup.RTC_HourFormat   = RTC_HourFormat_24;
	RTC_Init(&RTCsetup);
	RTC_AlarmCmd(RTC_Alarm_A, DISABLE);

/* ------------------------------------------------ */
/* Attach the interrupt handlers					*/

	OSisrInstall(-1, TIMtick);						/* Install the OS timer tick function			*/
	OSisrInstall(49, SD_ProcessIRQSrc);				/* Install the micro SD interrupt				*/
	OSisrInstall(59, SD_ProcessDMAIRQ);				/* Install the microp SD DMA interrupt			*/

	OSeint(1);

/* ------------------------------------------------ */
/* Processing loop									*/

	setvbuf(stdin,  NULL, _IONBF, 0);				/* By default, IAR library flush the I/O		*/
	setvbuf(stdout, NULL, _IONBF, 0);				/* buffer when full or when new-line			*/

	g_DirNowPath[0] = '\0';

	puts("\nTiny Shell for FatFS\n");				/* Welcome banner								*/
	puts("Available commands\n\n");

	help(100, NULL);

	for (;;) {										/* Infinite loop								*/
		if (FR_OK == f_getcwd(&g_DirNowPath[0], sizeof(g_DirNowPath))) {
			printf("%s > ", g_DirNowPath);			/* Prompt										*/
		}
		else {
			printf("> ");
		}
		gets(&CmdLine[0]);							/* Get the command from the user				*/

		CleanRead(&CmdLine[0]);						/* Replace tab by space and handle backspaces	*/

		ii     = 0;
		IsStr1 = 0;
		IsStr2 = 0;
		jj     = 0;
		PrevC  = ' ';
		while (CmdLine[ii] != '\0') {				/* Multi-space elimination						*/
			if (CmdLine[ii] == '\'') {				/* Toggle beginning / end of string				*/
				IsStr1 = !IsStr1;
			}
			if (CmdLine[ii] == '\"') {				/* Toggle beginning / end of string				*/
				IsStr2 = !IsStr2;
			}

			while ((PrevC == ' ')
			&&     (CmdLine[ii] == ' ')
			&&     (IsStr1 == 0)					/* Strings are left untouched					*/
			&&     (IsStr2 == 0)) {
				ii++;
			}
			PrevC = CmdLine[ii];
			CmdLine[jj++] = CmdLine[ii++];
		}
		CmdLine[jj] = '\0';

		Arg_C  = 0;									/* Split the command line into tokens			*/
		ii     = 0;
		IsStr1 = 0;
		IsStr2 = 0;
		PrevC  = '\0';
		while (CmdLine[ii] != '\0') {
			if (CmdLine[ii] == '\'') {				/* Properly handle strings enclosed with '		*/
				if (IsStr1 == 0) {
					Arg_V[Arg_C++] = &CmdLine[++ii];
				}
				else {
					CmdLine[ii++] = '\0';
				}
				IsStr1 = !IsStr1;
			}
			else if (CmdLine[ii] == '\"') {			/* Properly handle strings enclosed with "		*/
				if (IsStr2 == 0) {
					Arg_V[Arg_C++] = &CmdLine[++ii];
				}
				else {
					CmdLine[ii++] = '\0';
				}
				IsStr2 = !IsStr2;
			}
			if ((IsStr1 == 0)						/* Not in as string and is a white space		*/
			&&  (IsStr2 == 0)
			&&  (CmdLine[ii] == ' ')) {
				CmdLine[ii] = '\0';					/* This is the end of the token					*/
			}
					
			if ((PrevC == '\0')						/* Start of a new token							*/
			&&  (IsStr1 == 0)
			&&  (IsStr2 == 0)) {
				Arg_V[Arg_C++] = &CmdLine[ii];
			}

			PrevC = CmdLine[ii];
			ii++;
		}

		if (IsStr1 != 0) {							/* Check for incomplete strings					*/
			puts("ERROR: Missing end quote \'");
			Arg_C = 0;
		}
		if (IsStr2 != 0) {
			puts("ERROR: Missing end quote \"");
			Arg_C = 0;
		}

		if (Arg_C != 0) {							/* It is not an empty line						*/
			for (ii=0 ; ii<(sizeof(CommandLst)/sizeof(CommandLst[0])); ii++) {
				if (0 == strcmp(CommandLst[ii].Name, Arg_V[0])) {
					printf("\n");
					CommandLst[ii].FctPtr(Arg_C, Arg_V);
					break;
				}
			}
			if (ii == (sizeof(CommandLst)/sizeof(CommandLst[0]))) {
				printf("ERROR: unknown command \"%s\"\n", Arg_V[0]); 
			}
		}
	}
}


/* ------------------------------------------------------------------------------------------------ */

void IdleTask(void)
{
	for(;;) {
		 __ASM(" wfi");
	}
}

/* ------------------------------------------------------------------------------------------------ */

void TIMcallBack(void)
{
	return;
}

/* ------------------------------------------------------------------------------------------------ */

void CleanRead(char *Str)
{
int  ii;											/* Read index									*/
int  jj;											/* Write index									*/

	ii = 0;
	jj = 0;
	while(Str[ii] != '\0') {						/* Process the whole string						*/
		if (Str[ii] == '\b') {						/* This is a backspace							*/
			if ((ii != 0)							/* Not the first char in the string, then go	*/
			&&  (jj != 0)) {						/* back 1 character in the destination			*/
				jj--;
			}
			ii++;									/* Skip the back space							*/
		}
		else {										/* Is not a back space							*/
			if (Str[ii] == '\t') {					/* Replace TABS by white space					*/
				Str[ii] = ' ';
			}
			Str[jj++] = Str[ii++];					/* Copy the src to the destination				*/
		}
	}
	Str[jj] = '\0';									/* Terminate the string							*/

	return;
}

/* ------------------------------------------------------------------------------------------------ */

int cat(int argc, char *argv[])
{
char   *Fname;										/* Name of the file to cat						*/
FIL     FileDsc;									/* File descriptor								*/
int     ii;											/* General purpose								*/
int     IsEOF;										/* If eon of file is reached					*/
int     IsWrite;									/* If writing to the file						*/
UINT    Nrd;										/* Number of bytes read							*/
UINT    Nwrt;										/* Number of bytes written						*/
int     RetVal;										/* Return value									*/


	RetVal = 0;										/* Assume everything is OK						*/
	if (argc < 0) {									/* Special value to print usage					*/
		puts("cat   : Redirect a file to stdout or redirect stdin to a file");
		return(0);
	}

	if (argc < 2) {									/* Needs 1 or 2 options on the command line		*/
		RetVal = 1;
	}

	IsWrite = 0;
	if (RetVal == 0) {
		if (argv[1][0] == '>') {					/* Is a write to a file							*/
			IsWrite = 1;
			if (argv[1][1] != '\0') {				/* If no space between '>' & file name			*/
				Fname = &argv[1][1];				/* The file name starts at the 2nd character	*/
				if (argc != 2) {					/* Can only have 2 tokens then					*/
					RetVal = 1;
				}
			}
			else {									/* Space between '>' & file name				*/
				Fname = argv[2];					/* The file name is the 3rd token				*/
				if (argc != 3) {					/* Can only have 3 tokens then					*/
					RetVal = 1;
				}
			}
		}
		else if (argv[1][0] == '<'){				/* In case using > for write					*/
			if (argv[1][1] != '\0') {				/* If no space between '>' & file name			*/
				Fname = &argv[1][1];				/* The file name starts at the 2nd character	*/
				if (argc != 2) {					/* Can only have 2 tokens then					*/
					RetVal = 1;
				}
			}
			else {									/* Space between '>' & file name				*/
				Fname = argv[2];					/* The file name is the 3rd token				*/
				if (argc != 3) {					/* Can only have 3 tokens then					*/
					RetVal = 1;
				}
			}
		}
		else {
			Fname = &argv[1][0];
			if (argc != 2) {
				RetVal = 1;
			}
		}
	}

	if (RetVal != 0) {								/* Print usage in case of error					*/
		puts("usage: cat  source_file           Output the file contents on the screen");
		puts("       cat <target_file           Output the file contents on the screen");
		puts("       cat >target_file           Write from terminal to the file");
		puts("                                  Terminate with EOF<CR>");
		RetVal = 1;
	}

	if (RetVal == 0) {
		if (IsWrite == 0) {							/* Reading a file								*/
			if (FR_OK != f_open(&FileDsc, Fname, FA_READ)) {
				printf("ERROR: cannot open file %s\n", Fname);
				RetVal = 1;
			}
			else {
				do {								/* Dump the file contents to stdio until EOF	*/
					if (FR_OK != f_read(&FileDsc, &g_Buffer[0], sizeof(g_Buffer), &Nrd)) {
						PRT_ERROR("ERROR: problems reading the file");
						RetVal = 1;
						Nrd    = 0;
					}
					for (ii=0 ; ii<Nrd ; ii++) {
						putchar(g_Buffer[ii]);
					}
				} while (Nrd > 0);
				f_close(&FileDsc);
			}
		}
		else {										/* Writing to a file							*/
			if (FR_OK == f_stat(Fname, NULL)) {		/* If the file to write exist, delete it first	*/
				if (FR_OK != f_unlink(Fname)) {
					printf("ERROR: cannot overwrite the file %s\n", Fname);
					RetVal = 1;
				}
			}
			if (RetVal == 0) {
				if  (FR_OK != f_open(&FileDsc, Fname, FA_CREATE_ALWAYS|FA_WRITE)) {
					printf("ERROR: cannot open file %s\n", Fname);
					RetVal = 1;
				}
				else {
					IsEOF = 0;
					do {							/* Write to it until EOF from terminal			*/
						fgets(&g_Buffer[0], sizeof(g_Buffer), stdin);
						Nwrt = strlen(&g_Buffer[0]);
						for (ii=0 ; ii<Nwrt ; ii++) {
							if (g_Buffer[ii] == 0xFF) {
								Nwrt  = ii;
								IsEOF = 1;
								break;
							}
						}

						if (FR_OK != f_write(&FileDsc, &g_Buffer[0], Nwrt, &Nrd)) {
							PRT_ERROR("ERROR: problems writing the file");
							RetVal = 1;
							Nrd    = 0;
						}
						else {
							if (Nrd != Nwrt) {
								PRT_ERROR("ERROR: problems writing the file");
								RetVal = 1;
								IsEOF  = 1;
							}
						}
					} while (IsEOF == 0);
					f_close(&FileDsc);
				}
			}
		}
	}

	return(RetVal);
}

/* ------------------------------------------------------------------------------------------------ */

int cd(int argc, char *argv[])
{
int   RetVal;										/* Return value									*/


	RetVal = 0;										/* Assume everything is OK						*/
	if (argc < 0) {									/* Special value to print usage					*/
		puts("cd    : Change directory");
		return(0);
	}

	if (argc != 2) {								/* Need a directory name or ..					*/
		RetVal = 1;
	}

	if (RetVal != 0) {								/* Print usage in case of error					*/
		puts("usage: cd dir_name                Change directory to dir_name or up with dir_name=..");
		RetVal = 1;
	}

	if (RetVal == 0) {
		if (FR_OK != f_chdir(argv[1])) {			/* Use FatFS directly							*/
			PRT_ERROR("ERROR: cannot change directory");
			RetVal = 1;
		}
	}

	return(RetVal);
}

/* ------------------------------------------------------------------------------------------------ */

int chmod(int argc, char *argv[])
{
BYTE ReadOnly;										/* Setting read-only or read-write				*/
int  RetVal;										/* Return value									*/

	RetVal = 0;										/* Assume everything is OK						*/
	if (argc < 0) {									/* Special value to print usage					*/
		puts("chmod : Change a file / directory access modes");
		return(0);
	}

	if (argc != 3) {								/* Need a mode + directory name or a file name	*/
		RetVal = 1;
	}

	ReadOnly = (BYTE)0;								/* Assume is chmod +w							*/
	if (0 == strcmp(argv[1], "-w")) {
		ReadOnly = AM_RDO;
	}
	else if (0 != strcmp(argv[1], "+w")) {
		RetVal = 1;
	}

	if (RetVal != 0) {								/* Print usage in case of error					*/
		puts("usage: chmod -w file_name        Change a file / directory to read-only");
		puts("       chmod +w file_name        Change a file / directory to read-write");
		RetVal = 1;
	}

	if (RetVal == 0) {
		if (FR_OK != f_chmod(argv[2], ReadOnly, AM_RDO)) {
			PRT_ERROR("ERROR: cannot chmod the file");
			RetVal = 1;
		}
	}

	return(RetVal);
}

/* ------------------------------------------------------------------------------------------------ */

int cp(int argc, char *argv[])
{
FIL   FileDscDst;									/* Destination file descriptor					*/
FIL   FileDscSrc;									/* Source file descriptor						*/
UINT  Nrd;											/* Number of bytes read							*/
UINT  Nwrt;											/* Number of bytes written						*/
int   RetVal;										/* Return value									*/

	RetVal = 0;										/* Assume everything is OK						*/
	if (argc < 0) {									/* Special value to print usage					*/
		puts("cp    : Copy a file");
		return(0);
	}

	if (argc != 3) {								/* Need a source and destination file			*/
		RetVal = 1;
	}

	if (RetVal != 0) {								/* Print usage in case of error					*/
		puts("usage: cp src_file dst_file       Copy the file src_file to dst_file");
		RetVal = 1;
	}

	if (RetVal == 0) {
		if (FR_OK != f_open(&FileDscSrc, argv[1], FA_READ)) {
			PRT_ERROR("ERROR: cannot open src file");
			RetVal = 1;
		}
	}

	if (RetVal == 0) {
		if (FR_OK == f_stat(argv[2], NULL)) {
			if (FR_OK != f_unlink(argv[2])) {
				PRT_ERROR("ERROR: cannot overwrite dst file");
				RetVal = 1;
				f_close(&FileDscSrc);
			}
		}
	}

	if (RetVal == 0) {
		if  (FR_OK != f_open(&FileDscDst, argv[2], FA_CREATE_ALWAYS|FA_WRITE)) {
			PRT_ERROR("ERROR: cannot open dst file");
			RetVal = 1;
			f_close(&FileDscSrc);
		}
	}

	if (RetVal == 0) {

		do {
			if (FR_OK != f_read(&FileDscSrc, &g_Buffer[0], sizeof(g_Buffer), &Nrd)) {
				PRT_ERROR("ERROR: problems reading the file");
				RetVal = 1;
				Nrd    = 0;
				Nwrt   = 0;
			}
			if (FR_OK != f_write(&FileDscDst, &g_Buffer[0], Nrd, &Nwrt)) {
				PRT_ERROR("ERROR: problems writing the file");
				RetVal = 1;
				Nrd    = 0;
			}
			if (Nrd != Nwrt) {
				PRT_ERROR("ERROR: problems writing the file");
				Nrd = 0;
			}
		} while (Nrd > 0);
		f_close(&FileDscDst);
		f_close(&FileDscSrc);
	}

	return(RetVal);
}

/* ------------------------------------------------------------------------------------------------ */

int du(int argc, char *argv[])
{
DWORD    Bsize;										/* SD card block size							*/
DWORD    Capacity;									/* SD card number of sectors					*/
uint64_t Free;										/* Free space on the disk						*/
FATFS   *Fsys;										/* Volume to dump the stats						*/
int      ii;										/* General purpose								*/
DWORD    Ncluster;									/* Number of clusters on the disk				*/
char     Path[16];									/* Path of the disk								*/
int      RetVal;									/* Return value									*/
uint64_t Size;										/* Size of the disk								*/
WORD     Ssize;										/* SD card number of bytes per sector			*/


	RetVal = 0;										/* Assume everything is OK						*/
	if (argc < 0) {									/* Special value to print usage					*/
		puts("du    : Show disk usage");
		return(0);
	}

	if (argc != 1) {								/* Does not need an argument					*/
		RetVal = 1;
	}

	if (RetVal != 0) {								/* Print usage in case of error					*/
		puts("usage: du                         Show the disk usage");
		RetVal = 1;
	}

	if (RetVal == 0) {
		ii = 0;
		while((g_DirNowPath[ii] != '\0')
		&&    (g_DirNowPath[ii] != '/')) {
			Path[ii] = g_DirNowPath[ii];
			ii++;
		}
		Path[++ii] = '\0';

		if (FR_OK != f_getfree(Path, &Ncluster, &Fsys)) {
			PRT_ERROR("ERROR: problems retrieving the information");
			RetVal = 1;
		}

		if (RetVal == 0) {
			Free  = (uint64_t)Ncluster;
			Size  = (uint64_t)Fsys->n_fatent-2;		/* Number of FAT entries						*/
			Free *= Fsys->csize;					/* Time # sectors per clusters					*/
			Size *= Fsys->csize;
		  #if ((_MAX_SS) == 512)
			Free *= 512;							/* Time # bytes per sectors						*/
			Size *= 512;							/* Time # bytes per sectors						*/
		  #else
			Free *= Fsys->ssize;					/* Time # bytes per sectors						*/
			Size *= Fsys->ssize;					/* Time # bytes per sectors						*/
		  #endif

			printf("Disk size:%12llu bytes\n", Size);
			printf("Disk free:%12llu bytes\n", Free);
			printf("Disk used:%12llu bytes\n", Size-Free);

			disk_ioctl(0, GET_SECTOR_COUNT, &Capacity);
			disk_ioctl(0, GET_SECTOR_SIZE,  &Ssize);
			printf("SD size:  %12llu bytes\n", ((uint64_t)Capacity) * Ssize);
			disk_ioctl(0, GET_BLOCK_SIZE,  &Bsize);
			printf("SD blocks:%12lu bytes\n", 512*Bsize);
		}
	}

	return(RetVal);
}

/* ------------------------------------------------------------------------------------------------ */

int fmt(int argc, char *argv[])
{
int RetVal;											/* Return value									*/


	RetVal = 0;										/* Assume everything is OK						*/
	if (argc < 0) {									/* Special value to print usage					*/
		puts("fmt   : Format a drive");
		return(0);
	}

	if (argc != 2) {								/* Need a volume name to format					*/
		RetVal = 1;
	}

	if (RetVal != 0) {								/* Print usage in case of error					*/
		puts("usage: fmt device                 Format the device");
		RetVal = 1;
	}

	if (RetVal == 0) {
	  #if ((VERBOSE) > 0) 
		puts("Formatting started");
	  #endif
		if (FR_OK != f_mkfs(argv[1], 0, 0)) {		/* Let f_mkfs decide the au size				*/
			PRT_ERROR("ERROR: format of the drive failed");
			RetVal = 1;
		}
	}

  #if ((VERBOSE) > 0) 
	if (RetVal == 0) {
		puts("Done");
	}
  #endif

	return(RetVal);
}

/* ------------------------------------------------------------------------------------------------ */

int help(int argc, char *argv[])
{
int ii;												/* General purpose								*/
int RetVal;											/* Return value									*/


	RetVal = 0;										/* Assume everything is OK						*/
	if (argc < 0) {									/* Special value to print usage					*/
		puts("help  : Help");
		return(0);
	}

	if (argc == 1) {								/* Print the help for all commands				*/
		puts("usage: help                       Show help for all commands");
		puts("       help cmd ...               Show help for specified commands");
		putchar('\n');
	}
	if ((argc == 100)
	||  (argc ==   1)) {
		for (ii=0 ; ii<(sizeof(CommandLst)/sizeof(CommandLst[0])) ; ii++) {
			(void)CommandLst[ii].FctPtr(-1, NULL);
		}
		return(0);
	}

	if (argc == 2) {								/* Print the help for the specified command		*/
		for (ii=0 ; ii<(sizeof(CommandLst)/sizeof(CommandLst[0])) ; ii++) {
			if (0 == strcmp(argv[1], CommandLst[ii].Name)) {
				(void)CommandLst[ii].FctPtr(-1, NULL);
				return(0);
			}
		}
		RetVal = 1;
	}

	if (RetVal != 0) {								/* Print usage in case of error					*/
		puts("usage: help                       Show help for all commands");
		puts("       help cmd ...               Show help for specified commands");
		RetVal = 1;
	}

	return(RetVal);
}

/* ------------------------------------------------------------------------------------------------ */

int ls(int argc, char *argv[])
{
FILINFO FileInfo;
int RetVal;											/* Return value									*/
#if _USE_LFN
 char Fname[(_MAX_LFN)+1];
	FileInfo.lfname = &Fname[0];
	FileInfo.lfsize = sizeof(Fname);
#endif


	RetVal = 0;										/* Assume everything is OK						*/
	if (argc < 0) {									/* Special value to print usage					*/
		puts("ls    : List the current directory contents");
		return(0);
	}

	if (argc != 1) {								/* No argument accepted							*/
		RetVal = 1;
	}

	if (RetVal != 0) {								/* Print usage in case of error					*/
		puts("usage: ls                         List the directory contents");
		RetVal = 1;
	}

	if (RetVal == 0) {								/* refresh the current directory path			*/
		if (FR_OK != f_getcwd(&g_DirNowPath[0], sizeof(g_DirNowPath))) {
			RetVal = 1;
		}
	}

	if (RetVal == 0) {
		f_opendir(&g_Dir, &g_DirNowPath[0]);		/* Open the directory							*/

		while((FR_OK == f_readdir(&g_Dir, &FileInfo))	/* Scan all directory entries				*/
		&&    (FileInfo.fname[0] != '\0')) {
			putchar((FileInfo.fattrib & AM_DIR) ? 'd' : ' ');
			putchar('r');
			putchar((FileInfo.fattrib & AM_RDO) ? '-' : 'w');
			putchar('x');
			printf(" (%04d.%02d.%02d %02d:%02d:%02d) ", (((int)FileInfo.fdate>>9) &0x7F)+1980,
			                                            (((int)FileInfo.fdate>>5) &0x0F),
			                                            (((int)FileInfo.fdate)    &0x1F),
			                                            (((int)FileInfo.ftime>>11)&0x1F),
			                                            (((int)FileInfo.ftime>> 5)&0x3F),
			                                            (((int)FileInfo.ftime)    &0x1F)*2);
			printf(" %10lu ", FileInfo.fsize);
		  #if _USE_LFN
			if (FileInfo.lfname[0] != '\0') {
				puts(FileInfo.lfname);
			}
			else {
				puts(&FileInfo.fname[0]);
			}
		  #else
			puts(&FileInfo.fname[0]);
		  #endif
		}
	}
	return(RetVal);
}

/* ------------------------------------------------------------------------------------------------ */

int mkdir(int argc, char *argv[])
{
int RetVal;											/* Return value									*/


	RetVal = 0;										/* Assume everything is OK						*/
	if (argc < 0) {									/* Special value to print usage					*/
		puts("mkdir : Make a new directory");
		return(0);
	}

	if (argc != 2) {								/* Need the name of the directory to create		*/
		RetVal = 1;
	}

	if (RetVal != 0) {								/* Print usage in case of error					*/
		puts("usage: mkdir dir_name             Make a new directory with the name dir_name");
		RetVal = 1;
	}

	if (RetVal == 0) {
		if (FR_OK != f_mkdir(argv[1])) {			/* Direct use of FatFS function					*/
			PRT_ERROR("ERROR: cannot create directory");
			RetVal = 1;
		}
	}
	return(RetVal);
}

/* ------------------------------------------------------------------------------------------------ */

int mnt(int argc, char *argv[])
{
int RetVal;											/* Return value									*/
int VolNmb;

	RetVal = 0;										/* Assume everything is OK						*/
	if (argc < 0) {									/* Special value to print usage					*/
		puts("mnt   : Mount a drive");
		return(0);
	}

	if (argc != 2) {								/* Need he volume and the mount point			*/
		RetVal = 1;
	}

	if (RetVal != 0) {								/* Print usage in case of error					*/
		puts("usage: mnt device                 Mount a file system device number");
		RetVal = 1;
	}

	if (RetVal == 0) {
		VolNmb = strtol(argv[1], NULL, 10);
		if ((VolNmb < 0)
		||  (VolNmb >= MAX_FS)) {
		  #if ((MAX_FS) == 1)
			PRT_ERROR("ERROR: volume number must be 0 (only one file system available)");
		  #else
			printf("ERROR: volume number must be between 0 and %d\n", MAX_FS);
		  #endif
			RetVal = 1;
		}
	}

	if (RetVal == 0) {
		if (FR_OK != f_mount(&g_FileSys, argv[1], 1)) {
			printf("ERROR: cannot mount volume %s\n", argv[1]);
			RetVal = 1;
		}
	}

	if (RetVal == 0) {
		if (FR_OK != f_opendir(&g_Dir, "/")) {
			printf("ERROR: cannot mount volume %s\n", argv[1]);
			RetVal = 1;
		}
	}

  #if ((VERBOSE) > 0)
	if (RetVal == 0) {
		puts("The volume is now mounted");
	}
  #endif

	return(RetVal);
}

/* ------------------------------------------------------------------------------------------------ */

int mv(int argc, char *argv[])
{
int  DoCP;											/* If crossing directory, need to copy & delete	*/
int  RetVal;										/* Return value									*/


	RetVal = 0;										/* Assume everything is OK						*/
	if (argc < 0) {									/* Special value to print usage					*/
		puts("mv    : Move / rename a file");
		return(0);
	}

	if (argc != 3) {								/* Need a source and destination name			*/
		RetVal = 1;
	}

	if (RetVal != 0) {								/* Print usage in case of error					*/
		puts("usage: mv file_name new_name      Rename file_name to new_name");
		RetVal = 1;
	}

	if (RetVal == 0) {
	DoCP = (NULL != strchr(argv[1], '/'))
	     | (NULL != strchr(argv[2], '/'));
		if (DoCP == 0) {
			if (FR_OK != f_rename(argv[1], argv[2])) {
				RetVal = 1;
			}
		}
		else {
			RetVal = cp(argc, argv);
			if (RetVal == 0) {
				if (FR_OK != f_unlink(argv[1])) {
					RetVal = 1;
				}
			}
		}
		if (RetVal != 0) {
			PRT_ERROR("ERROR: renaming the file");
			RetVal = 1;
		}
	}

	return(RetVal);
}

/* ------------------------------------------------------------------------------------------------ */

int pwd(int argc, char *argv[])
{
int RetVal;											/* Return value									*/


	RetVal = 0;										/* Assume everything is OK						*/
	if (argc < 0) {									/* Special value to print usage					*/
		puts("pwd   : Show current directory");
		return(0);
	}

	if (argc != 1) {								/* Does not accept arguments					*/
		RetVal = 1;
	}

	if (RetVal != 0) {								/* Print usage in case of error					*/
		puts("usage: pwd                        Print the current working directory");
		RetVal = 1;
	}

	if (RetVal == 0) {
		if (FR_OK != f_getcwd(&g_DirNowPath[0], sizeof(g_DirNowPath))) {
			PRT_ERROR("ERROR: cannot get directory information");
			RetVal = 1;
		}
	}

	if (RetVal == 0) {
		printf("Current directory: %s\n", &g_DirNowPath[0]);
	}

	return(RetVal);
}

/* ------------------------------------------------------------------------------------------------ */

int perf(int argc, char *argv[])
{
UINT   BlkSize;
char  *Buffer;
char  *Cptr;
FIL    FileDsc;										/* File descriptor								*/
UINT   Left;
UINT   Nrd;
UINT   Nwrt;
int    RetVal;										/* Return value									*/
UINT   Size;
int    StartTime;
double Time;
static const char Fname[] = "__PERF__";
FILINFO FileInfo;


	RetVal = 0;										/* Assume everything is OK						*/
	if (argc < 0) {									/* Special value to print usage					*/
		puts("perf  : Throughput measurements");
		return(0);
	}

	if (argc != 3) {								/* Need the size of the transfers				*/
		RetVal = 1;
	}

	if (RetVal != 0) {								/* Print usage in case of error					*/
		puts("usage: perf                       Measure the read and write transfer rates");
		puts("       perf Nbytes BlkSize        Nbytes : file size to write then read");
		puts("                                  BlkSize: block size to use");
		RetVal = 1;
	}

	if (RetVal == 0) {
		Size    = (UINT)strtoul(argv[1], &Cptr, 10);
		if ((*Cptr=='k')
		||  (*Cptr=='K')) {
			Size *= 1024;
		}
		if ((*Cptr=='m')
		||  (*Cptr=='M')) {
			Size *= 1024*1024;
		}
		BlkSize = (UINT)strtoul(argv[2], &Cptr, 10);
		if ((*Cptr=='k')
		||  (*Cptr=='K')) {
			BlkSize *= 1024;
		}
		if ((*Cptr=='m')
		||  (*Cptr=='M')) {
			BlkSize *= 1024*1024;
		}
		Buffer  = &g_Buffer[0];
		if (BlkSize > sizeof(g_Buffer)) {
			Buffer = malloc(BlkSize);
			if (Buffer == (char *)NULL) {
				puts("ERROR: cannot allocate memory");
				RetVal = 1;
			}

		}
		TSKsleep(2);								/* Do this to make sure the test always same	*/
		if ((RetVal == 0)
		&& (FR_OK == f_stat(Fname, NULL))) {		/* If the file to write exist, delete it first	*/
			if (FR_OK != f_unlink(Fname)) {
				printf("ERROR: cannot overwrite the file %s\n", Fname);
				RetVal = 1;
			}
		}
		if (RetVal == 0) {
			printf("%d bytes file using R/W block size of %d bytes\n", Size, BlkSize);
			if  (FR_OK != f_open(&FileDsc, Fname, FA_CREATE_ALWAYS|FA_WRITE)) {
				printf("ERROR: cannot open file %s\n", Fname);
				RetVal = 1;
			}
			else {
				memset(&Buffer[0], 0x55, BlkSize);
				Left      = Size;
				StartTime = G_OStimCnt;
				do {
					Nwrt = Left;
					if (Nwrt > BlkSize) {
						Nwrt = BlkSize;
					}
					Left -= Nwrt;
					if (FR_OK != f_write(&FileDsc, &Buffer[0], Nwrt, &Nrd)) {
						PRT_ERROR("ERROR: problems writing the file");
						RetVal = 1;
						Left   = 0U;
					}
					else {
						if (Nrd != Nwrt) {
							PRT_ERROR("ERROR: problems writing the file");
							RetVal = 1;
							Left   = 0U;;
						}
					}
				} while (Left != 0U);
				StartTime = G_OStimCnt
				          - StartTime;

				f_close(&FileDsc);

				Time = ((double)StartTime)/(1000000.0/OS_TIMER_US);
				if (RetVal == 0) {
					printf("[%7.3lfs] Write rate %9.3lf kB/s\n",
					       Time, ((double)Size/1000.0)/Time);
				}
			}
			if ((RetVal == 0)
			&&  (FR_OK != f_open(&FileDsc, Fname, FA_READ))) {
				printf("ERROR: cannot open file %s\n", Fname);
				RetVal = 1;
			}
			else {
				Left      = Size;
				StartTime = G_OStimCnt;
				do {
					Nrd = Left;
					if (Nrd > BlkSize) {
						Nrd = BlkSize;
					}
					Left -= Nrd;
					if (FR_OK != f_read(&FileDsc, &Buffer[0], BlkSize, &Nrd)) {
						PRT_ERROR("ERROR: problems reading the file");
						RetVal = 1;
						Left   = 0U;
					}
				} while (Left != 0U);
				StartTime = G_OStimCnt
				          - StartTime;

				f_close(&FileDsc);

				Time = ((double)StartTime)/(1000000.0/OS_TIMER_US);
				if (RetVal == 0) {
					printf("[%7.3lfs] Read rate  %9.3lf kB/s\n",
					       Time, ((double)Size/1000.0)/Time);
				}
				f_close(&FileDsc);
			}
		}
		if ((BlkSize > sizeof(g_Buffer))
		&&  (Buffer != (char *)NULL)) {
			free(Buffer);
		}
	}

	if (RetVal == 0) {
		memset(&FileInfo, 0, sizeof(FileInfo));		/* Must zero otherwise garbage pointer			*/
		if (FR_OK != f_stat(Fname, &FileInfo)) {	/* doc: Pointer to the blank FILINFO structure	*/
			PRT_ERROR("ERROR: cannot stat the file");
			RetVal = 1;			
		}
	}

	if (RetVal == 0) {
		if (FR_OK != f_unlink(Fname)) {
			PRT_ERROR("ERROR: cannot remove the file");
			RetVal = 1;
		}
	}


	return(RetVal);
}

/* ------------------------------------------------------------------------------------------------ */

int rm(int argc, char *argv[])
{
FILINFO FileInfo;
int RetVal;											/* Return value									*/

	RetVal = 0;										/* Assume everything is OK						*/
	if (argc < 0) {									/* Special value to print usage					*/
		puts("rm    : Remove / delete a file");
		return(0);
	}

	if (argc != 2) {								/* Need the file name to delete					*/
		RetVal = 1;
	}

	if (RetVal != 0) {								/* Print usage in case of error					*/
		puts("usage: rm file_name               Delete the file file_name");
		RetVal = 1;
	}

	if (RetVal == 0) {
		memset(&FileInfo, 0, sizeof(FileInfo));		/* Must zero otherwise garbage pointer			*/
		if (FR_OK != f_stat(argv[1], &FileInfo)) {	/* doc: Pointer to the blank FILINFO structure"	*/
			PRT_ERROR("ERROR: cannot stat the file");
			RetVal = 1;			
		}
	}

	if (RetVal == 0) {
		if (FileInfo.fattrib & AM_DIR) {
			PRT_ERROR("ERROR: this is a directory, use rmdir");
			RetVal = 1;
		}
	}

	if (RetVal == 0) {
		if (FileInfo.fattrib & AM_RDO) {
			PRT_ERROR("ERROR: the file is read-only");
			RetVal = 1;
		}
	}

	if (RetVal == 0) {
		if (FR_OK != f_unlink(argv[1])) {
			PRT_ERROR("ERROR: cannot remove the file");
			RetVal = 1;
		}
	}

	return(RetVal);
}

/* ------------------------------------------------------------------------------------------------ */

int rmdir(int argc, char *argv[])
{
FILINFO FileInfo;
int RetVal;											/* Return value									*/


	RetVal = 0;										/* Assume everything is OK						*/
	if (argc < 0) {									/* Special value to print usage					*/
		puts("rmdir : Remove / delete a directory");
		return(0);
	}

	if (argc != 2) {								/* Need the directory name to delete	   		*/
		RetVal = 1;
	}

	if (RetVal != 0) {								/* Print usage in case of error					*/
		puts("usage: rmdir dir_name             Delete the directory dirname");
		RetVal = 1;
	}

	if (RetVal == 0) {
		memset(&FileInfo, 0, sizeof(FileInfo));		/* Must zero otherwise garbage pointer			*/
		if (FR_OK != f_stat(argv[1], &FileInfo)) {	/* doc: Pointer to the blank FILINFO structure"	*/
			PRT_ERROR("ERROR: cannot stat the file");
			RetVal = 1;			
		}
	}

	if (RetVal == 0) {
		if (!(FileInfo.fattrib & AM_DIR)) {
			PRT_ERROR("ERROR: this is not a directory, use rm");
			RetVal = 1;
		}
	}

	if (RetVal == 0) {
		if (FileInfo.fattrib & AM_RDO) {
			PRT_ERROR("ERROR: the directory is read-only");
			RetVal = 1;
		}
	}

	if (RetVal == 0) {
		if (FR_OK != f_unlink(argv[1])) {
			PRT_ERROR("ERROR: cannot remove the directory");
			RetVal = 1;
		}
	}

	return(RetVal);
}

/* ------------------------------------------------------------------------------------------------ */

int umnt(int argc, char *argv[])
{
int RetVal;											/* Return value									*/
int VolNmb;

	RetVal = 0;										/* Assume everything is OK						*/
	if (argc < 0) {									/* Special value to print usage					*/
		puts("umnt  : Unmount a drive");
		return(0);
	}

	if (argc != 2) {								/* Need he volume and the mount point			*/
		RetVal = 1;
	}

	if (RetVal != 0) {								/* Print usage in case of error					*/
		puts("usage: umnt device                Unmount a file system device number");
		RetVal = 1;
	}

	VolNmb = strtol(argv[1], NULL, 10);
	if ((VolNmb < 0)
	||  (VolNmb >= MAX_FS)) {
	  #if ((MAX_FS) == 1)
		PRT_ERROR("ERROR: volume number must be 0 (only one file system available)");
	  #else
		printf("ERROR: volume number must be between 0 and %d\n", (MAX_FS)-1);
	  #endif
		RetVal = 1;
	}

	if (RetVal == 0) {
		if (FR_OK != f_mount(NULL, argv[1], 0)) {
			printf("ERROR: cannot unmount volume %s\n", argv[1]);
			RetVal = 1;
		}
	}

  #if ((VERBOSE) > 0)
	if (RetVal == 0) {
		puts("The volume is now unmounted");
	}
  #endif

	return(RetVal);
}

/* EOF */
