/***********************************************************************
 *  avrp - Atmel AVR programming software to use with Atmel's
 *         standard 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"
#include "talk.h"

int InquireHW(struct ProgramInfo *PI)
	{
	int i, found, count, unknown = False;
	unsigned char SerBuff[256];
	struct ChipNode *CN;

	SerWriteByte(PI->Ser, 'L');
	SerReadData(PI->Ser, SerBuff, 1);
	if(InquireID(PI))
		{
		SerWriteByte(PI->Ser, 't');
		count = SerReadData(PI->Ser, SerBuff, 256);
		if(count)
			{
			if(count != 1)
				{
				if(!GET_ARG(PI->args, ARG_AVRTYPE))
					printf("Devices supported by programmer:\n");
				for(i = 0; SerBuff[i] != 0x00; i++)
					{
					if(i == (count - 1))
						{
						printf("Error: Device codes not terminated\n");
						return(False);
						}
					found = False;
					for(CN = PI->FirstChipNode; CN; CN = CN->Next)
						{
						if(CN->Code == SerBuff[i])
							{
							found = True;
							CN->Supported = True;
							if(!GET_ARG(PI->args, ARG_AVRTYPE))
								printf("   %s %s %s with %dKB flash (0x%02x)\n",
										 CN->VendorNode->Name,
										 CN->FamilyNode->Name, CN->Name,
										 CN->FamilyNode->FlashSize, (int)SerBuff[i]);
							}
						}
					if(!found)
						{
						if(!GET_ARG(PI->args, ARG_AVRTYPE))
							printf("   Unknown device code: 0x%02x\n", (int)SerBuff[i]);
						unknown = True;
						}
					}
				if(unknown && !GET_ARG(PI->args, ARG_AVRTYPE))
					printf("Warning: Connected programmer supports devices not supported by avrp\n"
					       "         You should update %s\n", (char *)GET_ARG(PI->args, ARG_DEFFILE));
				return(True);
				}
			else
				printf("Error: Your programmer doesn't suppport any chips at all\n");
			}
		else
			print_comm_err('t');
		}
	return(False);
	}



int InquireID(struct ProgramInfo *PI)
	{
	int count;
	unsigned char SerBuff[16];

	SerWriteByte(PI->Ser, 'p');
	if(SerReadData(PI->Ser, SerBuff, 1))
		{
		if((SerBuff[0] == 'S') || (SerBuff[0] == 'P'))
			{
			if(SerBuff[0] == 'S')
				{
				PI->ProgType = PROGTYPE_SER;
				printf("Serial mode programmer of type: ");
				}
			else
				{
				PI->ProgType = PROGTYPE_PAR;
				printf("Parallel mode programmer of type: ");
				}
			SerWriteByte(PI->Ser, 'S');
			count = SerReadData(PI->Ser, SerBuff, 15);
			if(count)
				{
				SerBuff[count] = '\0';
				printf("%s\n", SerBuff);
				SerWriteByte(PI->Ser, 'v');
				if(SerReadData(PI->Ser, SerBuff, 2) == 2)
					{
					printf("Hardware version: %c.%c", SerBuff[0], SerBuff[1]); 
					SerWriteByte(PI->Ser, 'V');
					if(SerReadData(PI->Ser, SerBuff, 2) == 2)
						{
						printf("   Firmware version: %c.%c\n", SerBuff[0], SerBuff[1]); 
						return(True);
						}
					else
						print_comm_err('V');
					}
				else
					print_comm_err('v');
				}
			else
				print_comm_err('S');
			}
		else
			printf("Error: Not a valid programmer type reported\n"
					 "       Possibly no programmer connected\n");
		}
	else
		print_comm_err('p');
	return(False);
	}


int DoTheTalk(struct ProgramInfo *PI)
	{
	int ok = False;
	unsigned char SerBuff[3];

	SerWriteByte(PI->Ser, 'T');
	SerWriteByte(PI->Ser, (unsigned char)PI->SelectedChipNode->Code);
	if((SerReadData(PI->Ser, SerBuff, 1)) && (SerBuff[0] == 13))
		{
		SerWriteByte(PI->Ser, 'P');
		if((SerReadData(PI->Ser, SerBuff, 1)) && (SerBuff[0] == 13))
			{
			SerWriteByte(PI->Ser, 's');
			if(SerReadData(PI->Ser, SerBuff, 3))
				{
				if((SerBuff[0] != 0xFF) || (SerBuff[1] != 0xFF) || (SerBuff[2] != 0xFF) || GET_ARG(PI->args, ARG_IGNORESIG))
					{
					if(CheckLockAndErase(PI, SerBuff))
						{
						if(CheckSig(PI, SerBuff))
							{
							if(GET_ARG(PI->args, ARG_PROG)|| GET_ARG(PI->args, ARG_READ)
							   || GET_ARG(PI->args, ARG_VERIFY)|| GET_ARG(PI->args, ARG_LOCKMODE)
							   || GET_ARG(PI->args, ARG_ENABLE) || GET_ARG(PI->args, ARG_DISABLE))
								{
								ok = DoMoreTalk(PI);
								}
							else
								printf("Missing argument: No action argument specified\n"
								       "   -p : Program\n"
								       "   -r : Read\n"
								       "   -v : Verify (can be used standalone or together with -p or -r)\n"
								       "   -l : Lockmode (use '-l list' to get a list of modes)\n"
								       "   --enable  : Fuses to enable\n"
								       "   --disable : Fuses to disable\n");
							}
						}
					}
				else
					printf("Error: There seems to be no chip inserted in the programmer\n"
					       "       Use -I to ignore this signaturecheck\n");
				}
			else
				print_comm_err('s');
			}
		else
			print_comm_err('P');
		SerWriteByte(PI->Ser, 'L');
		if(!((SerReadData(PI->Ser, SerBuff, 1)) && (SerBuff[0] == 13)))
			print_comm_err('L');
		}
	else
		print_comm_err('T');
	return(ok);
	}


int CheckLockAndErase(struct ProgramInfo *PI, unsigned char *SerBuff)
	{
	int ok;

	if((SerBuff[0] == 0x02) && (SerBuff[1] == 0x01) && (SerBuff[2] == 0x00))
		{
		ok = False;
		printf("Device is locked!\n");
		if(GET_ARG(PI->args, ARG_PROG))
			{
			if(Erase(PI))
				{
				SerWriteByte(PI->Ser, 's');
				if(SerReadData(PI->Ser, SerBuff, 3))
					{
					if((SerBuff[0] == 0x02) && (SerBuff[1] == 0x01) && (SerBuff[2] == 0x00))
						printf("Error: Failed to erase device!\n");
					else
						ok = True;
					}
				else
					{
					print_comm_err('s');
					return(False);
					}
				}
			else
				return(False);
			}
		else if(GET_ARG(PI->args, ARG_READ))
			printf("Error: Unable to read device\n");
		else if(GET_ARG(PI->args, ARG_VERIFY))
			printf("Error: Unable to verify device\n");
		if(!ok)
			DoFuses(PI);
		return(ok);
		}
	else if(GET_ARG(PI->args, ARG_PROG) && GET_ARG(PI->args, ARG_FLASH)
	        && PI->SelectedChipNode->FamilyNode->ArchNode->NeedFlashErase)
		return(Erase(PI));
	else
		return(True);
	}


int CheckSig(struct ProgramInfo *PI, unsigned char *Sig)
	{
	int ok = False, IntSig;
	struct ChipNode *CN, *FoundCN = NULL;
	struct VendorNode *VN, *FoundVN = NULL;
	struct FamilyNode *FN, *FoundFN = NULL;

	IntSig = Sig[0] | (Sig[1] << 8) | (Sig[2] << 16);
	if(IntSig == PI->SelectedChipNode->Sig)
		ok = True;
	else
		{
		printf("Warning: Signature does not match\n");
		for(CN = PI->FirstChipNode; CN; CN = CN->Next)
			if(IntSig == CN->Sig)
				{
				if(!FoundCN)
					printf("Signature in chip matched: %s\n", CN->Name);
				else
					printf("                           %s\n", CN->Name);
				FoundCN = CN;
				}
		if(!FoundCN)
			{
			printf("Inserted chip: Vendor: ");
			for(VN = PI->FirstVendorNode; VN; VN = VN->Next)
				if(VN->Code == Sig[2])
					{
					FoundVN = VN;
					break;
					}
			if(FoundVN)
				printf("%s", FoundVN->Name);
			else
				printf("Unknown (0x%02x)", (int)Sig[2]);
			printf("\n               Family: ");
			for(FN = PI->FirstFamilyNode; FN; FN = FN->Next)
				if(FN->Code == Sig[1])
					{
					FoundFN = FN;
					break;
					}
			if(FoundFN)
				printf("%s with %dk flash", FoundFN->Name, FoundFN->FlashSize);
			else
				printf("Unknown (0x%02x)", (int)Sig[1]);
			printf("\n               Part number: 0x%02x\n", (int)Sig[0]);
			}
		}
	if(!ok && GET_ARG(PI->args, ARG_IGNORESIG))
		{
		printf("Ignoring incorrect signature\n");
		ok = True;
		}
	return(ok);
	}


int DoMoreTalk(struct ProgramInfo *PI)
	{
	int ok = False;

	if(GET_ARG(PI->args, ARG_PROG))
		{
		if(GET_ARG(PI->args, ARG_FLASH) || GET_ARG(PI->args, ARG_EEPROM))
			{
			if(GET_ARG(PI->args, ARG_FLASH))
				{
				printf("Programming flash from file: %s ", (char *)GET_ARG(PI->args, ARG_FLASH));
				ok = Program(PI, WHAT_FLASH, DO_PROGRAM);
				}
			if(ok || !GET_ARG(PI->args, ARG_FLASH))
				if(GET_ARG(PI->args, ARG_EEPROM))
					{
					printf("Programming eeprom from file: %s ", (char *)GET_ARG(PI->args, ARG_EEPROM));
					ok = Program(PI, WHAT_EEPROM, DO_PROGRAM);
					}
			}
		else
			printf("Missing argument: One or more of these must be given:\n"
					 "   File to read flash data from:  -f <filename>\n"
					 "   File to read eeprom data from: -e <filename>\n");
		}
	else if(GET_ARG(PI->args, ARG_READ))
		{
		if(GET_ARG(PI->args, ARG_FLASH) || GET_ARG(PI->args, ARG_EEPROM))
			{
			if(GET_ARG(PI->args, ARG_FLASH))
				{
				printf("Reading flash to file: %s\n", (char *)GET_ARG(PI->args, ARG_FLASH));
				ok = Read(PI, WHAT_FLASH);
				}
			if(ok || !GET_ARG(PI->args, ARG_FLASH))
				if(GET_ARG(PI->args, ARG_EEPROM))
					{
					printf("Reading eeprom to file: %s\n", (char *)GET_ARG(PI->args, ARG_EEPROM));
					ok = Read(PI, WHAT_EEPROM);
					}
			}
		else
			printf("Missing argument: One or more of these must be given:\n"
					 "   File to write flash data to:  -f <filename>\n"
					 "   File to write eeprom data to: -e <filename>\n");
		}
	else
		ok = True;
	if(GET_ARG(PI->args, ARG_VERIFY) && (ok || !GET_ARG(PI->args, ARG_PROG)))
		{
		if(GET_ARG(PI->args, ARG_FLASH) || GET_ARG(PI->args, ARG_EEPROM))
			{
			if(GET_ARG(PI->args, ARG_FLASH))
				{
				printf("Verifying flash with: %s ", (char *)GET_ARG(PI->args, ARG_FLASH));
				ok = Program(PI, WHAT_FLASH, DO_VERIFY);
				}
			if(ok || !GET_ARG(PI->args, ARG_FLASH))
				if(GET_ARG(PI->args, ARG_EEPROM))
					{
					printf("Verifying eeprom with: %s ", (char *)GET_ARG(PI->args, ARG_EEPROM));
					ok = Program(PI, WHAT_EEPROM, DO_VERIFY);
					}
			}
		else
			printf("Missing argument: One or more of these must be given:\n"
					 "   File to verify flash data with:  -f <filename>\n"
					 "   File to verify eeprom data with: -e <filename>\n");
		}
	if(ok)
		ok = DoFuses(PI);
	return(ok);
	}


int Erase(struct ProgramInfo *PI)
	{
	unsigned char SerBuff;

	SerWriteByte(PI->Ser, 'e');
	printf("Erasing device...\n");
	if((SerReadData(PI->Ser, &SerBuff, 1)) && (SerBuff == 13))
		{
		SerWriteByte(PI->Ser, 'L');
		if((SerReadData(PI->Ser, &SerBuff, 1)) && (SerBuff == 13))
			{
			SerWriteByte(PI->Ser, 'P');
			if((SerReadData(PI->Ser, &SerBuff, 1)) && (SerBuff == 13))
				return(True);
			else
				print_comm_err('P');
			}
		else
			print_comm_err('L');
		}
	else
		print_comm_err('e');
	return(False);
	}


int DoFuses(struct ProgramInfo *PI)
	{
	unsigned char SerBuff, mask = 0xff;
	int i, found, lockmode, lockmode_list = False;
	struct FuseBitNode *FBN;
	struct LockBitNode *LBN;
	struct data_list *data;

	if(PI->ProgType == PROGTYPE_PAR)
		{
		SerWriteByte(PI->Ser, 'F');
		if(SerReadData(PI->Ser, &SerBuff, 1))
			{
			if(PI->SelectedChipNode->FuseType != 0)
				{
				printf("Reading fuses...\n");
				for(FBN = PI->SelectedChipNode->FuseNode->FirstFuseBitNode; FBN; FBN = FBN->Next)
					{
					if(~SerBuff & (1 << FBN->BitNum))
					        {
						printf("   Enabled:  %-8s (%s)\n", FBN->ShortName, FBN->LongName);
						mask &= ~(1 << FBN->BitNum);
						}
					else
						printf("   Disabled: %-8s (%s)\n", FBN->ShortName, FBN->LongName);
					}
				}
			if(PI->SelectedChipNode->LockType != 0)
				{
				printf("Reading lockmode...\n");
				lockmode = 0;
				for(i = 0; i < PI->SelectedChipNode->LockNode->NumberOfBits; i++)
					if((1 << ((PI->SelectedChipNode->LockNode->ReadBits >> (i * 4)) & 0x07)) & SerBuff)
						lockmode |= 1 << i;
				for(LBN = PI->SelectedChipNode->LockNode->FirstLockBitNode; LBN; LBN = LBN->Next)
					if(lockmode == LBN->Mask)
						break;
				if(LBN)
					printf("   Mode %d (%s)\n", LBN->Mode, LBN->LongName);
				else
					printf("Warning: Found no matching lockmode!\n");
				}
			}
		else
			{
			print_comm_err('F');
			return(False);
			}
		}

	if(GET_ARG(PI->args, ARG_ENABLE) || GET_ARG(PI->args, ARG_DISABLE))
		{
		if(PI->ProgType == PROGTYPE_PAR)
			{
			printf("Programming fuses...\n");
			for(data = GET_ARG(PI->args, ARG_ENABLE); data; data = data->next)
			        {
				found = False;
				for(FBN = PI->SelectedChipNode->FuseNode->FirstFuseBitNode; FBN; FBN = FBN->Next)
				        {
					if(!nocase_strcmp(data->data, FBN->ShortName))
					        {
						found = True;
						printf("   Enabling  %-8s (%s)\n", FBN->ShortName, FBN->LongName);
						mask &= ~(1 << FBN->BitNum);
						break;
						}
					}
				if(!found)
				        printf("Warning: Unknown fuse: %s\n", (char *)data->data);
				}
			for(data = GET_ARG(PI->args, ARG_DISABLE); data; data = data->next)
			        {
				found = False;
				for(FBN = PI->SelectedChipNode->FuseNode->FirstFuseBitNode; FBN; FBN = FBN->Next)
				        {
					if(!nocase_strcmp(data->data, FBN->ShortName))
					        {
						found = True;
						printf("   Disabling %-8s (%s)\n", FBN->ShortName, FBN->LongName);
						mask |= 1 << FBN->BitNum;
						break;
						}
					}
				if(!found)
				        printf("Warning: Unknown fuse: %s\n", (char *)data->data);
				}
			for(FBN = PI->SelectedChipNode->FuseNode->FirstFuseBitNode; FBN; FBN = FBN->Next)
			        {
				found = False;
				for(data = GET_ARG(PI->args, ARG_ENABLE); data; data = data->next)
				        {
					if(!nocase_strcmp(data->data, FBN->ShortName))
					        {
						found = True;
						break;
						}
					}
				if(!found)
				        for(data = GET_ARG(PI->args, ARG_DISABLE); data; data = data->next)
					        {
						if(!nocase_strcmp(data->data, FBN->ShortName))
						        {
							found = True;
							break;
							}
						}
				if(!found)
				        {
					if(~mask & (1 << FBN->BitNum))
					        printf("   Enabling %-8s (%s)\n", FBN->ShortName, FBN->LongName);
					else
					        printf("   Disabling %-8s (%s)\n", FBN->ShortName, FBN->LongName);
					}
				}
			SerWriteByte(PI->Ser, 'f');
			SerWriteByte(PI->Ser, mask);
			if(!((SerReadData(PI->Ser, &SerBuff, 1)) && (SerBuff == 13)))
			        {
				print_comm_err('f');
				return(False);
				}
			}
		else
			printf("Warning: Fuse programming is not supported by serial mode programmers\n");
		}

	if(GET_ARG(PI->args, ARG_LOCKMODE))
		{
		if(!nocase_strcmp((char *)GET_ARG(PI->args, ARG_LOCKMODE), "list"))
			lockmode_list = True;
		else
			{
			printf("Programming lockmode...\n");
			for(LBN = PI->SelectedChipNode->LockNode->FirstLockBitNode; LBN; LBN = LBN->Next)
				if(LBN->Mode == atoi(GET_ARG(PI->args, ARG_LOCKMODE)))
					break;
			if(LBN)
				{
				printf("   Mode %d (%s)\n", LBN->Mode, LBN->LongName);
				mask = 0xff;
				for(i = 0; i < PI->SelectedChipNode->LockNode->NumberOfBits; i++)
					if(!(LBN->Mask & (1 << i)))
						mask &= ~(1 << ((PI->SelectedChipNode->LockNode->WriteBits >> (i * 4)) & 0x07));
				SerWriteByte(PI->Ser, 'l');
				SerWriteByte(PI->Ser, mask);
				if(!((SerReadData(PI->Ser, &SerBuff, 1)) && (SerBuff == 13)))
					{
					print_comm_err('l');
					return(False);
					}
				}
			else
				{
				lockmode_list = True;
				printf("Warning: Unsupported lockmode %s\n", (char *)GET_ARG(PI->args, ARG_LOCKMODE));
				}
			}
		if(lockmode_list)
			{
			printf("Available lockmodes:\n");
			for(LBN = PI->SelectedChipNode->LockNode->FirstLockBitNode; LBN; LBN = LBN->Next)
				printf("   Mode %d: %s\n", LBN->Mode, LBN->LongName);
			}
		}
	return(True);
	}


void print_comm_err(char comm)
	{
	printf("Error: No response to command: ");
	switch(comm)
		{
		case 'P': printf("Enter programming mode"); break;
		case 'A': printf("Set address"); break;
		case 'c': printf("Write program memory, low byte"); break;
		case 'C': printf("Write program memory, high byte"); break;
		case 'R': printf("Read program memory"); break;
		case 'D': printf("Write data memory"); break;
		case 'd': printf("Read data memory"); break;
		case 'e': printf("Chip erase"); break;
		case 'l': printf("Write lock bits"); break;
		case 'f': printf("Write fuse bits"); break;
		case 'F': printf("Read fuse and lock bits"); break;
		case 'L': printf("Leave programming mode"); break;
		case 'T': printf("Select device type"); break;
		case 's': printf("Read signature bytes"); break;
		case 't': printf("Return supported device codes"); break;
		case 'S': printf("Return software identifier"); break;
		case 'V': printf("Return software version"); break;
		case 'v': printf("Return hardware version"); break;
		case 'p': printf("Return programmer type"); break;
		case 'x': printf("Set LED"); break;
		case 'y': printf("Clear LED"); break;
		}
	printf(" (%c)\n", comm);
	}
