/*******************************************************************************
 *	PicProgrammer.c	Serial port PIC programmer using BEAM PicBootLoader
 *			T.Barnaby,	Beam Ltd,	2008-02-29
 *******************************************************************************
 *
 * Version:	1.0.0
 */
#include <BSerial.h>
#include <BFile.h>
#include <iostream>
#include <getopt.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <HexFile.h>

typedef unsigned char	uchar;
typedef unsigned short	ushort;
typedef unsigned int	uint;

using namespace std;

enum Mode { Beam, Tiny };

void hd8(void* data, int n){
	unsigned char* d = (unsigned char*)data;
	int	i;
	
	for(i = 0; i < n; i++){
		printf("%2.2x ", *d++);
		if((i & 0xF) == 0xF)
			printf("\n");
	}
	printf("\n");
}

int writeBoot(BSerial& pic){
	uchar	d;
	
	d = 'B';
	pic.write(&d, 1);
	pic.read(&d, 1);
	if(d != 'B'){
		cerr << "Communications error\n";
		return 1;
	}
	
	return 0;
}

int writeDataBeam(BSerial& pic, uchar addressSpace, ushort address, char* data, uint number){
	uchar	d;
	
//	printf("Write: Address: %4.4x Data: %4.4x\n", address, data);
	d = 'W';
	pic.write(&d, 1);
	d = addressSpace;
	pic.write(&d, 1);
	d = address;
	pic.write(&d, 1);
	d = address >> 8;
	pic.write(&d, 1);

	pic.write(data, number);

	pic.read(&d, 1);
	if(d != 'W'){
		cerr << "Communications error\n";
		return 1;
	}
	
	return 0;
}

int writeDataTiny(BSerial& pic, uchar addressSpace, ushort address, char* data, uint number){
	uchar	d;
	uint	checksum = 0;
	int	i;
	
//	printf("Write: Address: %4.4x NBytes: %d\n", address, number);

	d = 0x00; checksum += d; pic.write(&d, 1);
	d = (address >> 8) & 0xFF; checksum += d; pic.write(&d, 1);
	d = (address) & 0xFF; checksum += d; pic.write(&d, 1);
	d = number; checksum += d; pic.write(&d, 1);

	for(i = 0; i < number; i++){
		d = data[i]; checksum += d; pic.write(&d, 1);
	}

	d = (-checksum) & 0xFF;
	pic.write(&d, 1);
	pic.flush();
	
	while(pic.read(&d, 1) != 1);
	if(d != 'K'){
		cerr << "Communications error\n";
		printf("Returned: %x %c\n", d, d);
		return 1;
	}
	
	return 0;
}

int readData(BSerial& pic, uchar addressSpace, ushort address, char* buffer, uint number){
	uchar	d;

//	printf("Read: Address: %u\n", address);
	d = 'R';
	pic.write(&d, 1);
	d = addressSpace;
	pic.write(&d, 1);
	d = address;
	pic.write(&d, 1);
	d = address >> 8;
	pic.write(&d, 1);

	pic.read(&d, 1);
	if(d != 'R'){
		cerr << "Communications error\n";
		return 1;
	}
	
	pic.read(buffer, 64);
	
	return 0;
}

int serialGetChar(BSerial& serial, char* ch, int timeout){
	while(timeout--){
		if(serial.rxNbytes())
			return serial.read(ch, 1);
		usleep(100000);
	}
	return -1;
}

void usage(void) {
	cerr << "Usage:\tpicProgramer [options]\n";
	cerr << " -help             - Help on command line parameters\n";
	cerr << " -i                - Information\n";
	cerr << " -m <mode>         - BootLoader mode: Tiny or Beam\n";
	cerr << " -w <fileName>     - Program with file *.hex\n";
	cerr << " -r <fileName>     - Read into raw binary file\n";
	cerr << " -d <devName>      - Serial device name (defailt /dev/ttyUSB0)\n";
}

static struct option options[] = {
		{ "?",			0, NULL, 0 },
		{ "h",			0, NULL, 0 },
		{ "help",		0, NULL, 0 },
		{ "m",			1, NULL, 0 },
		{ "w",			1, NULL, 0 },
		{ "r",			1, NULL, 0 },
		{ "i",			0, NULL, 0 },
		{ "d",			1, NULL, 0 },
		{ 0,0,0,0 }
};

int main(int argc, char** argv){
	BSerial		pic;
	BError		err;
	int		nb;
	uchar		ch;
	int		optIndex = 0;
	int		c;
	BString		s;
	int		doInfo = 0;
	BString		programFileName;
	BString		readFileName;
	BFile		file;
	char		data[512];
	unsigned short	address;
	BString		serialDev = "/dev/ttyS0";
	BString		mode = "Tiny";

	while((c = getopt_long_only(argc, argv, "", options, &optIndex)) == 0){
		s = options[optIndex].name;
		if(s == "m"){
			mode = optarg;
		}
		else if(s == "w"){
			programFileName = optarg;
		}
		else if(s == "r"){
			readFileName = optarg;
		}
		else if(s == "i"){
			doInfo = 1;
		}
		else if(s == "d"){
			serialDev = optarg;
		}
		else {
			usage();
			return 1;
		}
	}
	
	if(optind != argc){
		usage();
		return 1;
	}

{
	HexFile		file;

	if(mode == "Tiny")
		file.setAddressRange(0x0000, 0x8000 - 0x200);
	else
		file.setAddressRange(0x0800, 0x8000);

	if(err = file.read(programFileName)){
		cerr << "Error unable to open " << programFileName << ": " << err.getString() << "\n";
		return 1;
	}

	file.printInfo();
}

	if(err = pic.open(serialDev)){
		cerr << "Error unable to access " << serialDev << ": " << err.getString() << "\n";
		return 1;
	}
	
	pic.raw();
//	pic.baud(2400);
	pic.baud(4800);
	pic.config(8, 1);
	pic.setFlow(BSerial::NONE);
	pic.block(1);
	pic.sync(1);

#ifdef ZAP
	while(1){
		pic.read(&ch, 1);
		printf("Char: %x\n", ch);
	}
#endif

#ifdef ZAP
	chtx = 'A';
	while(1){
		printf("WriteChar: %x\n", chtx);
		pic.write(&chtx, 1);
		pic.read(&ch, 1);
		printf("Char: %x\n", ch);
		chtx++;
		sleep(1);
	}
	pause();
#endif

	if(mode == "Beam"){
		// Wait for "Boot"
		printf("Waiting for 'BOOT' message\n");
		while(1){
			pic.read(&ch, 1);
			if(ch != 'B')
				continue;
			pic.read(&ch, 1);
			if(ch != 'O')
				continue;
			pic.read(&ch, 1);
			if(ch != 'O')
				continue;
			pic.read(&ch, 1);
			if(ch != 'T')
				continue;
			break;
		}

		printf("Got BOOT message: Setting PG\n");
		pic.write("P", 1);

		// Test for correct program
		ch = 'I';
		pic.write(&ch, 1);

		pic.read(&ch, 1);
		if(ch != 'I'){
			fprintf(stderr, "Communications error: Read: 0x%x\n", ch);
			return 1;
		}
		if((nb = pic.read(data, 4)) != 4){
			printf("nb: %d %s\n", nb, strerror(errno));
			cerr << "Communications error\n";
			return 1;
		}
		data[nb] = '\0';
		if(strcmp(data, "PP10")){
			cerr << "PIC program does not return PP10: Got: " << data << "\n";
			return 1;
		}
	}

	if(mode == "Tiny"){
		char	cpuType;
			
		pic.baud(19200);
		pic.block(1);
		pic.flush();

		// Wait for "Boot"
		printf("Waiting for BOOT response message\n");
		while(1){
//			printf("Send Code:\n");
			ch = 0xC1;	
			pic.write(&ch, 1);
			pic.flush();

//			sleep(1);
			cpuType = 0x00;
			if(serialGetChar(pic, &cpuType, 5) != 1){
				sleep(1);
				continue;
			}

			if(serialGetChar(pic, (char*)&ch, 5) != 1){
				sleep(1);
				continue;
			}
			if(ch == 'K')
				break;
			usleep(500000);
		}

		pic.block(1);
		printf("Got BOOT responce: CPU type: %x\n", cpuType);
	}

	if(doInfo){
	}

	if(readFileName != ""){
		if(mode != "Beam"){
			cerr << "Error unable to read: not supported\n";
			return 1;
		}
		
		if(err = file.open(readFileName, "w")){
			cerr << "Error unable to open " << readFileName << ": " << err.getString() << "\n";
			return 1;
		}

		address = 0x800;

		printf("Read: Address: %4.4x\n", address);
		memset(data, 0, sizeof(data));
		readData(pic, 0, address, data, 64);
		hd8(data, 64);
	}

	if(programFileName != ""){
		HexFile		file;
		uint32_t	resetAddress = (0x8000 - 200 + 8) / 2;
		char		resetVector[64];
		char		newResetVector[8];

		if(mode == "Tiny")
			file.setAddressRange(0x0000, 0x8000 - 200);
		else
			file.setAddressRange(0x0800, 0x8000);

		if(err = file.read(programFileName)){
			cerr << "Error unable to open " << programFileName << ": " << err.getString() << "\n";
			return 1;
		}
		
		file.printInfo();
		
		printf("ProgramStart: 0x%x ProgramSize: 0x%x\n", file.programLowAddress(), file.programSize());

		if(mode == "Tiny"){
			// Save Reset vector
			memset(resetVector, 0xFF, sizeof(resetVector));
			file.readProgramData(0, &resetVector[64 - 8], 8);
			
			// Set new reset vector
			newResetVector[0] = resetAddress & 0xFF;
			newResetVector[1] = 0xEF;
			newResetVector[2] = (resetAddress >> 8) & 0xFF;
			newResetVector[3] = 0xF0 + ((resetAddress >> 16) & 0x0F);
			file.writeProgramData(0, newResetVector, 4);
		}

		address = file.programLowAddress();
		for(int i = 0; i < file.programSize(); i += 64, address += 64){
			file.readProgramData(address, data, 64);
			printf("Address: %x:\n", address); hd8(data, 64);
			if(mode == "Beam")
				writeDataBeam(pic, 0, address, data, 64);
			else
				writeDataTiny(pic, 0, address, data, 64);
		}

		// Write out saved reset vector
		if(mode == "Tiny"){
			printf("Write Vectors");
			writeDataTiny(pic, 0, 0x8000 - 200 - 64 + 8, resetVector, 64);
		}
		
		if(mode == "Beam"){
			writeBoot(pic);
		}
	}
	
	
	return 0;
}
 
