/****************************************************************************/
/*	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 <string.h>
#include <malloc.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <math.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/stat.h>
#include <sys/time.h>
#include "lpc-internals.h"

/****************************************************************************/
/*						Local definitions									*/
/****************************************************************************/

#define	RCOSC			14748000	// Default chip frequency
#define	LPC_CODE_BASE	0x300		// Base address for helper functions
#define	LPC_XFER_BUFF	0x400		// Base address for a data buffer

/*
*	Target error codes
*/

#define	LPC_TEC_OK			0		// OK
#define	LPC_TEC_INV_CMD		1		// Invalid command
#define	LPC_TEC_SRC_ADDR	2		// Source address not on boundary
#define	LPC_TEC_DST_ADDR	3		// Destination address not on boundary
#define	LPC_TEC_SRC_MAP		4		// Source address not mapped
#define	LPC_TEC_DST_MAP		5		// Destination address not mapped
#define	LPC_TEC_INV_CNT		6		// Invalid byte count
#define	LPC_TEC_INV_SECT	7		// Invalid sector number
#define	LPC_TEC_NOT_BLANK	8		// Sector not blank
#define	LPC_TEC_NOT_PREP	9		// Sector not prepared
#define	LPC_TEC_CMP_DIFF	10		// Comparison mismatch
#define	LPC_TEC_BUSY		11		// FALSH is busy
#define	LPC_TEC_PARAM		12		// Parameter error
#define	LPC_TEC_ADDR_ERR	13		// Address alignment error
#define	LPC_TEC_ADDR_MAP	14		// Unmapped address
#define	LPC_TEC_CMD_LOCK	15		// Command locked
#define	LPC_TEC_INV_LOCK	16		// Invalid unlock code
#define	LPC_TEC_INV_BAUD	17		// Invalid baud rate
#define	LPC_TEC_INV_STOP	18		// Invalid stop bit
#define	LPC_TEC_RD_PROT		19		// Code read protection enabled
#define	LPC_TEC_NUM_ERR		20		// Number of error codes

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

static int		_lpc_TargetContact( LPC lpc, int clock );
static int		_lpc_TargetResult( LPC lpc, int timo );
static int		_lpc_TargetErrorMap( int x );
static int		_lpc_TargetFirst64( LPC lpc );
static int		_lpc_TargetFirstSect( LPC lpc );
static int		_lpc_TargetPrepare( LPC lpc, int beg, int end );
static int		_lpc_TargetUnlock( LPC lpc );

/****************************************************************************/
/*					Constant data											*/
/****************************************************************************/

/*
*	Canned ISP commands and responses
*/

static const char	*lpc_ok		= "OK\r\n";
static const char	*lpc_sync	= "Synchronized\r\n";
static const char	*lpc_echo	= "A 0\r\n";
static const char	*lpc_lock	= "U 23130\r\n";

/*
*	Baud rates supported by the LPC's ISP
*/

static const int	bauds[]		= { 9600, 19200, 38400, 57600, 115200 };

/*
*	Code to read the FLASH in non-ISP mode
*/

static const char	lpc_read_flash[] = {

	0x48, 0x00, 0x4f, 0xe2,	//	e24f0048 	sub		r0,pc,#72
	0x38, 0x10, 0x90, 0xe5,	//	e5901038	ldr		r1,[r0,#56]
	0x3c, 0x30, 0x90, 0xe5,	//	e590203c	ldr		r3,[r0,#60]
	0x00, 0x20, 0x91, 0xe5,	//	e5912000	ldr		r2,[r1]
	0x00, 0x30, 0x81, 0xe5,	//	e5813000	str		r3,[r1]
	0x00, 0x30, 0xa0, 0xe3,	//	e3a03000 	mov		r3,#0
							//			loop:
	0x04, 0x40, 0x93, 0xe4,	//	e4934004	ldr		r4,[r3],#4
	0x04, 0x40, 0x80, 0xe4,	//	e4804004	str		r4,[r0],#4
	0x40, 0x00, 0x53, 0xe3,	//	e3530040 	cmp		r3,#64
	0xfb, 0xff, 0xff, 0xba,	//	bafffffb 	blt		loop
	0x00, 0x20, 0x81, 0xe5,	//	e5812000 	str		r2,[r1]
	0x1e, 0xff, 0x2f, 0xe1,	//	e12fff1e 	bx		lr
};

/*
*	Code to blank-check the first sector
*/

static const char	lpc_blank_first[] = {

	0x10, 0x30, 0x1f, 0xe5,	//	e51f3010 	ldr		r3, [pc,#-16]
	0x18, 0x10, 0x1f, 0xe5,	//	e51f1018 	ldr		r1, [pc,#-24]
	0x00, 0x20, 0x91, 0xe5,	//	e5912000 	ldr		r2,[r1]
	0x00, 0x30, 0x81, 0xe5,	//	e5813000	str		r3,[r1]
	0x1c, 0x30, 0x1f, 0xe5,	//	e51f301c 	ldr		r3,[pc,#-28]
	0x00, 0x00, 0xa0, 0xe3,	//	e3a03000 	mov		r0,#0
							//			loop:
	0x04, 0x40, 0x33, 0xe5,	//	e5334004 	ldr		r4,[r3,#-4]!
	0x01, 0x40, 0x84, 0xe2,	//	e2844001 	add		r4,r4,#1
	0x04, 0x00, 0x80, 0xe1,	//	e1800004 	orr		r0,r0,r4
	0x03, 0x30, 0x13, 0xe1,	//	e1130003 	tst		r3,r3
	0xfa, 0xff, 0xff, 0x1a,	//	1afffffa	bne		loop
	0x00, 0x20, 0x81, 0xe5,	//	e5812000 	str		r2,[r1]
	0x44, 0x00, 0x0f, 0xe5,	//	e50f0044 	str		r0,[pc,#-68]
	0x1e, 0xff, 0x2f, 0xe1,	//	e12fff1e 	bx		lr
};

/****************************************************************************/
/*					Public functions										*/
/****************************************************************************/

/** @addtogroup lpc_prg_lib */
/** @{ */

/**
*	Set the crystal frequency of the target.
*
*	The LPC chips must know their clock frequency in order to time
*	FLASH accesses. This function sets the frequency that the target
*	chips runs during programming. Some chips, notably the LPC23xx
*	series, run from an internally generated clock for which the
*	frequency is known. Others, for example the LPC210x chips run from
*	the frequency of their external crystal. The latter class of chips
*	must be told about their clock. This call can be used for that. It
*	must be called before LPC_TargetConnect() is called. The function
*	does nothing to the target, it just makes a note of the frequency,
*	which will subsequently be used by the LPC_TargetConnect() function.
*
*	@param	lpc		Pointer to the library state descriptor
*
*	@param	freq	The crystal frequency, in Hz. If set to 0, then no
*					change is done, just the current frequency is returned.
*
*	@return		The current frequency. It might be different from the setting,
*				if the target chip runs from its internal oscillator.
*/

LPC_ERROR	LPC_TargetCrystal( LPC lpc, int freq )
{
	if ( freq > 0 ) {

		lpc->tgt_freq = freq;
		_lpc_Log( lpc, LPC_LOG_TARGET,
				  "Target clock is set to %f MHz", freq/1E6 );
	}

	return( lpc->tgt_freq );
}

/**
*	Set the baud rate to talk to the target.
*
*	This function sets the initial baud rate with which the target
*	will be contacted. It doesn't do anything with the target, just
*	prepares internal data for a subsequent call to LPC_TargetConnect().
*	If the <tt>fast</tt> parameter is larger than <tt>baud</tt>, then after
*	contacting the target the LPC_TargetConnect() function negotiates the
*	highest baudrate achievable with the target's clock frequency but still
*	not larger than the <tt>fast</tt> argument and switches to that rate.
*	If the parameter is less than or equal to <tt>baud</tt>, then the initial
*	baud rate is kept.<br><br>
*	Due to the limitations of the ISP, only speeds of 9600, 19200, 38400,
*	57600 and 115200 baud are supported.
*
*	@attention
*		On some systems changing the baudrate also resets the modem
*		control signals, resetting the target. On such systems you must
*		not enable fast mode. On x86 Linux it is safe to use the fast mode
*		with the standard PC serial ports as well as with USB-serial adapters.
*
*	@param	lpc		Pointer to the library state descriptor
*
*	@param	baud	The initial baud rate. If this value is 0, then the
*					settings are not changed, just the current baudrate
*					setting is returned. In that case, if the \p fast
*					argument is 0, then the returned value is the initial
*					baud rate and if non-zero, the fast baud rate is returned.
*					Note that after connecting to the target the fast baud
*					rate value is updated to the actual negotiated value.
*
*	@param	fast	The highest baud rate that is allowed to be used.
*					If this number is less than or equal to the \p baud
*					argument, then no speed-up is attempted.
*					Otherwise, after the initial connection a higher
*					baud rate might be selected. This argument puts an upper
*					limit on that baud rate, but it is not guaranteed that
*					a higher baud rate will be selected at all, as it depends
*					on the clock frequency of the target and other factors.
*
*	@return			The initial baud rate after the call or -1 if the
*					specified baud rate is not valid.
*/

LPC_ERROR	LPC_TargetBaud( LPC lpc, int baud, int fast )
{
	if ( baud ) {

		if ( ! _lpc_SerialBaud( baud ) ) return( LPC_ERROR_CONFIG );

		if ( fast < baud ) fast = baud;

		_lpc_Log( lpc, LPC_LOG_TARGET,
				  "Initial baud rate is set to %d, max allowed is %d",
				  baud, fast );

		lpc->tgt_baud = baud;
		lpc->tgt_fast = fast;
		return( lpc->tgt_baud );
	}
	else {

		return( fast ? lpc->tgt_fast : lpc->tgt_fast );
	}
}

/**
*	Assign the target control signals to a user function.
*
*	Although most often the target's reset and ISP signals are controlled
*	using the serial port's RTS and DTR signals, in some cases your target
*	might not have those signals connected. In that case the controlling of
*	the reset and ISP can be delegated to your program, which can do anything
*	it wishes (e.g. telling the user to manually assert/negate the signals).
*	This function takes a function and a parameter. Whenever the reset or
*	ISP signal of your target needs to be changed, the function you passed
*	as the first argument will be called. That function, in turn, will get
*	two arguments. The first argument is an LPC_CONTROL enumerator type,
*	which is one of LPC_RES_ASSERT, LPC_RES_NEGATE, LPC_ISP_ASSERT or
*	LPC_ISP_NEGATE telling you what to do. The second argument, a void pointer
*	is the pointer that you specified as the second argument of this function.
*
*	@param	lpc		Pointer to the library state descriptor
*
*	@param	func	A function pointer for manual signal assertion.
*					If this pointer is NULL, then all signal associations
*					will be deleted. Otherwise, the pointer must be to a
*					function taking an int and a void * argument. The function
*					will be called every time a target control signal has to
*					be changed. It should return 0 in case of failure and
*					!0 in case of success.
*
*	@param	param	A void pointer, which will be passed to the function
*					specified by the \p func argument.
*
*	@return			0 on success, negative error code on failure
*/

LPC_ERROR	LPC_TargetControlManual( LPC lpc,
								  	 int (*func)( LPC_CONTROL, void * ),
								  	 void *param )
{
	lpc->ser_resc = -1;
	lpc->ser_ispc = -1;
	lpc->tgt_user = func;
	lpc->tgt_parm = param;
	_lpc_Log( lpc, LPC_LOG_TARGET, "Bound target control to a user funtion" );
	return( 0 );
}

/**
*	Assign the target control signals to modem control signals.
*
*	The target chip's reset and ISP signals are assumed to be driven by the
*	modem control signals of RTS and DTR. This function binds the target
*	control signals to the modem control signals and sets up the active
*	polarity for them.<br>
*	This function does nothing on the actual lines, it simply sets up an
*	association. Therefore, this function can be called even when the serial
*	port is not open.<br>
*	Although most often the above mentioned modem control signals are used
*	to control the target, if your target board does not use those signals,
*	then it is possible to set up a custom signal control. In that case you
*	provide a callback function that will be called whenever a control signal
*	should be changed. The function returns void and has two integer arguments.
*	The first argument is set to 0 if the signal to control is the reset
*	and 1 if the signal to set is the chip's ISP pin. The second argument is
*	1 if the signal selected by the first argument should be asserted and 0
*	if it should be negated.
*
*	@param	lpc		Pointer to the library state descriptor
*
*	@param	reset	The control signal and polarity for the target's reset
*					signal. It is one of the values defined by the
*					enum LPC_CONTOL_MODE.
*
*	@param	isp		The control signal and polarity for the target's ISP
*					signal. It is one of the values defined by the
*					enum LPC_CONTOL_MODE.
*
*	@return			0 on success, negative error code on failure
*/

LPC_ERROR	LPC_TargetControlModem( LPC lpc, LPC_CONTROL_MODE reset,
								    LPC_CONTROL_MODE isp )
{
static const char *modes[] = { "+RTS", "-RTS", "+DTR", "-DTR" };

	lpc->tgt_user = NULL;

	if ( reset > 3 || isp > 3 || ( reset & 2 ) == ( isp & 2 ) ) {

		return( LPC_ERROR_CONFIG );
	}

	lpc->ser_resc = reset;
	lpc->ser_ispc = isp;

	_lpc_Log( lpc, LPC_LOG_TARGET, "Bound target reset to %s and ISP to %s",
			  modes[ reset ], modes[ isp ] );

	return( 0 );
}

/**
*	Control of the target's reset signal.
*
*	This call asserts or negates the reset signal of the target. The
*	signal must have already be bound by the LPC_TargetControl() call.
*
*	@param	lpc		Pointer to the library state descriptor
*
*	@param	val		If non-zero, the reset signal will be asserted.
*					If zero, reset will be negated.
*
*	@return			0 on success, -1 if the modes specified are illegal.
*/

LPC_ERROR	LPC_TargetReset( LPC lpc, int val )
{
	_lpc_Log( lpc, LPC_LOG_TARGET, "%sing target reset",
			 val ? "Assert" : "Negat" );

	if ( lpc->tgt_user ) {

		val = val ? LPC_RES_ASSERT : LPC_RES_NEGATE;
		return( (*lpc->tgt_user)( val, lpc->tgt_parm ) ? 0 : LPC_ERROR_CBACK );
	}

	if ( lpc->ser_resc < 0 ) return( LPC_ERROR_CONFIG );
	val = ( val ? lpc->ser_resc : ~lpc->ser_resc ) & 1;
	return( LPC_SerialSignal( lpc, lpc->ser_resc & 2, val ) );
}

/**
*	Control of the target's ISP signal.
*
*	This call asserts or negates the ISP signal of the target. The
*	signal must have already be bound by the LPC_TargetControl() call.
*
*	@param	lpc		Pointer to the library state descriptor
*
*	@param	val		If non-zero, the ISP signal will be asserted.
*					If zero, ISP will be negated.
*
*	@return			0 on success, -1 if the modes specified are illegal.
*/

LPC_ERROR	LPC_TargetIsp( LPC lpc, int val )
{
	_lpc_Log( lpc, LPC_LOG_TARGET, "%sing target ISP",
			 val ? "Assert" : "Negat" );

	if ( lpc->tgt_user ) {

		val = val ? LPC_ISP_ASSERT : LPC_ISP_NEGATE;
		return( (*lpc->tgt_user)( val, lpc->tgt_parm ) ? 0 : LPC_ERROR_CBACK );
	}

	if ( lpc->ser_ispc < 0 ) return( LPC_ERROR_CONFIG );
	val = ( val ? lpc->ser_ispc : ~lpc->ser_ispc ) & 1;
	return( LPC_SerialSignal( lpc, lpc->ser_ispc & 2, val ) );
}

/**
*	Registers a power-up callback function.
*
*	In some cases the target's ISP goes into a funny state where
*	it ignores the ISP signal after a reset. In that case the only
*	way to enter the ISP mode is powering up the target while the
*	reset is held low. This function allows a user callback to be
*	set up which will be called when the target power should be
*	removed or applied. The callback will receive two arguments.
*	The first one isan integer, which is 0 for removing the power
*	and 1 for powering up. The second argument of the callback is
*	a void pointer, which will be the same as what this function
*	receives in its third parameter.
*
*	Calling this function with a NULL finction will remove the callback.
*
*	@param	lpc		Pointer to the library state descriptor
*
*	@param	func	The callback function
*
*	@param	user	The first argument to the function
*/

void	LPC_TargetPower( LPC lpc, void (*func)(int,void*), void *user )
{
	lpc->tgt_pwrf = func;
	lpc->tgt_pwru = user;
}

/**
*	Attempt to connect to the target.
*
*	This function tries to establish connection to the target.
*	The serial port must be open. The initial baud rate must
*	have been set by the LPC_TargetBaud() call. The reset and
*	ISP signals must have been bound by the LPC_SerialBind() call.<br>
*	This function resets the target, puts it into ISP mode, establishes
*	the serial link and reads the chip ID. If the chip supports a higher
*	baud rate and the use of such baud rate was allowed, then both the
*	target and the serial port are reconfigured to use the higher rate.
*	<br><br>
*	If the chip is the kind that has no internal oscillator, for example
*	the LPC210x series, then it is mandatory that the crystal frequency
*	is set by calling LPC_TargetCrystal() before calling this function.
*	If you don't do that, you will get an <tt>LPC_ERROR_CONFIG</tt> code.
*
*	@param	lpc		Pointer to the library state descriptor
*
*	@return			The negotiated baud rate on success or a negative
*					error code on failure.
*/

LPC_ERROR	LPC_TargetConnect( LPC lpc )
{
LPC_CHIP	**chip;
int		err, guess, known;
double	speed;

	lpc->tgt_chip = NULL;

	// If the serial port is not open or if the control
	// signals are not set, then we can't do anything.

	if ( lpc->ser_port < 0 ) return( LPC_ERROR_CONFIG );
	if ( ( lpc->ser_resc < 0 || lpc->ser_ispc < 0 ) &&
		 ! lpc->tgt_user ) return( LPC_ERROR_CONFIG );

	_lpc_Log( lpc, LPC_LOG_TARGET, "Trying to connect to the target" );

	// Set up the initial baud rate

	if ( ( err = LPC_SerialBaud( lpc, lpc->tgt_baud ) ) < 0 ) return( err );

	// Take a guess on the clock frequency. If the user specified
	// it, we use that value. If not, we use the value that all
	// LPC23xx chips use (derived from their RC oscillator).

	guess = lpc->tgt_freq ? lpc->tgt_freq : RCOSC;
	_lpc_Log( lpc, LPC_LOG_TARGET, "Assuming %8.5f MHz target clock",
			  guess / 1e6 );

	// If we have to do power control, do it now

	if ( lpc->tgt_pwrf ) {

		// Tell the user to power the target down

		(*lpc->tgt_pwrf)( 0, lpc->tgt_pwru );

		// Assert reset

		if ( ( err = LPC_TargetReset( lpc, 1 ) ) < 0 ) return( err );

		// Tell the user to power up the target

		(*lpc->tgt_pwrf)( 1, lpc->tgt_pwru );
	}

	// Connect to the target

	if ( ( err = _lpc_TargetContact( lpc, (int) round( guess / 1E3 )  ) ) < 0 )

		return( err );

	// Request for the chip ID.

	if ( ( err = _lpc_SerialPuts( lpc, "J\r\n" ) < 0 ) ) return( err );

	// First we get back a command status code (should be 0).

	if ( ( err = _lpc_TargetResult( lpc, 100 ) ) )

		return( _lpc_TargetErrorMap( err ) );

	// Then we get the chip ID

	if ( ( err = _lpc_TargetResult( lpc, 100 ) ) < 0 ) return( err );

	// Try to identify the chip by its ID

	for ( chip = (LPC_CHIP **) chips ; *chip ; chip++ ) {

		if ( (*chip)->code == (unsigned) err ) {

			// We've found our chip.

			break;
		}
	}

	if ( ! *chip ) {

		// This is an unknown chip, we can't continue.

		_lpc_Error( lpc, "Chip identifier 0x%08x (%d) is not known", err, err );
		return( LPC_ERROR_CHIP );
	}

	if ( ! *chip && ! lpc->tgt_freq ) {

		// We don't know the chip's clock frequency, we can't continue

		_lpc_Error( lpc, "The %s chip must have its clock frequency set",
				    (*chip)->name );
		return( LPC_ERROR_CONFIG );
	}

	// If the chip has built-in oscillator, that's the frequency,
	// otherwise we accept what the user told us.

	known = (*chip)->cpu->has_rco ?
			(*chip)->cpu->has_rco : (unsigned) lpc->tgt_freq;

	// Now we know what chip we're dealing with. Update the data

	lpc->tgt_freq = known;

	if ( known != guess ) {

		// We told the chip the incorrect clock, reset,
		// reconnect and tell it the real clock.

		_lpc_Log( lpc, LPC_LOG_TARGET,
				  "Determined %8.5 MHz target clock, reconnecting",
				  known / 1e6 );
		err = _lpc_TargetContact( lpc, (int) round( known / 1E3 ) );
		if ( err < 0 ) return( err );
	}

	// OK, now we have a chip ready to work.

	lpc->tgt_chip = *chip;
	_lpc_Log( lpc, LPC_LOG_TARGET, "Target is an %s @ %f MHz",
			  lpc->tgt_chip->name, known / 1E6 );

	// If we're allowed to switch to a higher baud rate,
	// we have to work out the highest allowed value and
	// switch to that.

	if ( lpc->tgt_fast > lpc->tgt_baud ) {

		// The bootloader in the chip does not use the fractional divider,
		// so we have to see what baud rates can we synthetise from the
		// current clock by simple integer division.

		for ( guess = sizeof( bauds ) / sizeof( int ) ; --guess >= 0 ; ) {

			if ( bauds[ guess ] > lpc->tgt_fast ) continue;
			if ( bauds[ guess ] < lpc->tgt_baud ) continue;
			speed = known / ( lpc->tgt_chip->cpu->sio_div * bauds[ guess ] );
			speed = known / ( lpc->tgt_chip->cpu->sio_div * speed );
			speed = fabs( ( bauds[ guess ] - speed ) / bauds[ guess ] );

			if ( speed < 0.03 ) break;
		}

		if ( guess >= 0 ) {

			// We can switch to a higher baud rate
			// Tell it to the target and get the response.

			sprintf( lpc->ser_sbuf, "B %d 1\r\n", bauds[ guess ] );

			if ( ( err = _lpc_SerialPuts( lpc, lpc->ser_sbuf ) ) < 0 )

				return( err );

			// Get the response from the target

			err = _lpc_TargetResult( lpc, 100 );

			if ( err == 0 ) {

				// The target accepted the new baud rate.
				// Set it up with the serial driver

				if ( ( guess = LPC_SerialBaud( lpc, bauds[ guess ] ) ) < 0 )

					return( guess );

				_lpc_Log( lpc, LPC_LOG_TARGET, "Stepped up to %d baud", guess );
			}
			else if ( err < 0 ) {

				return( err );
			}
			else {

				// The target refused the higher baud rate,
				// that's fine, we keep using the current one.

				_lpc_Log( lpc, LPC_LOG_TARGET,
						  "Step-up refused, staying at %d baud",
						  ( guess = lpc->ser_baud ) );

			}

			// Update our state

			lpc->tgt_fast = lpc->ser_baud;
		}
	}
	else {

		guess = lpc->ser_baud;
	}

	return( guess );
}

/**
*	Write data to the RAM on the target.
*
*	The function writes data to the target's main RAM. You can not write
*	data to the peripheral registers or RAM belonging to peripherals.
*
*	@param	lpc		Pointer to the library state descriptor
*
*	@param	addr	The address in the target's RAM. It must be word
*					aligned, that is, a multiple of 4.
*
*	@param	data	Pointer to data in the host's memory. The data pointed
*					will be copied into the target's RAM as bytes, not as
*					words.
*
*	@param	dlen	The number of bytes to transfer. It must be a multiple
*					of 4.
*
*	@return		0 on success or a negative error code on failure.
*/

LPC_ERROR	LPC_TargetMemWrite( LPC lpc, int addr, const void *data, int dlen )
{
char	buff[ 80 ];
char	*savd, *dptr = (char *) data;
int		line, csum, xfer, savl, retc, retv;

	if ( ! lpc->tgt_chip ) return( LPC_ERROR_CHIP );
	if ( ! dlen ) return( 0 );

	_lpc_Log( lpc, LPC_LOG_TARGET, "Writing %d bytes to address 0x%08x",
			  dlen, addr );

	sprintf( buff, "W %u %u\r\n", addr, dlen );
	retv = _lpc_SerialPuts( lpc, buff );
	if ( retv < 0 ) return( retv );
	retv = _lpc_TargetResult( lpc, 100 );
	if ( retv ) return( _lpc_TargetErrorMap( retv ) );

	savd = dptr;
	savl = dlen;
	line = 0;
	csum = 0;
	retc = 0;

	while ( dlen ) {

		// UU-encode a line, up to 45 bytes.

		xfer = dlen > 45 ? 45 : dlen;
		retv = _lpc_UuEncode( buff, dptr, xfer );

		// Note that UU-encoding specifies LF only but the LPC bootloader
		// expects CR/LF and barfs if it doesn't get the CR.

		buff[ retv + 0 ] = '\r';
		buff[ retv + 1 ] = '\n';
		buff[ retv + 2 ] = '\0';
		if ( ( retv = _lpc_SerialPuts( lpc, buff ) ) < 0 ) return( retv );

		// Although the LPC bootloader is supposed to support XON/XOFF,
		// it does not, at least on the LPC23xx chips. It can process
		// data up to 57600 baud, but if the baud rate is higher than
		// that, then we shall give an extra millisec for the target to
		// process the line.

		if ( lpc->ser_baud > 57600 ) usleep( 1000 );

		// Update the length, data pointer and checksum

		dlen -= xfer;
		while ( xfer-- ) csum += *dptr++ & 255;

		// If we haven't sent 20 lines yet, keep going

		if ( ++line != 20 && dlen ) continue;

		// Now we have to send the checksum and wait for a response

		sprintf( buff, "%d\r\n", csum );
		retv = _lpc_SerialPuts( lpc, buff );
		if ( retv < 0 ) return( retv );
		retv = LPC_SerialRead( lpc, buff, 50, 200 );

		if ( retv < 0 ) return( retv );

		if ( retv == 0 ) {

			// Timeout on read.

			return( LPC_ERROR_COMMS );
		}

		line = 0;
		csum = 0;

		if ( ! strcmp( buff, "OK\r\n" ) ) {

			// The checksum is correct, keep going.

			retc = 0;
			savd = dptr;
			savl = dlen;
			continue;
		}

		if ( ! strcmp( buff, "RESEND\r\n" ) ) {

			// The checksum is incorrect, we have to resend.
			// We re-send a block up to 2 times.

			if ( ++retc != 3 ) {

				dptr = savd;
				dlen = savl;
				continue;
			}
		}

		// Too many retries or the response was garbled.

		LPC_SerialWrite( lpc, "\e", 1 );
		return( LPC_ERROR_COMMS );
	}

	return( 0 );
}

/**
*	Reading the target's RAM or FLASH.
*
*	The function can be used to read the FLASH or the main RAM of
*	the target. You can not read from peripheral registers or RAM
*	blocks associated with peripheral units.
*
*	@param	lpc		Pointer to the library state descriptor
*
*	@param	addr	The address in the target's RAM or FLASH. It must be word
*					aligned, that is, a multiple of 4.
*
*	@param	data	Pointer to a buffer in the host's memory, where the
*					data read from the target will be stored. The data
*					will be stored as bytes, not words.
*
*	@param	dlen	The number of bytes to transfer. It must be a multiple
*					of 4.
*
*	@return		0 on success or a negative error code on failure.
*/

LPC_ERROR	LPC_TargetMemRead( LPC lpc, int addr, void *data, int dlen )
{
char	buff[ 200 ];
char	*savd, *dptr = data;
int		line, csum, xfer, savl, retc, retv;

	if ( ! lpc->tgt_chip ) return( LPC_ERROR_CHIP );
	if ( ! dlen ) return( 0 );

	if ( addr < 64 ) {

		// The first 64 bytes of FLASH should be read. We have to
		// download special code to do that. It will copy the first
		// 64 bytes into RAM and we read it from there. For all other
		// addresses we can read the FLASH directly.

		retv = _lpc_TargetFirst64( lpc );
		if ( retv < 0 ) return( retv );

		// Now we have to get the data, but only the first few bytes

		xfer = 64 - addr;
		if ( xfer > dlen ) xfer = dlen;

		retv = LPC_TargetMemRead( lpc, lpc->tgt_chip->ram->ram_beg + addr +
								 	   LPC_CODE_BASE, dptr, xfer );
		if ( retv < 0 ) return( retv );
		addr += xfer;
		dlen -= xfer;
		dptr += xfer;
		return( LPC_TargetMemRead( lpc, addr, dptr, dlen ) );
	}

	_lpc_Log( lpc, LPC_LOG_TARGET, "Reading %d bytes from address 0x%08x",
			  dlen, addr );

	sprintf( buff, "R %u %u\r\n", addr, dlen );
	retv = _lpc_SerialPuts( lpc, buff );
	if ( retv < 0 ) return( retv );
	retv = _lpc_TargetResult( lpc, 100 );
	if ( retv  ) return( _lpc_TargetErrorMap( retv ) );

	savd = dptr;
	savl = dlen;
	line = 0;
	csum = 0;
	retc = 0;

	while ( dlen ) {

		retv = LPC_SerialRead( lpc, lpc->ser_sbuf, 63, 100 );
		if ( retv < 0 ) return( retv );

		if ( ! retv ||
			 ( retv = _lpc_UuDecode( buff, lpc->ser_sbuf, retv ) ) < 0 ) {

			return( LPC_ERROR_COMMS );
		}

		if ( retv > dlen ) {

			// The target sent us too much data, it is an error

			return( LPC_ERROR_COMMS );
		}

		// OK, we can copy the data to the user

		memcpy( data, buff, retv );
		data  = ((char *) data) + retv;
		dlen -= retv;

		// Update the checksum

		while ( retv-- ) csum += buff[ retv ] & 255;
		if ( ++line != 20 && dlen ) continue;

		// We now must expect the checksum

		line = 0;
		retv = _lpc_TargetResult( lpc, 100 );
		if ( retv < 0 ) return( retv );

		if ( retv == csum ) {

			// Checksum matches

			if ( ( retv = _lpc_SerialPuts( lpc, "OK\r\n" ) ) < 0 )

				return( retv );

			savd = dptr;
			savl = dlen;
			csum = 0;
			retc = 0;
			continue;
		}

		if ( ++retc < 3 ) {

			// Mismatch, request for a resend

			if ( ( retv = _lpc_SerialPuts( lpc, "RESEND\r\n" ) ) < 0 )

				return( retv );

			dptr = savd;
			dlen = savl;
			csum = 0;
			continue;
		}

		// Too many retries, bail out

		_lpc_SerialPuts( lpc, "\e" );
		return( LPC_ERROR_COMMS );
	}

	// Success

	return( 0 );
}

/**
*	Execute code on the target.
*
*	The function transfer control from the ISP code to any code on the
*	target. The function does not wait for the code to complete, it returns
*	immediately.
*
*	@param	lpc		Pointer to the library state descriptor
*
*	@param	addr	The address where code execution should start. For ARM
*					code it must be on 4-byte boundary, for THUMB code
*					it must be on a 2-byte boundary.
*
*	@param	thumb	If this argument is non-zero, then code execution will
*					start in THUMB modem, if it is 0, then the code will be
*					executed in ARM mode.
*
*	@return		0 on success or a negative error code on failure.
*/

LPC_ERROR	LPC_TargetExecute( LPC lpc, int addr, int thumb )
{
int		retv;

	if ( ( retv = _lpc_TargetUnlock( lpc ) ) < 0 ) return( retv );

	_lpc_Log( lpc, LPC_LOG_TARGET,
			  "Executing code from 0x%08x in %s mode", addr,
			 thumb ? "THUMB" : "ARM" );

	sprintf( lpc->ser_sbuf, "G %d %c\r\n", addr, thumb ? 'T' : 'A' );
	retv = _lpc_SerialPuts( lpc, lpc->ser_sbuf );
	if ( retv < 0 ) return( retv );
	return( _lpc_TargetErrorMap( _lpc_TargetResult( lpc, 100 ) ) );
}

/**
*	Write data to the FLASH of the target.
*
*	This call writes data to the target's FLASH. It does <b>not</b>
*	erase the FLASH, you must have erased the relevant sector(s)
*	beforehand. It does, however, verify that the FLASH write was
*	successful
*
*	@param	lpc		Pointer to the library state descriptor
*
*	@param	addr	The start address in the FLASH. It must be on a 256
*					byte boundary.
*
*	@param	data	Pointer to the data to be written into the FLASH
*
*	@param	dlen	The length of the data to be written. It must be
*					a multiple or 256 bytes.
*
*	@return		0 on success, a negative error code on failure.
*/

LPC_ERROR	LPC_TargetFlash( LPC lpc, int addr, const void *data, int dlen )
{
char	buff[ 64 ];
int		xfer, retv, base;

	if ( ! lpc->tgt_chip ) return( LPC_ERROR_CHIP );
	if ( ( addr & 255 ) || ( dlen & 255 ) ) return( LPC_ERROR_PARAM );
	if ( ! dlen ) return( 0 );

	// Check if the address range is valid

	if ( LPC_TargetSector( lpc, addr ) < 0 ||
		 LPC_TargetSector( lpc, addr + dlen - 256 ) < 0 )

		return( LPC_ERROR_PARAM );

	// Now burn the data into the FLASH. The 'base' variable is set to
	// a suitable address in the target's RAM for data buffer.

	base = lpc->tgt_chip->ram->ram_beg + LPC_XFER_BUFF;

	while ( dlen ) {

		// Determine if we can use larger transfer blocks.
		// We can, if we have enough data, we are aligned and the target
		// has more than 2K of RAM. Actually the alignment requirement is
		// not needed, we should only need to test if we cross a sector
		// boundary, but since the alignment test automatically guarantees
		// the boundary thing as well and it was simpler to code...

		xfer = 256;

		if ( lpc->tgt_chip->ram->ram_len > 2 ) {

			if ( dlen >=  512 && ! ( addr & 0x1ff ) ) xfer = 512;
			if ( dlen >= 1024 && ! ( addr & 0x3ff ) ) xfer = 1024;
		}

		// Transfer the data to the target, prepare the sector and
		// unlock the copy from RAM to FLASH command

		retv = LPC_TargetMemWrite( lpc, base, data, xfer );
		if ( retv < 0 ) return( retv );
		retv = LPC_TargetSector( lpc, addr );
		retv = _lpc_TargetPrepare( lpc, retv, retv );
		if ( retv < 0 ) return( retv );
		retv = _lpc_TargetUnlock( lpc );
		if ( retv < 0 ) return( retv );

		// Now issue the actual command

		_lpc_Log( lpc, LPC_LOG_TARGET,
				  "Burning %d bytes from %08x to %08x", xfer, base, addr );

		sprintf( buff, "C %d %d %d\r\n", addr, base, xfer );
		retv = _lpc_SerialPuts( lpc, buff );
		if ( retv < 0 ) return( retv );

		// Wait for completion. It can take a long time, so we specify a
		// rather long timeout for the read operation

		retv = _lpc_TargetResult( lpc, 1000 );
		if ( retv ) return( _lpc_TargetErrorMap( retv ) );

		// So far so good. Now issue a compare command for the FLASH.

		retv = LPC_TargetCompare( lpc, addr, base, xfer );

		if ( retv  < 0 ) return( retv );
		if ( retv == 0 ) return( LPC_ERROR_FLASH );

		// All is good.

		dlen -= xfer;
		addr += xfer;
		data  = (char *) data + xfer;
	}

	return( 0 );
}

/**
*	Erase one or more sectors of the FLASH.
*
*	Erases all sectors between \p first and \p last, inclusive.
*	After the erase it checks if the sectors are indeed blank.
*
*	@param	lpc		Pointer to the library state descriptor
*
*	@param	first	The first sector to erase
*
*	@param	last	The last sector to erase. If you want to erase
*					only one sector, then set this argument to the
*					same value as \p first. The \p last argument must
*					larger than or equal to the \p first argument.
*
*	@return		0 on success, a negative error code on failure. In case of
*				a sector being non-blank after erase, LPC_ERROR_FAIL is
*				returned.
*/

LPC_ERROR	LPC_TargetErase( LPC lpc, int first, int last )
{
char	buff[ 64 ];
int		retv;

	if ( ! lpc->tgt_chip )

		return( LPC_ERROR_CHIP );

	if ( first < 0 || last < first ||
		 last >= (int) lpc->tgt_chip->rom->num_sec )

		return( LPC_ERROR_PARAM );

	if ( ( retv = _lpc_TargetPrepare( lpc, first, last ) ) < 0 )

		return( retv );

	if ( ( retv = _lpc_TargetUnlock( lpc ) ) < 0 )

		return( retv );

	_lpc_Log( lpc, LPC_LOG_TARGET, "Erasing sectors %d to %d", first, last );
	sprintf( buff, "E %d %d\r\n", first, last );

	if ( ( retv = _lpc_SerialPuts( lpc, buff ) ) < 0 ||
		 ( retv = _lpc_TargetResult( lpc, 2000 ) ) < 0 )

		return( retv );

	if ( retv > 0 ) return( _lpc_TargetErrorMap( retv ) );

	// Check the sectors for being blank

	if ( ( retv = LPC_TargetBlank( lpc, first, last ) ) < 0 ) return( retv );

	if ( retv == 0 ) {

		// We found a sector which is not blank, report error

		_lpc_Log( lpc, LPC_LOG_ERROR,
				  "Non-blank sector after erase" );
		return( LPC_ERROR_FAIL );
	}

	return( 0 );
}

/**
*	Blank check a range of sectors of the target.
*
*	The function checks if the given sector of the target is blank or
*	not.
*
*	@param	lpc		Pointer to the library state descriptor
*
*	@param	first	The first sector to test
*
*	@param	last	The last sector to test, greater than or equal to first
*
*	@return			1 if the sector is blank, 0 if it is not blank and
*					a negative error code on failure.
*/

LPC_ERROR	LPC_TargetBlank( LPC lpc, int first, int last )
{
char	buff[ 64 ];
int		retv;

	if ( ! lpc->tgt_chip )

		return( LPC_ERROR_CHIP );

	if ( first < 0 || first > last ||
		 last >= (int) lpc->tgt_chip->rom->num_sec )

		return( LPC_ERROR_PARAM );

	if ( ! first ) {

		retv = _lpc_TargetFirstSect( lpc );
		if ( retv != 1 ) return( retv );
		if ( ++first > last ) return( 1 );
	}

	_lpc_Log( lpc, LPC_LOG_TARGET,
			  "Blank checking sectors %d to %d", first, last );
	sprintf( buff, "I %d %d\r\n", first, last );

	if ( ( retv = _lpc_SerialPuts( lpc, buff ) ) < 0 )

		return( retv );

	retv = _lpc_TargetResult( lpc, 2000 );

	if ( retv == 0 ) {

		_lpc_Log( lpc, LPC_LOG_TARGET,
				  "Sectors %d to %d are blank", first, last );
		return( 1 );
	}

	retv = _lpc_TargetErrorMap( retv );

	if ( retv == LPC_ERROR_FAIL ) {

		// Test failed, we have to read two more words

		_lpc_Log( lpc, LPC_LOG_TARGET,
				  "Sectors %d to %d are not all blank", first, last );
		_lpc_TargetResult( lpc, 2000 );
		_lpc_TargetResult( lpc, 2000 );
		return( 0 );
	}

	return( retv );
}

/**
*	Map an address to a sector number.
*
*	The function receives an address and returns the FLASH sector
*	number that contains the address. If the address is not within
*	the FLASH, it returns <tt>LPC_ERROR_PARAM</tt>, or, if the chip
*	type is not yet known, <tt>LPC_ERROR_CHIP</tt>.
*
*	@param	lpc		Pointer to the library state descriptor
*
*	@param	addr	The address in the FLASH
*
*	@return		The sector number, which is >= 0, or a negative error code.
*/

LPC_ERROR	LPC_TargetSector( LPC lpc, unsigned addr )
{
unsigned	sect;

	if ( ! lpc->tgt_chip ) return( LPC_ERROR_CHIP );

	for ( sect = 0 ; sect < lpc->tgt_chip->rom->num_sec ; sect++ ) {

		if ( addr < (unsigned) lpc->tgt_chip->rom->sec_siz[ sect ] * 1024 )

			return( sect );
		else
			addr -= lpc->tgt_chip->rom->sec_siz[ sect ] * 1024;
	}

	return( LPC_ERROR_PARAM );
}

/**
*	Obtain the current chip descriptor.
*
*	The function returns a pointer to the descriptor of the current
*	target chip.
*
*	@param	lpc		Pointer to the library state descriptor
*
*	@return			The pointer to the chip descriptor or NULL if the
*					target is not known.
*/

const LPC_CHIP	*LPC_TargetChip( LPC lpc )
{
	return( lpc->tgt_chip );
}

/**
*	Compares two regions of the target
*/

LPC_ERROR	LPC_TargetCompare( LPC lpc, int adr1, int adr2, int dlen )
{
char	buff[ 64 ];
int		len1, len2, retv, base;

	if ( ! lpc->tgt_chip ) return( LPC_ERROR_CHIP );

	// Test the parameters

	if ( dlen < 0 || ( dlen & 3 ) || ( adr1 & 3 ) || ( adr2 & 3 ) ) {

		return( LPC_ERROR_PARAM );
	}

	// The obvious

	if ( ! dlen || adr1 == adr2 ) return( 1 );

	// Make sure that addr1 is the smaller one

	if ( adr1 > adr2 ) {

		retv = adr1;
		adr1 = adr2;
		adr2 = retv;
	}

	// If either address is less than 64, we have to grab the first
	// 64 bytes of the FLASH and do a manual compare

	if ( adr1 < 64 ) {

		base = lpc->tgt_chip->ram->ram_beg + LPC_CODE_BASE;

		// We have to get the first 64 bytes

		if ( ( retv = _lpc_TargetFirst64( lpc ) ) < 0 ) return( retv );

		// Work out the length of the comaprison

		len1 = 64 - adr1;
		if ( len1 > dlen ) len1 = dlen;

		// Now there's a trick: if addr2 is also less than 64, then we
		// might have to split the comparison into three instead of two...

		if ( adr2 < 64 ) {

			// Since adr2 was larger than adr1, the length goes down.

			len2 = 64 - adr2;
			if ( len2 > dlen ) len2 = dlen;

			// Do the first compare

			retv = LPC_TargetCompare( lpc, base + adr1, base + adr2, len2 );
			if ( retv < 0 ) return( retv );

			// Note that we know that len2 <= len1.

			adr1 += len2;
			adr2 += len2;
			len1 -= len2;
		}

		// Now adr2 is definitely out of the first 64 bytes range,
		// but adr1 is still in it. Do the compare.

		retv = LPC_TargetCompare( lpc, base + adr1, adr2, len1 );
		if ( retv < 0 ) return( retv );

		adr1 += len1;
		adr2 += len1;
		dlen -= len1;

		// If we have no data left, we just leave

		if ( ! dlen ) return( 0 );
	}

	// Both addresses are larger than 64, we can do a normal compare.
	// Since the data to compare can possibly be large, we give some
	// extra time for the response.

	_lpc_Log( lpc, LPC_LOG_TARGET, "Comparing %d bytes at %08x and %08x",
			  dlen, adr1, adr2 );
	sprintf( buff, "M %d %d %d\r\n", adr1, adr2, dlen );
	if ( ( retv = _lpc_SerialPuts( lpc, buff ) ) < 0 ) return( retv );
	retv = _lpc_TargetErrorMap( _lpc_TargetResult( lpc, 1000 ) );

	if ( ! retv ) {

		_lpc_Log( lpc, LPC_LOG_TARGET, "Regions match" );
		return( 1 );
	}

	if ( retv == LPC_ERROR_FAIL ) {

		// We have to consume the offset

		_lpc_TargetResult( lpc, 1000 );
		_lpc_Log( lpc, LPC_LOG_TARGET, "Regions differ" );
		return( 0 );
	}

	return( retv );
}

/**
*	Calculates the checksum of the vector table.
*
*	The LPC2xxx chips use an otherwise empty slot in the ARM vector
*	table to store a checksum of the vectors in the table. If the
*	checksum does not match, the chip assumes that its FLASH does
*	not contain valid code and does drop into the on-chip bootloader.
*	If the checksum is correct, then it executes the user code in the
*	FLASH.<br>
*	This function receives a pointer to a 32-byte memory block (the
*	size of the ARM vector table) and calculates and fills in the
*	checksum.
*
*	@param	lpc		Pointer to the library state descriptor
*
*	@param	vectab	A pointer to a 32-byte memory block which is assumed
*					to contain the ARM vector table. The checksum of that
*					block is calculated and the result is written back
*					into the same memory block. The memory block should
*					be in the target's format (i.e. little-endian).
*/

void	LPC_TargetVectab( LPC lpc, void *vectab )
{
unsigned char	*dptr = vectab;
int				i, k;

	for ( k = -1, i = 0 ; i < 4 ; i++, dptr++, k >>= 8 ) {

		k += dptr[  0 ];
		k += dptr[  4 ];
		k += dptr[  8 ];
		k += dptr[ 12 ];
		k += dptr[ 16 ];
		k += dptr[ 24 ];
		k += dptr[ 28 ];
		dptr[ 20 ] = ~k;
	}

	dptr = vectab;
	_lpc_Log( lpc, LPC_LOG_TARGET,
			  "Calculated vector checksum 0x%02x%02x%02x%02x",
			  dptr[ 23 ], dptr[ 22 ], dptr[ 21 ], dptr[ 20 ] );
}

/** @} */

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

/*
*	Contacts the target
*	~~~~~~~~~~~~~~~~~~~
*	Resets the target, drives it into ISP mode and sets up the
*	initial comms.
*/

static	int		_lpc_TargetContact( LPC lpc, int clock )
{
int		try, err, acc;
char	temp[ 16 ];

	for ( try = 0 ; try < 3 ; try++ ) {

		// Assert reset and ISP, wait, negate reset. We keep LPC asserted,
		// it may be used to control a UART enable or something like that.

		if ( ( err = LPC_TargetReset( lpc, 1 ) ) < 0 ) return( err );
		if ( ( err = LPC_TargetIsp( lpc, 1 ) ) < 0 ) return( err );
		usleep( 50000 );
		if ( ( err = LPC_TargetReset( lpc, 0 ) ) < 0 ) return( err );

		// Now we must drain the UART. We read it until
		// it comes back to us with timeout.

		for ( acc = 0 ;; ) {

			err = LPC_SerialRead( lpc, lpc->ser_sbuf, BLEN, 100 );

			if ( err < 0 ) return( err );

			if ( err > 0 ) {

				if ( ( acc += err ) > 2014 ) {

					// We collected a kilobyte worth of garbage from the
					// target. It's way too much. Declare error.

					_lpc_Error( lpc, "Excess garbage from the serial port" );
					return( LPC_ERROR_COMMS );
				}

				continue;
			}

			break;
		}

		// TX: ?
		// RX: Synchronized\r\n
		//
		// There can be at most one junk character in front of the
		// target's response.

		err = LPC_SerialWrite( lpc, "?", 1 );
		if ( err < 0 ) return( err );

		err = LPC_SerialRead( lpc, lpc->ser_sbuf, BLEN, 200 );
		if ( err < 0 ) return( err );

		if ( strcmp( lpc->ser_sbuf, lpc_sync ) &&
			 strcmp( lpc->ser_sbuf + 1, lpc_sync ) ) {

			// Target didn't respond. Try again.

			continue;
		}

		// TX: Synchronized\r\n
		// RX: Synchronized\r\n
		// RX: OK\r\n

		err = LPC_SerialWrite( lpc, lpc_sync, strlen( lpc_sync ) );
		if ( err < 0 ) return( err );

		err = LPC_SerialRead( lpc, lpc->ser_sbuf, BLEN, 100 );
		if ( err < 0 ) return( err );

		if ( strcmp( lpc->ser_sbuf, lpc_sync ) ) continue;

		err = LPC_SerialRead( lpc, lpc->ser_sbuf, BLEN, 100 );
		if ( err < 0 ) return( err );

		if ( strcmp( lpc_ok, lpc->ser_sbuf ) ) continue;

		// TX: xtal_freq\r\n
		// RX: xtal_freq\r\n
		// RX: OK\r\n

		sprintf( temp, "%d\r\n", clock );

		err = LPC_SerialWrite( lpc, temp, strlen( temp ) );
		if ( err < 0 ) return( err );

		err = LPC_SerialRead( lpc, lpc->ser_sbuf, BLEN, 100 );
		if ( err < 0 ) return( err );

		if ( strcmp( lpc->ser_sbuf, temp ) ) continue;

		err = LPC_SerialRead( lpc, lpc->ser_sbuf, BLEN, 100 );
		if ( err < 0 ) return( err );

		if ( strcmp( lpc->ser_sbuf, lpc_ok ) ) continue;

		// Turn the echo off
		// The messaging:
		// TX: A\r\n
		// RX: A\r\n
		// RX: 0\r\n

		err = LPC_SerialWrite( lpc, lpc_echo, strlen( lpc_echo ) );
		if ( err < 0 ) return( err );

		err = LPC_SerialRead( lpc, lpc->ser_sbuf, BLEN, 100 );
		if ( err < 0 ) return( err );

		if ( strcmp( lpc->ser_sbuf, lpc_echo ) ) continue;

		err = LPC_SerialRead( lpc, lpc->ser_sbuf, BLEN, 100 );
		if ( err < 0 ) return( err );

		if ( strcmp( lpc->ser_sbuf, "0\r\n" ) ) continue;

		// We're connected.

		return( 0 );
	}

	// Could not connect to the target. Negate the ISP signal and report error.

	_lpc_Error( lpc, "Target doesn't respond" );
	LPC_TargetIsp( lpc, 0 );
	return( LPC_ERROR_COMMS );
}

/*
*	Reads the numerical result from the target
*	~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*	Returns the result (>=0) on success or -1 on error. In that case
*	the error message is also set.
*/

static int	_lpc_TargetResult( LPC lpc, int timo )
{
int		ret;
char	*endp;

	ret = LPC_SerialRead( lpc, (char *) lpc->ser_sbuf, 20, timo );

	if ( ret < 0 ) return( ret );

	if ( ! ret ) {

		// Timeout, it's a comms error

		_lpc_Error( lpc, "Target response timeout" );
		return( LPC_ERROR_COMMS );
	}

	ret = strtoul( lpc->ser_sbuf, &endp, 10 );

	if ( endp == lpc->ser_sbuf || endp[ 0 ] != '\r' || endp[ 1 ] != '\n' ) {

		// Illegal response to the command

		_lpc_Error( lpc, "Illegal target response" );
		return( LPC_ERROR_COMMS );
	}

	return( ret );
}

/*
*	Maps a target error to an LPC error code
*	~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/

static	int		_lpc_TargetErrorMap( int code )
{
	if ( code <= 0 ) return( code );

	switch ( code ) {

		case LPC_TEC_INV_CMD:	return( LPC_ERROR_COMMAND );
		case LPC_TEC_SRC_ADDR:	return( LPC_ERROR_ADDRESS );
		case LPC_TEC_DST_ADDR:	return( LPC_ERROR_ADDRESS );
		case LPC_TEC_SRC_MAP:	return( LPC_ERROR_ADDRESS );
		case LPC_TEC_DST_MAP:	return( LPC_ERROR_ADDRESS );
		case LPC_TEC_INV_CNT:	return( LPC_ERROR_COMMAND );
		case LPC_TEC_INV_SECT:	return( LPC_ERROR_SECTOR );
		case LPC_TEC_NOT_BLANK:	return( LPC_ERROR_FAIL );
		case LPC_TEC_NOT_PREP:	return( LPC_ERROR_LOCKED );
		case LPC_TEC_CMP_DIFF:	return( LPC_ERROR_FAIL );
		case LPC_TEC_BUSY:		return( LPC_ERROR_BUSY );
		case LPC_TEC_PARAM:		return( LPC_ERROR_COMMAND );
		case LPC_TEC_ADDR_ERR:	return( LPC_ERROR_ADDRESS );
		case LPC_TEC_ADDR_MAP:	return( LPC_ERROR_ADDRESS );
		case LPC_TEC_CMD_LOCK:	return( LPC_ERROR_LOCKED );
		case LPC_TEC_INV_LOCK:	return( LPC_ERROR_COMMAND );
		case LPC_TEC_INV_BAUD:	return( LPC_ERROR_COMMAND );
		case LPC_TEC_INV_STOP:	return( LPC_ERROR_COMMAND );
		case LPC_TEC_RD_PROT:	return( LPC_ERROR_PROTECT );
		default:				return( LPC_ERROR_UNKNOWN );
	}
}

/*
*	Loads the first 64 bytes of FLASh into the target's RAM @ LPC_CODE_BASE
*	~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/

static int	_lpc_TargetFirst64( LPC lpc )
{
char	buff[ sizeof( lpc_read_flash ) + 8 ];
int		xfer, retv;

	_lpc_Log( lpc, LPC_LOG_TARGET,
			  "Address below 0x40, uploading helper code" );

	memcpy( buff + 8, lpc_read_flash, sizeof( lpc_read_flash ) );
	buff[ 0 ] = ( lpc->tgt_chip->cpu->mem_ctr ) >>  0;
	buff[ 1 ] = ( lpc->tgt_chip->cpu->mem_ctr ) >>  8;
	buff[ 2 ] = ( lpc->tgt_chip->cpu->mem_ctr ) >> 16;
	buff[ 3 ] = ( lpc->tgt_chip->cpu->mem_ctr ) >> 24;
	buff[ 4 ] = ( lpc->tgt_chip->cpu->mem_usr ) >>  0;
	buff[ 5 ] = ( lpc->tgt_chip->cpu->mem_usr ) >>  8;
	buff[ 6 ] = ( lpc->tgt_chip->cpu->mem_usr ) >> 16;
	buff[ 7 ] = ( lpc->tgt_chip->cpu->mem_usr ) >> 24;

	xfer = lpc->tgt_chip->ram->ram_beg + LPC_CODE_BASE;
	retv = LPC_TargetMemWrite( lpc, xfer + 56, buff,
							   sizeof( lpc_read_flash ) + 8 );
	if ( retv < 0 ) return( retv );

	// Now we have to execute the code, that will read the first
	// 64 bytes of the FLASH into RAM.

	retv = LPC_TargetExecute( lpc, xfer + 64, 0 );
	if ( retv < 0 ) return( retv );

	// Give the code 2ms to execute, that should be more than enough.

	usleep( 2000 );
	return( 0 );
}

/*
*	Checks if the first sector of the FLASH is blank
*	~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*	Returns 1 if yes, 0 if no and <0 on error.
*/

static int	_lpc_TargetFirstSect( LPC lpc )
{
unsigned char	buff[ sizeof( lpc_blank_first ) + 12 ];
int				xfer, retv;

	_lpc_Log( lpc, LPC_LOG_TARGET,
			  "Blank check sector 0, uploading helper code" );

	memcpy( buff + 12, lpc_blank_first, sizeof( lpc_blank_first ) );
	buff[  0 ] = ( lpc->tgt_chip->cpu->mem_ctr ) >>  0;
	buff[  1 ] = ( lpc->tgt_chip->cpu->mem_ctr ) >>  8;
	buff[  2 ] = ( lpc->tgt_chip->cpu->mem_ctr ) >> 16;
	buff[  3 ] = ( lpc->tgt_chip->cpu->mem_ctr ) >> 24;
	buff[  4 ] = ( lpc->tgt_chip->cpu->mem_usr ) >>  0;
	buff[  5 ] = ( lpc->tgt_chip->cpu->mem_usr ) >>  8;
	buff[  6 ] = ( lpc->tgt_chip->cpu->mem_usr ) >> 16;
	buff[  7 ] = ( lpc->tgt_chip->cpu->mem_usr ) >> 24;
	buff[  8 ] = ( lpc->tgt_chip->rom->sec_siz[ 0 ] << 10 ) >>  0;
	buff[  9 ] = ( lpc->tgt_chip->rom->sec_siz[ 0 ] << 10 ) >>  8;
	buff[ 10 ] = ( lpc->tgt_chip->rom->sec_siz[ 0 ] << 10 ) >> 16;
	buff[ 11 ] = ( lpc->tgt_chip->rom->sec_siz[ 0 ] << 10 ) >> 24;

	xfer = lpc->tgt_chip->ram->ram_beg + LPC_CODE_BASE;
	retv = LPC_TargetMemWrite( lpc, xfer, buff, sizeof( buff ) );
	if ( retv < 0 ) return( retv );

	// Now we have to execute the code, that will read the first
	// 64 bytes of the FLASH into RAM.

	retv = LPC_TargetExecute( lpc, xfer + 12, 0 );
	if ( retv < 0 ) return( retv );

	// Give the code 4ms to execute, that should be more than enough.

	usleep( 2000 );

	// Read back the result

	retv = LPC_TargetMemRead( lpc, xfer, buff, 4 );

	if ( retv < 0 ) return( retv );

	if ( buff[ 0 ] || buff[ 1 ] || buff[ 2 ] || buff[ 3 ] ) {

		_lpc_Log( lpc, LPC_LOG_TARGET, "Sector 0 is not blank" );
		return( 0 );
	}
	else {

		_lpc_Log( lpc, LPC_LOG_TARGET, "Sector 0 is blank" );
		return( 1 );
	}
}

/*
*	Prepares a sector for erase or write
*	~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/

static int	_lpc_TargetPrepare( LPC lpc, int beg, int end )
{
char	buff[ 64 ];
int		retv;

	if ( ! lpc->tgt_chip ) return( LPC_ERROR_CHIP );
	_lpc_Log( lpc, LPC_LOG_TARGET, "Preparing sectors %d to %d", beg, end );
	sprintf( buff, "P %d %d\r\n", beg, end );
	if ( ( retv = _lpc_SerialPuts( lpc, buff ) ) < 0 ) return( retv );
	if ( ( retv = _lpc_TargetResult( lpc, 100 ) ) < 0 ) return( retv );
	return( _lpc_TargetErrorMap( retv ) );
}

/*
*	Unlocks the target
*	~~~~~~~~~~~~~~~~~~
*/

static int	_lpc_TargetUnlock( LPC lpc )
{
int		retv;

	if ( ! lpc->tgt_chip ) return( LPC_ERROR_CHIP );
	_lpc_Log( lpc, LPC_LOG_TARGET, "Unlocking target" );
	retv = _lpc_SerialPuts( lpc, lpc_lock );
	if ( retv < 0 ) return( retv );
	return( _lpc_TargetErrorMap( _lpc_TargetResult( lpc, 100 ) ) );
}


