/***********************************************************************
 *  avrp - Atmel AVR programming software to use with Atmel's
 *         serial-port programmers.
 *  Copyright (C) 1997-1998 Jon Anders Haugum
 *
 *  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; see the file COPYING.  If not, write to
 *  the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 *  Boston, MA 02111-1307, USA.
 *
 *
 *  Author of avrp can be reached at:
 *     email: jonah@colargol.tihlde.hist.no
 *     www: http://www.colargol.tihlde.hist.no/~jonah/el/avrp.html
 *     Postal address: Jon Anders Haugum
 *                     Øvre Møllenbergsgt 52
 *                     7014 Trondheim
 */

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

#include "avrp.h"


const char *Title = "avrp 1.0 beta 3 (28 September 1998)\n"
                    "Copyright (C) 1997-1998 Jon Anders Haugum\n"
                    "   avrp comes with NO WARRANTY, to the extent permitted by law.\n"
                    "   You may redistribute copies of avrp under the terms\n"
                    "   of the GNU General Public License.\n"
                    "   For more information about these matters, see the files named COPYING.\n";

const char *Usage =
#ifdef __amigaos__
	"usage: avrp [-s <serial device:serial unit>] [-a <chip type>] [-prv]\n"
#else
	"usage: avrp [-s <serial port>] [-a <chip type>] [-prv]\n"
#endif
	"            [-f <filename>] [-e <filename>] [-l <lockmode>]\n"
        "            [--enable <fuse(s)>] [--disable <fuse(s)>]\n"
	"            [-d <deffile>] [-qIh] [--version] [--help]\n"
	"\n"
#ifdef __amigaos__
	"   -s : Specifies which serial port and unit to use (e.g. serial.device:0)\n"
#elif _WIN32
	"   -s : Specifies which serial port to use (e.g. COM2)\n"
#else
	"   -s : Specifies which serial port to use (e.g. /dev/ttyS1)\n"
#endif
	"   -a : Specifies chip to program/read (use '-a list' to get a list)\n"
	"   -p : Program a chip (-f or -e must be specified)\n"
	"   -r : Read a chip    (-f or -e must be specified)\n"
	"   -v : Verify         (-f or -e must be specified)\n"
	"   -f : Flashfile to write/read\n"
	"   -e : Eepromfile to write/read\n"
	"   -l : Lockmode (use '-l list' to get a list of modes)\n"
	"   -q : Quiet. Do not print out progress (little speedup)\n"
	"   -d : Specifies name and path to avrp.def\n"
	"   -I : Ignore chip signature\n"
	"   --enable   : Enable fuse(s)\n"
	"   --disable  : Disable fuse(s)\n"
	"   --autoinc  : Programmer support automatic address incrementation\n"
	"   --version  : Version information\n"
	"   --help, -h : This help text\n"
	"\n"
	"Report bugs to jonah@colargol.tihlde.hist.no\n";


int main(int argc, char *argv[])
	{
	int ShowUsage = False;
	char *temp_env;
	struct ProgramInfo *PI;
	struct args *args;

	args = alloc_args(ARG_COUNT);
	if(args)
		{
		define_arg(args, ARG_SERIALPORT, ARGTYPE_STRING,       's', NULL,      getenv("AVRP_SERIALPORT"));
		define_arg(args, ARG_AVRTYPE   , ARGTYPE_STRING,       'a', NULL,      getenv("AVRP_AVRTYPE"));
		define_arg(args, ARG_PROG      , ARGTYPE_BOOLEAN,      'p', NULL,      False);
		define_arg(args, ARG_READ      , ARGTYPE_BOOLEAN,      'r', NULL,      False);
		define_arg(args, ARG_VERIFY    , ARGTYPE_BOOLEAN,      'v', NULL,      False);
		define_arg(args, ARG_FLASH     , ARGTYPE_STRING,       'f', NULL,      NULL);
		define_arg(args, ARG_EEPROM    , ARGTYPE_STRING,       'e', NULL,      NULL);
		define_arg(args, ARG_QUICK     , ARGTYPE_BOOLEAN,      'q', NULL,      False);
		temp_env = getenv("AVRP_DEFFILE");
		define_arg(args, ARG_DEFFILE   , ARGTYPE_STRING,       'd', NULL,      temp_env ? temp_env : DEFFILENAME);
		define_arg(args, ARG_IGNORESIG , ARGTYPE_BOOLEAN,      'I', NULL,      False);
		define_arg(args, ARG_LOCKMODE  , ARGTYPE_STRING,       'l', NULL,      NULL);
		define_arg(args, ARG_ENABLE    , ARGTYPE_STRING_MULTI,   0, "enable",  NULL);
		define_arg(args, ARG_DISABLE   , ARGTYPE_STRING_MULTI,   0, "disable", NULL);
		define_arg(args, ARG_AUTOINC   , ARGTYPE_BOOLEAN,        0, "autoinc", False);
		define_arg(args, ARG_VER       , ARGTYPE_BOOLEAN,        0, "version", False);
		define_arg(args, ARG_HELP      , ARGTYPE_BOOLEAN,      'h', "help",    False);
		if(read_args(args, argc, argv))
			{
			if(!GET_ARG(args, ARG_HELP) && ((argc != 1) || GET_ARG(args, ARG_SERIALPORT)))
				{
				if(!GET_ARG(args, ARG_VER))
					{
					PI = GetPI(args);
					if(PI)
						{
						if(GET_ARG(args, ARG_SERIALPORT))
							{
							PI->Ser = OpenSer((char *)GET_ARG(args, ARG_SERIALPORT));
							if(PI->Ser)
								{
								if(InquireHW(PI))
									{
									if(DetectAVRType(PI))
										DoTheTalk(PI);
									}
								else
									printf("%s: No programmer found\n", (char *)GET_ARG(args, ARG_SERIALPORT));
								CloseSer(PI->Ser);
								}
							}
						else
							printf("Missing argument: No serial port specified\n"
#ifdef __amigaos__
									 "Use: -s <serial device:serial unit>\n"
#else
									 "Use: -s <serial port>\n"
#endif
									 "Or set the environment variable: AVRP_SERIALPORT\n");
						FreePI(PI);
						}
					}
				else
					printf("%s", Title);
				}
			else
				ShowUsage = True;
			}
		free_args(args);
		}
	else
		{
		ShowUsage = True;
		printf("\n");
		}
	if(ShowUsage)
		{
		if(argc == 1)
			printf("%s\n", Title);
		printf("%s", Usage);
		}
	exit(EXIT_SUCCESS);
	}


int DetectAVRType(struct ProgramInfo *PI)
	{
	int several_match = False, chiplist = False;
	struct ChipNode *CN;

	if(GET_ARG(PI->args, ARG_AVRTYPE))
		{
		if(nocase_strcmp((char *)GET_ARG(PI->args, ARG_AVRTYPE), "list"))
			{
			for(CN = PI->FirstChipNode; CN; CN = CN->Next)
				if(nocase_strstr(CN->Name, (char *)GET_ARG(PI->args, ARG_AVRTYPE)))
					{
					if(PI->SelectedChipNode)
						several_match = True;
					PI->SelectedChipNode = CN;
					printf("Selected chip: %s %s %s with %dKB flash\n", CN->VendorNode->Name,
					       CN->FamilyNode->Name, CN->Name, CN->FamilyNode->FlashSize);
					}
			}
		else
			chiplist = True;
		if(several_match)
			printf("Error: There were several chips that matched: %s\n"
			       "       You must be more specific\n", (char *)GET_ARG(PI->args, ARG_AVRTYPE));
		else if(PI->SelectedChipNode)
			{
			if(PI->SelectedChipNode->Supported)
				return(True);
			else
				printf("Error: %s is not supported by your programmer\n", PI->SelectedChipNode->Name);
			}
		else
			{
			if(!chiplist)
				printf("Error: Unable to find a match to '%s' in %s\n", (char *)GET_ARG(PI->args, ARG_AVRTYPE),
				       (char *)GET_ARG(PI->args, ARG_DEFFILE));
			printf("The following chips are defined:\n");
			for(CN = PI->FirstChipNode; CN; CN = CN->Next)
				{
				if(CN->Supported)
					printf("   %s (%s %s with %d kB flash)\n", CN->Name, CN->VendorNode->Name,
					       CN->FamilyNode->Name, CN->FamilyNode->FlashSize);
				else
					printf("   %s (%s %s with %d kB flash) Not supported by programmer\n",
					       CN->Name, CN->VendorNode->Name, CN->FamilyNode->Name,
					       CN->FamilyNode->FlashSize);
				}
			}
		}
	else
		{
		printf("Missing argument: Type of AVR must be specified\n"
		       "Use: -a <AVR type>\n"
		       "Or set the environment variable: AVRP_AVRTYPE\n"
		       "Shortest unique abbreviation can be used\n"
		       "List of all supported chips can be obtained with: -a list\n");
		}
	return(False);
	}


struct ProgramInfo *GetPI(struct args *args)
	{
	int ok = False;
	struct ProgramInfo *PI;
	struct ChipNode *CN;
	struct VendorNode *VN;
	struct FamilyNode *FamN;
	struct ArchNode *AN;
	struct FuseNode *FuseN;
	struct LockNode *LN;

	PI = (struct ProgramInfo *)calloc(1, sizeof(struct ProgramInfo));
	if(PI)
		{
		PI->args = args;
		if(ReadDefFile(PI))
			{
			ok = True;
			for(FamN = PI->FirstFamilyNode; FamN; FamN = FamN->Next)
				{
				for(AN = PI->FirstArchNode; AN; AN = AN->Next)
					if(!strcmp(FamN->Name, AN->Name))
						{
						FamN->ArchNode = AN;
						break;
						}
				if(!FamN->ArchNode)
					{
					ok = False;
					printf("Error: %s: Can't find architecture %s\n",
					       (char *)GET_ARG(PI->args, ARG_DEFFILE), FamN->Name);
					}
				}
			for(CN = PI->FirstChipNode; CN; CN = CN->Next)
				{
				for(VN = PI->FirstVendorNode; VN; VN = VN->Next)
					if(VN->Code == ((CN->Sig >> 16) & 0xFF))
						{
						CN->VendorNode = VN;
						break;
						}
				for(FamN = PI->FirstFamilyNode; FamN; FamN = FamN->Next)
					if(FamN->Code == ((CN->Sig >> 8) & 0xFF))
						{
						CN->FamilyNode = FamN;
						break;
						}
				if(!(CN->VendorNode && CN->FamilyNode))
					{
					ok = False;
					printf("Error: %s: No vendor and family matched the signature of %s\n",
					       (char *)GET_ARG(PI->args, ARG_DEFFILE), CN->Name);
					}
				else if(!CN->VendorNode)
					{
					ok = False;
					printf("Error: %s: No vendor matched the signature of %s\n",
					       (char *)GET_ARG(PI->args, ARG_DEFFILE), CN->Name);
					}
				else if(!CN->FamilyNode)
					{
					ok = False;
					printf("Error: %s: No family matched the signature of %s\n",
					       (char *)GET_ARG(PI->args, ARG_DEFFILE), CN->Name);
					}
				if(CN->FuseType != 0)
					{
					for(FuseN = PI->FirstFuseNode; FuseN; FuseN = FuseN->Next)
						if(FuseN->Num == CN->FuseType)
							{
							CN->FuseNode = FuseN;
							break;
							}
					if(!CN->FuseNode)
						{
						ok = False;
						printf("Error: %s: Can't find fusetype %d for %s\n",
						       (char *)GET_ARG(PI->args, ARG_DEFFILE), CN->FuseType, CN->Name);
						}
					}
				if(CN->LockType != 0)
					{
					for(LN = PI->FirstLockNode; LN; LN = LN->Next)
						if(LN->Num == CN->LockType)
							{
							CN->LockNode = LN;
							break;
							}
					if(!CN->LockNode)
						{
						ok = False;
						printf("Error: %s: Can't find locktype %d for %s\n",
						       (char *)GET_ARG(PI->args, ARG_DEFFILE), CN->LockType, CN->Name);
						}
					}
				}
			}
		if(!ok)
			{
			FreePI(PI);
			PI = NULL;
			}
		}
	else
		printf("Error: Unable to allocate memory\n");
	return(PI);
	}


void FreePI(struct ProgramInfo *PI)
	{
	void *Node;
	struct ChipNode *CN;
	struct VendorNode *VN;
	struct FamilyNode *FamN;
	struct ArchNode *AN;
	struct FuseNode *FuseN;
	struct FuseBitNode *FBN;
	struct LockNode *LN;
	struct LockBitNode *LBN;

	for(CN = PI->FirstChipNode; CN;)
		{
		if(CN->Name) free(CN->Name);
		Node = CN;
		CN = CN->Next;
		free(Node);
		}
	for(VN = PI->FirstVendorNode; VN;)
		{
		if(VN->Name) free(VN->Name);
		Node = VN;
		VN = VN->Next;
		free(Node);
		}
	for(FamN = PI->FirstFamilyNode; FamN;)
		{
		if(FamN->Name) free(FamN->Name);
		Node = FamN;
		FamN = FamN->Next;
		free(Node);
		}
	for(AN = PI->FirstArchNode; AN;)
		{
		if(AN->Name) free(AN->Name);
		Node = AN;
		AN = AN->Next;
		free(Node);
		}
	for(FuseN = PI->FirstFuseNode; FuseN;)
		{
		for(FBN = FuseN->FirstFuseBitNode; FBN;)
			{
			if(FBN->ShortName) free(FBN->ShortName);
			if(FBN->LongName) free(FBN->LongName);
			Node = FBN;
			FBN = FBN->Next;
			free(Node);
			}
		Node = FuseN;
		FuseN = FuseN->Next;
		free(Node);
		}
	for(LN = PI->FirstLockNode; LN;)
		{
		for(LBN = LN->FirstLockBitNode; LBN;)
			{
			if(LBN->LongName) free(LBN->LongName);
			Node = LBN;
			LBN = LBN->Next;
			free(Node);
			}
		Node = LN;
		LN = LN->Next;
		free(Node);
		}
	free(PI);
	}





