    /*
main.c - vrflash main
Copyright (C) 2001  Jeff Carneal

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

#include "config.h"
#include <stdio.h>
#include <string.h>
#include <sys/time.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
#include "main.h"
#include "xmodem.h"
#include "etxack.h"
#include "port.h"
#include "progress.h"
#include "vrerror.h"
#include "vrfile.h"

const char *versionstr =
	"vrflash:  0.24 - 2002/10/30 - Copyright 2001 Jeff Carneal <jeff@apex.net>";
int do_portlog = 0;
extern VRFILE *first;

int
main (int argc, char **argv)
{
	int fd = 0;
	int ret = 0;
	int argval = 0;
	int logfile_fd = 0;
	int i = 0;
	char serialport[SMBUFSIZE];
	char buffer[BUFSIZE];
	char cmdline[BUFSIZE];
	char *restore =
		"$linux console=ttyS0,115200 init=/sbin/restore_defaults\r";
	long filesize = 0;
	VRFILE *cur = NULL;
	CONFIG conf;
	extern PM pmeter;
	extern char *optarg;
	extern int optind;

	memset (buffer, 0, BUFSIZE);
	memset (serialport, 0, SMBUFSIZE);
	logfile_fd = 0;

	/*
	 * Initialize config values
	 */
	memset (&conf, 0, sizeof (CONFIG));
	conf.chkpmon = 1;
	conf.xmodemload = 1;
	conf.chkromsize = 1;
	conf.chkkernsize = 1;

	/*
	 * Save command line for logging
	 */
	memset (cmdline, 0, BUFSIZE);
	cmdline[0] = '\n';
	for (i = 0; i < argc; i++)
	{
		if ((strlen (cmdline) + strlen (argv[i])) < BUFSIZE)
			sprintf (cmdline, "%s %s", cmdline, argv[i]);
	}

	while ((argval = getopt (argc, argv, "hlrs:t:vCDRX")) != EOF)
	{
		switch (argval)
		{
		case 'h':
		case 'v':
			usage ();
			break;
		case 'l':
			do_portlog = 1;
			if (!
			    (logfile_fd =
			     open (LOGFILE, O_CREAT | O_TRUNC | O_WRONLY,
				   0644)))
			{
				fprintf (stderr,
					 "Warning:  can't open %s for writing\n",
					 LOGFILE);
			}
			else
			{
				write (logfile_fd, versionstr,
				       strlen (versionstr));
				write (logfile_fd, cmdline, strlen (cmdline));
				write (logfile_fd, "\n\n", 2);
				close (logfile_fd);
			}
			break;
		case 'r':
			conf.restart = 1;
			break;
		case 's':
			conf.ttyport = strdup (optarg);
			break;
		case 't':
			conf.tmpdir = strdup (optarg);
			break;
		case 'C':
			conf.chkromsize = 0;
			conf.chkkernsize = 0;
			break;
		case 'D':
			conf.chkpmon = 0;
			break;
		case 'R':
			conf.restore_defaults = 1;
			break;
		case 'X':
			conf.xmodemload = 0;
			break;
		}
	}

	if (conf.ttyport)
		snprintf (serialport, SMBUFSIZE, "/dev/%s", conf.ttyport);
	else
		snprintf (serialport, SMBUFSIZE, "/dev/ttyS0");

	argc -= optind;
	argv += optind;

	if ((argc < 2) && (!conf.restore_defaults))
	{
		usage ();
	}

	fprintf (stderr, "\n");
	/*
	 * Create the list of files/offsets
	 * we need to transfer
	 */
	while (argv[0])
	{
		if (!argv[1])
		{
			vr_error ("Error:  no offset specified for '%s'\n",
				  argv[0]);
		}
		/*
		 * Create and initialize
		 * each new file struct
		 */
		cur = (VRFILE *) malloc (sizeof (VRFILE));
		if (!cur)
			vr_error ("Error:  out of memory");
		memset (cur, 0, sizeof (VRFILE));

		cur->filename = strdup (argv[0]);

		if (strncmp (argv[1], "kernel", 6) == 0)
		{
			cur->offset = 0x0;
		}
		else if (strncmp (argv[1], "pmon", 4) == 0)
		{
			cur->offset = 0xc00000;
		}
		else if ((strncmp (argv[1], "romdisk", 7) == 0) ||
			 (strncmp (argv[1], "rootdisk", 8) == 0))
		{
			cur->offset = 0x200000;
		}
		else if (strncmp (argv[1], "cramfs", 6) == 0)
		{
			cur->offset = 0x100000;
		}
		else if (strncmp (argv[1], "0x", 2) == 0)
		{
			argv[1] += 2;
			cur->offset = axtoi (argv[1]);
		}
		else
		{
			cur->offset = axtoi (argv[1]);
		}

		vrfile_chk (cur, &conf);
		vrfile_add (cur);

		argv += 2;
	}
	/* Debug Only   
	 * vrfile_list();
	 * exit(1); 
	 */

	/*
	 * Check for PMON overwrite
	 */
	if (conf.chkpmon)
		check_pmon ();

	/* 
	 * Open the serial port 
	 */
	fprintf (stderr, "Opening serial port...");
	if ((fd = port_open (serialport)) < 0)
	{
		vr_error ("Error: Unable to open port '%s'", serialport);
	}
	fprintf (stderr, "done\n");
	port_init (fd);

	/* 
	 * Wait here for first character
	 * from the serial port.  
	 */
	fprintf (stderr, "Waiting for input from port (CTRL-C to exit)...");
	port_send (fd, "\r", 1, NOPORTLOG);
	while (read (fd, buffer, 1) == 0) ;
	fprintf (stderr, "done\n\n");

	for (cur = first; cur; cur = cur->next)
	{

		/*
		 * Clear port and find PMON prompt
		 */
		port_getpmon (fd, buffer);

		if (conf.xmodemload)
		{
			fprintf (stderr, "Sending etxack start command...");
			port_send (fd, "load\r", 5, PORTLOG);
			fprintf (stderr, "done\n");

			port_readflush (fd);
			fprintf (stderr,
				 "Starting etxack send (xmodem-load)...");
			ex_sendfile (fd);
			fprintf (stderr, "done\n\n");

			conf.xmodemload = 0;
			port_getpmon (fd, buffer);
		}

		fprintf (stderr, "Sending xmodem start command...");
		port_send (fd, "g -e a0080274\r", 14, PORTLOG);
		fprintf (stderr, "done\n");

		fprintf (stderr, "Starting xmodem send - %s...\n",
			 cur->filename);
		filesize = xmodem_sendfile (cur->filename, fd);

		port_getpmon (fd, buffer);

		/*
		 * Issue flash command
		 */
		filesize = SECTOR_ALIGN (filesize);
		fprintf (stderr, "Sending flash command...");
		snprintf (buffer, BUFSIZE, "flash 80090000 %lx %lx\r",
			  filesize, cur->offset);
		port_send (fd, buffer, strlen (buffer), PORTLOG);
		port_readflush (fd);
		port_send (fd, "y\r", 2, PORTLOG);
		fprintf (stderr, "done\n");

		/*
		 * Setup progress meter for flash
		 */
		memset (&pmeter, 0, sizeof (PM));
		gettimeofday (&pmeter.start, NULL);
		pmeter.totalkbytes = (int) filesize / 1024;
		pm_showmeter (PM_START);

		while ((ret = port_readline (fd, buffer)) != -1)
		{
			if (strstr (buffer, "Programming block"))
			{
				/* vr3 sector size 0x20000 */
				pmeter.bytesdone += 131072;
				if (pmeter.bytesdone > filesize)
					pmeter.bytesdone = filesize;
				pm_showmeter (PM_UPDATE);
			}
			else if (strstr (buffer, "Error"))
			{
				fprintf (stderr, "\nWARNING:  '%s'\n",
					 buffer);
			}
			else if (strstr (buffer, prompt1))
			{
				break;
			}
			else if (strstr (buffer, prompt2))
			{
				break;
			}
		}
		if (ret == -1)
			vr_error ("Error: timeout reading from port");

		pm_showmeter (PM_END);
	}			/* for(cur=first; cur; cur=cur->next) */

	port_getpmon (fd, buffer);

	if (conf.restore_defaults)
	{
		// Code added by Sean Barnes for sdram PMON
		fprintf (stderr, "Unzipping kernel...");
		port_send (fd, "unzip\r", strlen ("unzip\r"), PORTLOG);
		while ((ret = port_readline (fd, buffer)) != -1)
		{
			if (strstr (buffer, prompt1))
			{
				break;
			}
	                else if (strstr (buffer, prompt2))
			{
				break;
			}
		}
		fprintf (stderr, "done\n");

		fprintf (stderr, "Restoring defaults...\n");
		port_send (fd, restore, strlen (restore), PORTLOG);
		while ((ret = port_readline (fd, buffer)) != -1)
		{
			fprintf (stderr, "%s\n", buffer);
			if (strstr (buffer, "Hard reset"))
			{
				break;
			}
		}
		fprintf (stderr, "\nvrflash completed\n");
	}
	else if (conf.restart)
	{
		// Code added by Sean Barnes for sdram PMON
		fprintf (stderr, "Unzipping kernel...");
		port_send (fd, "unzip\r", strlen ("unzip\r"), PORTLOG);
		while ((ret = port_readline (fd, buffer)) != -1)
		{
			if (strstr (buffer, prompt1))
			{
				break;
			}
			else if (strstr (buffer, prompt2))
			{
				break;
			}
		}
		fprintf (stderr, "done\n");

		fprintf (stderr, "Restarting system...");
		port_send (fd, "$linux\r", 7, PORTLOG);
		fprintf (stderr, "done\n");
	}

	close (fd);

	vrfile_cleanup (0);
	return 0;
}


void
usage (void)
{
	fprintf (stderr, "\n %s\n", versionstr);
	fprintf (stderr,
		 "\n Usage:  vrflash [OPTIONS] <file> <kernel|romdisk|cramfs|pmon|0xOffset> \n");
	fprintf (stderr,
		 "  [ -l ]                 -- "
		 "Capture input/output to ./capture.log\n");
	fprintf (stderr,
		 "  [ -r ]                 -- "
		 "Restart VR3 after loading\n");
	fprintf (stderr,
		 "  [ -R ]                 -- "
		 "Restore defaults after flash\n");
	fprintf (stderr,
		 "  [ -s <serial port> ]   -- "
		 "Serial port.  Default == ttyS0\n");
	fprintf (stderr,
		 "  [ -t <temp dir> ]      -- "
		 "Temp dir for split files.  Default==/tmp\n");
	fprintf (stderr,
		 "  [ -v (or -h) ]         -- " "Version and usage (this)\n");
	fprintf (stderr,
		 "  [ -C ]                 -- "
		 "Disable kernel/romdisk size check (expert-only!)\n");
	fprintf (stderr,
		 "  [ -D ]                 -- "
		 "Disable PMON overwrite check (expert-only!)\n");
	fprintf (stderr,
		 "  [ -X ]                 -- "
		 "Do not load xmodem-load.srec\n\n");
	exit (1);
}

/* 
 * I boosted this from a borland.com example
 * My version worked but was ugly...stepped on.
 * Kudos to the bad mother that came up with this
 */
long
axtoi (char *str)
{
	int n = 0;
	int m = 0;
	int count;
	long intValue = 0;
	int digit[10];
	char *ptr;

	ptr = str;
	if (!strncmp (str, "0x", 2))
		ptr += 2;

	while (ptr && (ptr[n]) && (n < 10))
	{
		if (ptr[n] > 0x29 && ptr[n] < 0x40)	/* if 0 to 9 */
			digit[n] = ptr[n] & 0x0f;
		else if (ptr[n] >= 'a' && ptr[n] <= 'f')	/* if a to f */
			digit[n] = (ptr[n] & 0x0f) + 9;
		else if (ptr[n] >= 'A' && ptr[n] <= 'F')	/* if A to F */
			digit[n] = (ptr[n] & 0x0f) + 9;
		else
			vr_error ("Error:  invalid offset '%s'", str);
		n++;
	}
	count = n;
	m = n - 1;
	n = 0;
	while (n < count)
	{
		/*
		 * digit[n] is value of hex digit at position n
		 * (m << 2) is the number of positions to shift
		 * OR the bits into return value
		 */
		intValue = intValue | (digit[n] << (m << 2));
		m--;
		n++;
	}
	return (intValue);
}


int
check_pmon (void)
{
	VRFILE *cur;
	int writesize;
	long i;

	/*
	 * * PMON ([bf]c00000-[bf]c7ffff)
	 */

	/*
	 * I check every 64k instead of
	 * every byte now.  Since PMON occupies
	 * 320K, I feel that's pretty safe
	 */
	for (cur = first; cur; cur = cur->next)
	{
		writesize = SECTOR_ALIGN (cur->size);
		for (i = cur->offset; i < writesize + cur->offset;
		     i += 0x10000)
		{
			/*fprintf(stderr, "Checking (0x%lx, 0x%lx)\n", writesize+cur->offset, i); */
			if ((i >= 0xc00000) && (i <= 0xc7ffff))
			{
				vr_error ("Warning:  PMON overwrite detected by '%s'.  Aborting. (0x%lx)\n", cur->filename);
			}
		}
		/*
		 * Check last byte to be flashed
		 * separately.  Just in case a few bytes
		 * slip in under the 0x10000 radar
		 */
		i = writesize + cur->offset - 1;
		/*fprintf(stderr, "Checking (0x%lx, 0x%lx)\n", writesize+cur->offset, i); */
		if ((i >= 0xc00000) && (i <= 0xc7ffff))
		{
			vr_error ("Warning:  PMON overwrite detected by '%s'.  Aborting.\n", cur->filename);
		}
	}
	return 0;
}
