/****************************************************************************/
/*	Copyright (c) 2009, Bendor Research Pty. Ltd. All rights reserved.		*/
/*	This software is released under the GNU General Public License, version	*/
/*	3, with the exemptions that 1) the header files installed by this 	 	*/
/*	package can be included in closed-source code and 2) the libraries		*/
/*	built by this package can be statically linked into a closed-source 	*/
/*	binary. See the file LICENSE for the details.							*/
/*	This software is provided "AS IS", without any warranty or guarantee.	*/
/*	By using this software you acknowledge that neither Bendor Research nor	*/
/*	any of its personnel or affiliates can be held liable for any damages,	*/
/*	including, but not limited to, loss of business, loss of money, loss of */
/*	life, loss of reputation, arising out of this software being faulty or	*/
/*	not performing as advertised, even if Bendor Research was aware of such	*/
/*	faults in the software. You use this software at your own risk, any and */
/*	all liabilities that arise out of your using this software are strictly	*/
/*	yours.																	*/
/****************************************************************************/

#include "lpctcl.h"

/****************************************************************************/
/*							Local prototypes								*/
/****************************************************************************/

static	void	Lpctcl_Destroy( ClientData data );
static	int		Lpctcl_Lpc( ClientData data, Tcl_Interp *tcl,
					   		int objc, Tcl_Obj * const objv[] );

/****************************************************************************/
/*							Local data										*/
/****************************************************************************/

/*
*	Table of the commands
*/

static const CMDTABLE	topcmd[] = {

	{ "port",		Lpctcl_SerialPort		},
	{ "close",		Lpctcl_SerialClose		},
	{ "baud",		Lpctcl_SerialBaud		},
	{ "modem",		Lpctcl_SerialModem		},
	{ "manual",		Lpctcl_SerialManual		},
	{ "progress",	Lpctcl_TargetProgress	},
	{ "power",		Lpctcl_TargetPower		},
	{ "reset",		Lpctcl_TargetReset		},
	{ "recv",		Lpctcl_TargetRecv		},
	{ "send",		Lpctcl_TargetSend		},
	{ "connect",	Lpctcl_TargetConnect	},
	{ "chiptype",	Lpctcl_TargetType		},
	{ "crystal",	Lpctcl_TargetXtal		},
	{ "flash",		Lpctcl_TargetFlash		},
	{ "format",		Lpctcl_FileType			},
	{ "load",		Lpctcl_FileLoad			},
	{ "save",		Lpctcl_FileSave			},
	{ "burn",		Lpctcl_FileBurn			},
	{ "vector",		Lpctcl_FileVect			},
	{ "invalidate",	Lpctcl_FileInvalid		},
	{ "logproc",	Lpctcl_LogProc			},
	{ "logon",		Lpctcl_LogOn			},
	{ "logoff",		Lpctcl_LogOff			},
	{ "errormsg",	Lpctcl_LogError			},
	{ NULL,			NULL					}
};

/****************************************************************************/
/*							Library entry									*/
/****************************************************************************/

/*
*	Initialise the library
*	~~~~~~~~~~~~~~~~~~~~~~
*	This function is called by the TCL system when the user loads the library.
*	It initialises the library and loads the low level LPC library in turn.
*/

int		Lpctcl_Init( Tcl_Interp *tcl )
{
LPCTCL	*lib;

	// First, try to allocate the structure we need

	if ( ! ( lib = malloc( sizeof( LPCTCL ) ) ) ) {

		// Can't get the memory

		Tcl_AppendResult( tcl, "Out of memory", NULL );
		return( TCL_ERROR );
	}

	// Open the low level library

	if ( ! ( lib->lpc = LPC_Open() ) ) {

		Tcl_AppendResult( tcl, "Could not initialise the LPC library", NULL );
		free( lib );
		return( TCL_ERROR );
	}

	// Now register the top-level command (lpc)

	if ( ! Tcl_CreateObjCommand( tcl, "lpcprg", Lpctcl_Lpc,
								 (ClientData) lib, Lpctcl_Destroy ) ) {

		// No need to set the result, it's already set by the command

		LPC_Close( lib->lpc );
		free( lib );
		return( TCL_ERROR );
	}

	// Init the state

	lib->tcl = tcl;

	lib->ser_slow = 9600;
	lib->ser_fast = 9600;
	lib->ser_user = NULL;
	lib->log_user = NULL;
	lib->log_proc = NULL;
	lib->tgt_user = NULL;
	lib->tgt_proc = NULL;
	lib->tgt_pwrf = NULL;
	lib->hex_type = "srec";

	memset( lib->tgt_data, 0xff, sizeof( lib->tgt_data ) );
	memset( lib->tgt_flag, STAT_VOID, sizeof( lib->tgt_flag ) );
	return( TCL_OK );
}

/****************************************************************************/
/*					Library-wise utility functions							*/
/****************************************************************************/

/*
*	This function runs a command by name
*	~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*	If the objv[0] string can be found in the command table, then the
*	associated function will be called (with objc and objv adjusted).
*	Otherwise a TCL error message is set.
*/

int		Lpctcl_Command( const CMDTABLE *table, LPCTCL *lib, Tcl_Interp *tcl,
					   	int objc, Tcl_Obj * const objv[] )
{
char	*cmd;

	if ( objc < 2 ) {

		Tcl_AppendResult( tcl, "Not enough arguments", NULL );
		return( TCL_ERROR );
	}

	cmd = Tcl_GetStringFromObj( objv[ 1 ], NULL );

	while ( table->name ) {

		if ( ! strcmp( table->name, cmd ) )

			return( (*table->func)( lib, tcl, objc - 1, &objv[ 1 ] ) );
		else
			table++;
	}

	Tcl_AppendResult( tcl, "lpctcl: ",
					  Tcl_GetStringFromObj( objv[ 0 ], NULL ),
					  " has no subcommand ",
					  Tcl_GetStringFromObj( objv[ 1 ], NULL ),
					  NULL );
	return( TCL_ERROR );
}

/*
*	Set the interpreter response in case of an LPC library error
*	~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/

void	Lpctcl_Liberror( Tcl_Interp *tcl, int code )
{
	switch ( code ) {

		case LPC_ERROR_CONFIG:

			Tcl_AppendResult( tcl, "Invalid configuration", NULL );
			break;

		case LPC_ERROR_SYSTEM:

			Tcl_AppendResult( tcl, "A system call failed", NULL );
			break;

		case LPC_ERROR_CHIP:

			Tcl_AppendResult( tcl, "Unknown target chip", NULL );
			break;

		case LPC_ERROR_COMMS:

			Tcl_AppendResult( tcl, "Target communication failure", NULL );
			break;

		case LPC_ERROR_PARAM:

			Tcl_AppendResult( tcl, "Invalid parameter", NULL );
			break;

		case LPC_ERROR_PROTECT:

			Tcl_AppendResult( tcl, "Target is read-protected", NULL );
			break;

		case LPC_ERROR_SECTOR:

			Tcl_AppendResult( tcl, "Target: illegal sector", NULL );
			break;

		case LPC_ERROR_BUSY:

			Tcl_AppendResult( tcl, "Target: FLASH is busy", NULL );
			break;

		case LPC_ERROR_FAIL:

			Tcl_AppendResult( tcl, "Target: test failed", NULL );
			break;

		case LPC_ERROR_COMMAND:

			Tcl_AppendResult( tcl, "Target: illegal command", NULL );
			break;

		case LPC_ERROR_LOCKED:

			Tcl_AppendResult( tcl, "Target: sector locked", NULL );
			break;

		case LPC_ERROR_FLASH:

			Tcl_AppendResult( tcl, "Target: FLASH verify failed", NULL );
			break;

		case LPC_ERROR_ADDRESS:

			Tcl_AppendResult( tcl, "Target: illegal address", NULL );
			break;

		case LPC_ERROR_UNKNOWN:

			Tcl_AppendResult( tcl, "Illegal target response code", NULL );
			break;

		case LPC_ERROR_FILE:

			Tcl_AppendResult( tcl, "Hexfile format error", NULL );
			break;

		case LPC_ERROR_FILETYPE:

			Tcl_AppendResult( tcl, "Illegal hexfile type", NULL );
			break;

		case LPC_ERROR_CBACK:

			// The result is already set!
			break;

		default:

			Tcl_AppendResult( tcl, "Unknown library response", NULL );
			break;
	}
}

/****************************************************************************/
/*							Local functions									*/
/****************************************************************************/

/*
*	Close the library
*	~~~~~~~~~~~~~~~~~
*	This function is called by the TCL subsystem when the user wants to
*	unload the library. The 'data' parameter is a pointer to the state
*	structure, allocated by Lpctcl_Init(), cast to ClientData.
*/

static	void	Lpctcl_Destroy( ClientData data )
{
LPCTCL	*lib = (void *) data;

	LPC_Close( lib->lpc );
	if ( lib->ser_user ) free( lib->ser_user );
	free( lib );
}

/*
*	This is the top level command
*	~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*	The TCL subsystem calls this function whenever it encounters a command
*	starting with 'lpcprg'. The 'data' parameter is a pointer to the state
*	structure allocated by the Lpctcl_Init() function. The 'tcl' parameter
*	is the pointer to the calling interpreter, objc is the number of arguments
*	the 'lpcprg' itself is in there, so the smallest number is 1) and 'objv'
*	is an array of TCL objects containing the arguments (objv[0] contains the
*	"lpcprg" string).
*
*	The function simply invokes the internal command dispatch using the top
*	level command table.
*/

static	int		Lpctcl_Lpc( ClientData data, Tcl_Interp *tcl,
					   		int objc, Tcl_Obj * const objv[] )
{
LPCTCL	*lib = (void *) data;

	return( Lpctcl_Command( topcmd, lib, tcl, objc, objv ) );
	return( TCL_OK );
}


