/******************************************************************************
 *
 * Filename: loader_prompt.c
 *
 * Instantiation of the interactive loader functions.
 *
 * Revision information:
 *
 * 20AUG2004	kb_admin	initial creation
 * 12JAN2005	kb_admin	massive changes for tftp, strings, and more
 * 05JUL2005	kb_admin	save tag address, and set registers on boot
 *
 * BEGIN_KBDD_BLOCK
 * No warranty, expressed or implied, is included with this software.  It is
 * provided "AS IS" and no warranty of any kind including statutory or aspects
 * relating to merchantability or fitness for any purpose is provided.  All
 * intellectual property rights of others is maintained with the respective
 * owners.  This software is not copyrighted and is intended for reference
 * only.
 * END_BLOCK
 *
 * $FreeBSD: src/sys/boot/arm/at91/libat91/loader_prompt.c,v 1.2 2006/04/21 13:56:26 imp Exp $
 *****************************************************************************/

#include "at91rm9200_lowlevel.h"
#include "p_string.h"
#include "eeprom.h"
#ifdef SUPPORT_TAG_LIST
#include "tag_list.h"
#endif
#include "emac.h"
#include "loader_prompt.h"
#include "env_vars.h"
#include "lib.h"


/******************************* GLOBALS *************************************/


/*********************** PRIVATE FUNCTIONS/DATA ******************************/

static char	inputBuffer[MAX_INPUT_SIZE];
static int	buffCount;

// argv pointer are either NULL or point to locations in inputBuffer
static char	*argv[MAX_COMMAND_PARAMS];

static char	backspaceString[] = {0x8, ' ', 0x8, 0};

static command_entry_t	CommandTable[] = {
	{COMMAND_COPY, "c"},
	{COMMAND_DUMP, "d"},
	{COMMAND_EXEC, "e"},
	{COMMAND_HELP, "?"},
	{COMMAND_LOCAL_IP, "ip"},
	{COMMAND_MAC, "m"},
	{COMMAND_SERVER_IP, "server_ip"},
	{COMMAND_SET, "s"},
#ifdef SUPPORT_TAG_LIST
	{COMMAND_TAG, "t"},
#endif
	{COMMAND_TFTP, "tftp"},
	{COMMAND_WRITE, "w"},
	{COMMAND_XMODEM, "x"},
	{COMMAND_FINAL_FLAG, 0}
};

static unsigned tagAddress;

/*
 * .KB_C_FN_DEFINITION_START
 * unsigned BuildIP(void)
 *  This private function packs the test IP info to an unsigned value.
 * .KB_C_FN_DEFINITION_END
 */
static unsigned
BuildIP(void)
{
	return ((p_ASCIIToDec(argv[1]) << 24) |
	    (p_ASCIIToDec(argv[2]) << 16) |
	    (p_ASCIIToDec(argv[3]) << 8) |
	    p_ASCIIToDec(argv[4]));
}


/*
 * .KB_C_FN_DEFINITION_START
 * int StringToCommand(char *cPtr)
 *  This private function converts a command string to a command code.
 * .KB_C_FN_DEFINITION_END
 */
static int
StringToCommand(char *cPtr)
{
	int	i;

	for (i = 0; CommandTable[i].command != COMMAND_FINAL_FLAG; ++i)
		if (!p_strcmp(CommandTable[i].c_string, cPtr))
			return (CommandTable[i].command);

	return (COMMAND_INVALID);
}


/*
 * .KB_C_FN_DEFINITION_START
 * void RestoreSpace(int)
 *  This private function restores NULL characters to spaces in order to
 * process the remaining args as a string.  The number passed is the argc
 * of the first entry to begin restoring space in the inputBuffer.
 * .KB_C_FN_DEFINITION_END
 */
static void
RestoreSpace(int startArgc)
{
	char	*cPtr;

	for (startArgc++; startArgc < MAX_COMMAND_PARAMS; startArgc++) {
		if ((cPtr = argv[startArgc]))
			*(cPtr - 1) = ' ';
	}
}


/*
 * .KB_C_FN_DEFINITION_START
 * int BreakCommand(char *)
 *  This private function splits the buffer into separate strings as pointed
 * by argv and returns the number of parameters (< 0 on failure).
 * .KB_C_FN_DEFINITION_END
 */
static int
BreakCommand(char *buffer)
{
	int	pCount, cCount, state;

	state = pCount = 0;
	p_memset((char*)argv, 0, sizeof(argv));

	for (cCount = 0; cCount < MAX_INPUT_SIZE; ++cCount) {

		if (!state) {
			/* look for next command */
			if (!p_IsWhiteSpace(buffer[cCount])) {
				argv[pCount++] = &buffer[cCount];
				state = 1;
			} else {
				buffer[cCount] = 0;
			}
		} else {
			/* in command, find next white space */
			if (p_IsWhiteSpace(buffer[cCount])) {
				buffer[cCount] = 0;
				state = 0;
			}
		}

		if (pCount >= MAX_COMMAND_PARAMS) {
			return (-1);
		}
	}

	return (pCount);
}


/*
 * .KB_C_FN_DEFINITION_START
 * void ParseCommand(char *)
 *  This private function executes matching functions.
 * .KB_C_FN_DEFINITION_END
 */
static void
ParseCommand(char *buffer)
{
	int		argc;

	if ((argc = BreakCommand(buffer)) < 1) {
		return ;
	}

	switch (StringToCommand(argv[0])) {

	case COMMAND_COPY:
	{
	// "c <to> <from> <size in bytes>"
	// copy memory

		char		*to, *from;
		unsigned	size;

		if (argc > 3) {
			to = (char *)p_ASCIIToHex(argv[1]);
			from = (char *)p_ASCIIToHex(argv[2]);
			size = p_ASCIIToHex(argv[3]);
			p_memcpy(to, from, size);
		}

	}
	break;

	case COMMAND_DUMP:
	// display boot commands
		DumpBootCommands();
	break;

	case COMMAND_EXEC:
	{
	// "e <address>"
	// execute at address

		void (*execAddr)(unsigned, unsigned, unsigned);

		if (argc > 1) {
			/* in future, include machtypes (MACH_KB9200 = 612) */
			execAddr = (void (*)(unsigned, unsigned, unsigned))
			  p_ASCIIToHex(argv[1]);
			(*execAddr)(0, 612, tagAddress);
		}

	}
	break;

	case COMMAND_TFTP:
	{
	// "tftp <local_dest_addr filename>"
	//  tftp download

		unsigned address = 0;

		if (argc > 2)
			address = p_ASCIIToHex(argv[1]);

		TFTP_Download(address, argv[2]);

	}
	break;

	case COMMAND_SERVER_IP:
	// "server_ip <server IP 192 200 1 20>"
	// set download server address

		if (argc > 4)
			SetServerIPAddress(BuildIP());
	break;

	case COMMAND_HELP:
	// dump command info
		printf("Commands:\r\n"
		"\tc\r\n"
		"\td\r\n"
		"\te\r\n"
		"\tip\r\n"
		"\tserver_ip\r\n"
		"\tm\r\n"
		"\ttftp\r\n"
		"\ts\r\n"
#ifdef SUPPORT_TAG_LIST
		"\tt\r\n"
#endif
		"\tw\r\n"
		"\tx\r\n");
	break;

	case COMMAND_LOCAL_IP:
	// "local_ip <local IP 192 200 1 21>
	// set ip of this module

		if (argc > 4)
			SetLocalIPAddress(BuildIP());
	break;

	case COMMAND_MAC:
	{
	// "m <mac address 12 34 56 78 9a bc>
	// set mac address using 6 byte values

		unsigned	low_addr, high_addr;

		if (argc > 6) {

			low_addr = (p_ASCIIToHex(argv[4]) << 24) |
			    (p_ASCIIToHex(argv[3]) << 16) |
			    (p_ASCIIToHex(argv[2]) << 8) |
			    p_ASCIIToHex(argv[1]);
			high_addr = 
			    (p_ASCIIToHex(argv[6]) << 8) |
			    p_ASCIIToHex(argv[5]);
			SetMACAddress(low_addr, high_addr);
		}

	}
	break;

	case COMMAND_SET:
	{
	// s <index> <new boot command>
	// set the boot command at index (0-based)

		unsigned	index;

		if (argc > 1) {
			RestoreSpace(2);
			index = p_ASCIIToHex(argv[1]);
			SetBootCommand(index, argv[2]);
		}

	}
	break;

#ifdef SUPPORT_TAG_LIST
	case COMMAND_TAG:
	{
	// t <address> <boot command line>
	// create tag-list for linux boot

		if (argc > 2) {
			RestoreSpace(2);
			tagAddress = p_ASCIIToHex(argv[1]);
			InitTagList(argv[2], (void*)tagAddress);
		}

	}
	break;
#endif

	case COMMAND_WRITE:
	// write the command table to non-volatile

		WriteCommandTable();
	break;

	case COMMAND_XMODEM:
	{
	// "x <address>"
	// download X-modem record at address

		char		*destAddr = 0;

		if (argc > 1) {
			destAddr = (char *)p_ASCIIToHex(argv[1]);
			xmodem_rx(destAddr);
		}
	}
	break;

	default:
		break;
	}

	printf("\r\n");
}


/*
 * .KB_C_FN_DEFINITION_START
 * void ServicePrompt(char)
 *  This private function process each character checking for valid commands.
 * This function is only executed if the character is considered valid.
 * Each command is terminated with NULL (0) or '\r'.
 * .KB_C_FN_DEFINITION_END
 */
static void ServicePrompt(char p_char) {

	if (p_char == '\r') {
		p_char = 0;

	}

	if (p_char != 0x8) {
		if (buffCount < MAX_INPUT_SIZE-1) {

			inputBuffer[buffCount] = p_char;
			++buffCount;
			putchar(p_char);
		}

	} else if (buffCount) {

		/* handle backspace BS */
		--buffCount;
		inputBuffer[buffCount] = 0;
		printf(backspaceString);
		return ;
	}

	if (!p_char) {
		printf("\r\n");
		ParseCommand(inputBuffer);
		p_memset(inputBuffer, 0, MAX_INPUT_SIZE);
		buffCount = 0;
		printf("\r\n>");
	}
}


/* ************************** GLOBAL FUNCTIONS ********************************/


/*
 * .KB_C_FN_DEFINITION_START
 * void Bootloader(void *inputFunction)
 *  This global function is the entry point for the bootloader.  If the
 * inputFunction pointer is NULL, the loader input will be serviced from
 * the uart.  Otherwise, inputFunction is called to get characters which
 * the loader will parse.
 * .KB_C_FN_DEFINITION_END
 */
void
Bootloader(int(*inputFunction)(int))
{
	int	ch = 0;

	p_memset((void*)inputBuffer, 0, sizeof(inputBuffer));

	buffCount = 0;
	if (!inputFunction) {
		inputFunction = getc;
	}

	printf("\r\n>");

	while (1)
		if ((ch = ((*inputFunction)(0))) > 0)
			ServicePrompt(ch);
}
