/*
 * TODO: fuer seitenweises programmieren noch die Seitenauswahl fuer die
 * Seitenorientierten Eproms schreiben (27513,27011)
 * written 1998 by holm@freibergnet.de
 */
#define TERMIOS

#include <stdio.h>
#include<sys/types.h>
#include<sys/mman.h>
#include<sys/stat.h>
#include<fcntl.h>
#ifdef TERMIOS
#include <termios.h>
#else
#include<termio.h>
#endif
#include<unistd.h>
#include<stdlib.h>
#include<signal.h>

#define MINGAPLEN 0x10
extern int      strncmp();

FILE           *pfile;

#ifdef TERMIOS
struct termios  t1, t2;
#else
struct termio   t1, t2;
#endif

struct stat     st;
caddr_t         filebuffer;
int             loop, vpp, curtty, bytes, laenge;
char            ichrbuf[20], ochrbuf[20], u;
char            ptyp, tty_dev[BUFSIZ], fname[255];
unsigned char   uch;
int             p, v, r, retval, verbose;

int 
myputchar(int fdesc, int ch)
{
	int             rck;

	rck = write(fdesc, &ch, 1);
	 usleep(20); 		/* 21000 */
	return (rck);
}


/* int mwstrg (int fdesc,char *str) 
{ 
   return(write(fdesc,str,strlen(str))); 
}
*/

int 
mwstrg(int fdesc, char *str)
{
	while (*str) {
		myputchar(fdesc, *str++);
	}
	return (0);
} 

unsigned char 
asc2hex(unsigned char high, unsigned char low)
{
	unsigned char   a, b;

	if ((a = high) <= 0x39) {
		a = a & 0x0f;
	} else {
		a = a & 0xdf;
		a = (a - 7) & 0x0f;
	}
	b = a << 4;
	if ((a = low) <= 0x39) {
		a = a & 0x0f;
	} else {
		a = a & 0xdf;
		a = (a - 7);
		a = a & 0x0f;
	}
	return (b | a);
}

unsigned char 
mygetchar(int fp)
{
	unsigned char   chrbuf[20];

	while (read(fp, chrbuf, 1) < 1) {
		perror("read(2)");
	}
	return (chrbuf[0]);
}

unsigned char 
gethex(int fp)
{
	unsigned char   a, b;

	a = mygetchar(fp);
	b = mygetchar(fp);
	return (asc2hex(a, b));
}

void 
usage(char **argv)
{
	fprintf(stderr, "usage:\n");
	fprintf(stderr, "%s p promtype vpp file\n", argv[0]);
	fprintf(stderr, "%s v promtype file\n", argv[0]);
	fprintf(stderr, "%s r promtype file\n", argv[0]);
	fprintf(stderr, "for verbose messages use  \n");
	fprintf(stderr, "%s pv promtype vpp file\n", argv[0]);
	fprintf(stderr, "%s vv promtype file\n", argv[0]);
	fprintf(stderr, "%s rv promtype file\n", argv[0]);

}

void 
mabort(int sigtype)
{
	fclose(pfile);
	if (p)
		munmap(filebuffer, laenge);
#ifdef TERMIOS
	tcsetattr(curtty, TCSAFLUSH, &t2);
#else
	ioctl(curtty, TCSETA, &t2);
#endif
	fprintf(stderr, "\nProgram abort!\n");
	exit(-1);
}


int 
main(argc, argv)
	int             argc;
	char          **argv;
{
	unsigned char   data;
	int             aadr, eadr;
	int             gaplen, datablocks;
	int             blockloop;
	int            *bdataptr, *edataptr;

	p = v = r = verbose = 0;
	signal(SIGINT, mabort);

	if (getenv("EPROGTTY") == NULL) {
		fprintf(stderr,
			"%s Error: EPROGTTY not found in Environment!\n",
			argv[0]);
		exit(-1);
	} else {
		strcpy(tty_dev, getenv("EPROGTTY"));
	}
	if ((argc < 4) || (argc > 5)) {
		usage(argv);
		exit(-1);
	}
	if (strcmp(argv[1], "p") == 0) {
		p++;
	}
	if (strcmp(argv[1], "v") == 0) {
		v++;
	}
	if (strcmp(argv[1], "r") == 0) {
		r++;
	}
	if (strcmp(argv[1], "pv") == 0) {
		p++;
		verbose++;
	}
	if (strcmp(argv[1], "vv") == 0) {
		v++;
		verbose++;
	}
	if (strcmp(argv[1], "rv") == 0) {
		r++;
		verbose++;
	}

	if (p + v + r == 0) {
		fprintf(stderr, "%s: Error Bad Arguments\n", argv[0]);
		usage(argv);
		exit(-1);
	}
	ptyp = 'F';
	vpp = u = 0;
	if (strncmp("2716", argv[2], 4) == 0)
		ptyp = 'C';
	if (strncmp("2532", argv[2], 4) == 0)
		ptyp = 'B';
	if (strncmp("2732", argv[2], 4) == 0)
		ptyp = 'A';
	if (strncmp("2764", argv[2], 4) == 0)
		ptyp = '8';
	if (strncmp("27128", argv[2], 5) == 0)
		ptyp = '6';
	if (strncmp("27256", argv[2], 5) == 0)
		ptyp = '4';
	if (strncmp("27512", argv[2], 5) == 0)
		ptyp = '2';
	if (strncmp("27513", argv[2], 5) == 0)
		ptyp = '1';
	if (strncmp("27011", argv[2], 5) == 0)
		ptyp = '0';
	if (ptyp == 'F') {
		fprintf(stderr, " %s :bad promtype\n", argv[0]);
		exit(-1);
	}
	if ((argc == 5) && (p == 1)) {
		if (strncmp("25", argv[3], 2) == 0) {
			vpp = 25;
			u = '2';
		}
		if (strncmp("21", argv[3], 2) == 0) {
			vpp = 21;
			u = '1';
		}
		if (strncmp("12", argv[3], 2) == 0) {
			vpp = 12;
			u = '3';
		}
		if (vpp == 0) {
			fprintf(stderr, " %s :bad vpp\n", argv[0]);
			exit(-1);
		}
		strcpy(fname, argv[4]);
	} else {
		strcpy(fname, argv[3]);
	}

	if ((ptyp == 'A') && (vpp == 21))
		ptyp = '9';
	if ((ptyp == '8') && (vpp == 12))
		ptyp = '7';
	if ((ptyp == '6') && (vpp == 12))
		ptyp = '5';
	if ((ptyp == '4') && (vpp == 12))
		ptyp = '3';

	switch (ptyp) {
	case '0':
		bytes = 131072;
		break;
	case '1':
		bytes = 65536;
		break;
	case '2':
		bytes = 65536;
		break;
	case '3':
		bytes = 32768;
		break;
	case '4':
		bytes = 32768;
		break;
	case '5':
		bytes = 16384;
		break;
	case '6':
		bytes = 16384;
		break;
	case '7':
		bytes = 8192;
		break;
	case '8':
		bytes = 8192;
		break;
	case '9':
		bytes = 4096;
		break;
	case 'A':
		bytes = 4096;
		break;
	case 'B':
		bytes = 4096;
		break;
	case 'C':
		bytes = 2048;
		break;
	}

	if (p || v) {
		if ((pfile = fopen(fname, "r")) == NULL) {
			fprintf(stderr, "can't open file: %s\n", fname);
			exit(-1);
		}
		stat(fname, &st);
		if (st.st_size < bytes) {
			fprintf(stderr,
		"%s: Error Filelength for %s shorter than prom\
 has Bytes\n", argv[0], fname);
			fclose(pfile);
			exit(-1);
		}
		if (st.st_size > bytes) {
			fprintf(stderr,
		"%s: Warning Filelength for %s longer\
 then prom has Bytes; truncated!\n", argv[0], fname);
		}
	}
	if (r) {
		if ((pfile = fopen(fname, "w")) == NULL) {
			fprintf(stderr, "can't open file: %s\n", fname);
			exit(-1);
		}
	}
	curtty = open(tty_dev, O_RDWR|O_NOCTTY);
	if (curtty == -1) {
		fprintf(stderr, "Device open for %s failed !", tty_dev);
		fclose(pfile);
		exit(-1);
	}
#ifdef TERMIOS
	tcgetattr(curtty, &t1);
	tcgetattr(curtty, &t2);
	t1.c_iflag = IGNPAR | IGNBRK;
	t1.c_oflag = OPOST | ONLCR;
	t1.c_lflag = 0;
	t1.c_cflag = B9600| CS8 | CREAD | CRTSCTS | CLOCAL;
	cfsetspeed(&t1, B9600);
	t1.c_cc[VMIN] = 1;
	t1.c_cc[VTIME] = 0;
	tcsetattr(curtty, TCSADRAIN, &t1);
#else
	ioctl(curtty, TCGETA, &t1);
	ioctl(curtty, TCGETA, &t2);
	t1.c_iflag = IGNPAR | IGNBRK;
	t1.c_oflag = OPOST | ONLCR;
	t1.c_cflag = B9600 | CS8 | CREAD | CLOCAL | CRTSCTS;
	t1.c_lflag = 0;
	t1.c_cc[4] = 1;		/* read() kehrt nach einem character zurueck */
	t1.c_cc[5] = 0;		/* TIME ist uninterressant */
	ioctl(curtty, TCSETA, &t1);
#endif

	myputchar(curtty, 'I');	/* Initialisierung */
	sleep(1);
	myputchar(curtty, 'T');	/* Typ */
	myputchar(curtty, ptyp);/* Typ waehlen */
	sleep(1);
	/********************************************************************/
	if (p) {
		laenge = st.st_size;
		if ((filebuffer = mmap(0, laenge, PROT_READ, MAP_PRIVATE,
				       pfile->_fileno, 0)) == (caddr_t) - 1) {
			perror("mmap failed!");
			fclose(pfile);
			exit(-1);
		}
		datablocks = 0;
		if ((bdataptr = (int *) malloc(sizeof(aadr))) == NULL) {
			perror("malloc failed");
			fclose(pfile);
			munmap(filebuffer, laenge);
			exit(-1);
		}
		if ((edataptr = (int *) malloc(sizeof(eadr))) == NULL) {
			perror("malloc failed");
			fclose(pfile);
			munmap(filebuffer, laenge);
			exit(-1);
		}
		aadr = 0;
		while ((data = *(filebuffer + aadr)) == 0xff) {
			aadr++;
			if (aadr >= laenge) {
				goto end;
			}
		}		/* Begin des Datenblocks gefunden */
		eadr = aadr + 1;
search_end:
		gaplen = 0;
		while ((data = *(filebuffer + eadr)) != 0xff) {
			eadr++;
			if (eadr >= laenge) {
				goto end;
			}
		}		/* Ende des Datenblocks gefunden */

		while ((data = *(filebuffer + eadr + gaplen)) == 0xff) {
			gaplen++;
			if ((eadr + gaplen) >= laenge) {
				goto end;
			}
		}		/* Laenge der Blockluecke gefunden */

		if (gaplen < MINGAPLEN) {
			eadr += gaplen;
			goto search_end;
		} else {
			bdataptr[datablocks] = aadr;
			edataptr[datablocks] = eadr - 1;
			datablocks++;
			if ((bdataptr = (int *) realloc(bdataptr,
				(datablocks + 1) * sizeof(aadr))) == NULL) {
				perror("realloc failed");
				fclose(pfile);
				munmap(filebuffer, laenge);
				exit(-1);
			}
			if ((edataptr = (int *) realloc(edataptr,
				(datablocks + 1) * sizeof(eadr))) == NULL) {
				perror("realloc failed");
				fclose(pfile);
				munmap(filebuffer, laenge);
				exit(-1);
			}
			eadr += gaplen;
			aadr = eadr;
			eadr++;
			goto search_end;
		}

end:

		bdataptr[datablocks] = aadr;
		edataptr[datablocks] = eadr - 1;

		/*************************************************************/

		for (blockloop = 0; blockloop < datablocks + 1; blockloop++) {
			myputchar(curtty, 'X');	/* Startadresse */
			myputchar(curtty, '0');	/* Seite 0 des Eproms */
			sprintf(ochrbuf, "%04X", bdataptr[blockloop]);
			fprintf(stderr, "%04X -> ", bdataptr[blockloop]);
			mwstrg(curtty, ochrbuf);
			myputchar(curtty, 'Y');	/* Startadresse */
			myputchar(curtty, '0');	/* Seite 0 des Eproms */
			sprintf(ochrbuf, "%04X", edataptr[blockloop]);
			fprintf(stderr, "%04Xh ", edataptr[blockloop]);
			mwstrg(curtty, ochrbuf);
			usleep(5000);
			myputchar(curtty, 'P');	/* Mode programm */
			myputchar(curtty, '1');	/* Algorithmus */
			for (loop = (bdataptr[blockloop]);
				loop < (edataptr[blockloop] + 1); loop++) {
				sprintf(ochrbuf, "%02X",
					(unsigned char) filebuffer[loop]);
				mwstrg(curtty, ochrbuf);
			}
			usleep(50000);
			myputchar(curtty, 'S');
			if (mygetchar(curtty) != '0') {
				fprintf(stderr,
				"program block %d failed !\n", blockloop);
			} else {
				fprintf(stderr,
				"program block %d ok !\n", blockloop);
			}
			if (gethex(curtty) != 0xda) {	/* empfange 0x0d 0x0a */
				fprintf(stderr, "%s : read error!\n", tty_dev);
				fclose(pfile);
				munmap(filebuffer, laenge);
#ifdef TERMIOS
				tcsetattr(curtty, TCSAFLUSH, &t2);
#else
				ioctl(curtty, TCSETA, &t2);
#endif
				close(curtty);
				exit(-1);
			}
		}
	}
	if (v) {
		myputchar(curtty, 'V');	/* Mode verify */
		for (loop = 0; loop < bytes; loop++) {
			fscanf(pfile, "%c", &uch);
			sprintf(ochrbuf, "%02X", uch);
			mwstrg(curtty, ochrbuf);
			if (verbose != 0)
			   printf("Verify : %d of %d\r",loop, bytes);
		}
	}
	if (r) {
		myputchar(curtty, 'R');	/* Mode read */
		for (loop = 0; loop < bytes; loop++) {
			fprintf(pfile, "%c", gethex(curtty));
			if (verbose != 0)
			   printf("Receive : %d of %d\r",loop, bytes);
			
		}
		if (gethex(curtty) != 0xda) {	/* empfange 0x0d 0x0a */
			fprintf(stderr, "%s : read error!\n", tty_dev);
			fclose(pfile);
#ifdef TERMIOS
			tcsetattr(curtty, TCSAFLUSH, &t2);
#else
			ioctl(curtty, TCSETA, &t2);
#endif
			close(curtty);
			exit(-1);
		}
	}
	if (v) {
		myputchar(curtty, 'S');
		if (mygetchar(curtty) != '0') {
			if (p) {
				fprintf(stderr, "program failed !\n");
			}
			if (v) {
				fprintf(stderr, "verify failed !\n");
			}
		} else {
			if (p) {
				fprintf(stderr, "program ok !\n");
			}
			if (v) {
				fprintf(stderr, "verify ok !\n");
			}
		}
		if (gethex(curtty) != 0xda) {	/* empfange 0x0d 0x0a */
			fprintf(stderr, "%s : read error!\n", tty_dev);
			fclose(pfile);
#ifdef TERMIOS
			tcsetattr(curtty, TCSAFLUSH, &t2);
#else
			ioctl(curtty, TCSETA, &t2);
#endif
			close(curtty);
			exit(-1);
		}
	}

#ifdef TERMIOS
	tcsetattr(curtty, TCSAFLUSH, &t2);
#else
	ioctl(curtty, TCSETA, &t2);
#endif
	close(curtty);
	fclose(pfile);
	if (p)
		munmap(filebuffer, laenge);
	exit(0);
}

