/**********************************************************************
 * moxaload.c
 *	Firmware Download Utility for PCL Multiport Card	
 *	by Emily Wu
 *
 *	Copyright (c) 404 Technologies Inc. 1994. All Rights Reserved.
 * History:
 *	Kevin Li 6/94	get_os_info()
 *	Kevin Li 12/94	merge Solaris's moxaload.c
 *	Victor   07/96	modified. for V3.0
 *	Casper	12/30/98 modified for linux ,5.0
 *      Casper  07/20/99 modified for ver 5.1
 *********************************************************************/


#include	<sys/types.h>
#include	<termios.h>
#include	<stdio.h>
#include	<fcntl.h>
#include	<unistd.h>
#include	<sys/errno.h>
#include	"pcladm.h"


typedef unsigned char	uchar;
extern int	errno;


#define BIOS		0
#define TTYS		1
#define COMM		2

#define MINSLEEPTIME	2

#define ABIOS	0
#define BTTYS	1
#define BCOMM	2
#define ATTYS	3
#define ACOMM	4
#define Total	5

char	*szBoard[] = {
        "",
        "C218 Turbo/PCI",
        "PCL-844 ISA",
        "C320 Turbo/PCI",
        "PCL-747 ISA",
        "CP-204J"
};

moxa_board_conf	conf[4];
MOXA_PCI_Config	pciconf;
int		pcinotset,pciboardset[C218320_MAXCARD];
int		pciindex[C218320_MAXCARD], memconflict[C218320_MAXCARD];
uchar		cardtype[C218320_MAXCARD], cardtype1[C218320_MAXCARD];

struct dl_str {
	uchar	*buf;
	int	len;
	int	cardno;
};

struct dl_str	dl_blk;

int		fd, ffd;
uchar		c218_bios[8192];
uchar		c320_bios[8192];
uchar		cp204_bios[8192];
uchar		c218ttys_buf[10240];
uchar		c218comm_buf[10240];
uchar		cp204comm_buf[10240];
uchar		c320bttys_buf[10240];
uchar		c320bcomm_buf[10240];
uchar		c320attys_buf[10240];
uchar		c320acomm_buf[10240];

char c218_file[] = "/usr/lib/pcl/p747tunx.cod";
char c320_file[] = "/usr/lib/pcl/p844tunx.cod";
char cp204_file[] = "/usr/lib/pcl/p204unx.cod";

char		moxa_card;
int		c218_len[3];		/* BIOS, TTYS, COMM */
int		cp204_len[3];		/* BIOS, TTYS, COMM */
int		c320_len[Total];	/* ABIOS, BTTYS, BCOMM... */
char		c218flag=0, c320flag=0,cp204flag=0;
char		fail[C218320_MAXCARD];

int		SleepTime = MINSLEEPTIME;

void cp204file();
void c218file();
void c320file();
void ld204comm(int cardno);
void ld218comm(int cardno);
void ld320comm(int cardno);
void func_load(int cardno);


int  main(int argc, char *argv[])
{
	int	i, j, k, len, errflag[C218320_MAXCARD];
	char	buf[30];
        
	if (argc < 2 || argv[1][0] != '-' || argv[1][1] != 'y')
		exit(1);
                
	if (argc == 3)
		sscanf(argv[2], "%d", &SleepTime);
	if (SleepTime < MINSLEEPTIME )
		SleepTime = MINSLEEPTIME;
	for (i=0; i<C218320_MAXCARD; i++)
		fail[i] = 0;
	if ((fd = open("/dev/pcl", O_RDWR)) < 0) {
		printf("ERROR! open /dev/pcl failure.\n");
		errexit();
	}

	i = ioctl(fd, MOXA_GET_CONF, &conf);

	for ( i=0; i<C218320_MAXCARD; i++ ) {
	    if ( (conf[i].boardType == C218TPCI) ||
	         ( conf[i].boardType == C218TISA)) {
		c218flag = 1;
		cardtype1[i] = C218TSERIES;
	    }
	    else if ( (conf[i].boardType == C320TPCI) ||
	         ( conf[i].boardType == C320TISA)) {
		c320flag = 1;
		cardtype1[i] = C320TSERIES;
	    }
	    else if (conf[i].boardType == CP204J) {
		cp204flag = 1;
		cardtype1[i] = CP204JSERIES;
	    }
	    else
		cardtype1[i] = 0;
	}
	if ( c218flag )
		c218file();
	if ( c320flag )
		c320file();
	if ( cp204flag )
		cp204file();

	for (i = 0;i < C218320_MAXCARD;i++) {
	    memconflict[i] = 0;
	}

	for (i = 0;i < C218320_MAXCARD; i++) {
	    if ( !conf[i].boardType ) 
	        continue;
	    for (j = i+1;j < C218320_MAXCARD;j++) {
		if ( !conf[j].boardType )
		    continue;
		    if ( conf[i].baseAddr == conf[j].baseAddr) {
			    memconflict[i] = 1;
			    memconflict[j] = 1;
		    }
	    }
	}

	if ( i < 0 ) {
		close(fd);
		printf("Ioctl control error for card configuration !\n");
		errexit();
	}


/* Call clear card */
	i = 0x404;
        if (ioctl(fd, MOXA_INIT_DRIVER, &i) == -1) {
		printf("PCL device ioctl (LCLRCARD) error\n");
		errexit();
        }

	for ( i=0; i<C218320_MAXCARD; i++ ) {
	    if ( cardtype1[i] )	/* load bios and find the card */
		switch ( cardtype1[i] ) {
		case C218TSERIES:
		     dl_blk.buf = c218_bios;
		     dl_blk.len = c218_len[BIOS];
		     dl_blk.cardno = i;
		     if (ioctl(fd, MOXA_LOAD_BIOS, &dl_blk) == -1) {
			printf("PCL device ioctl (MOXA_LOAD_BIOS) error\n");
			errexit();
		     }
		     break;
		case C320TSERIES:		/* C320 */
		     dl_blk.buf = c320_bios;
		     dl_blk.len = c320_len[ABIOS];
		     dl_blk.cardno = i;
		     if (ioctl(fd, MOXA_LOAD_BIOS, &dl_blk) == -1) {
			printf("PCL device ioctl (MOXA_LOAD_BIOS) error\n");
			errexit();
		     }
		     break;
		case CP204JSERIES:
		     dl_blk.buf = cp204_bios;
		     dl_blk.len = cp204_len[BIOS];
		     dl_blk.cardno = i;
		     if (ioctl(fd, MOXA_LOAD_BIOS, &dl_blk) == -1) {
			printf("PCL device ioctl (MOXA_LOAD_BIOS) error\n");
			errexit();
		     }
		     break;
		}
	}

	printf("Intellio Series device driver (Ver. %s)\n", MOXA_VER);
	printf("Downloading ......\n");

	sleep(SleepTime);
	for (i=0; i<C218320_MAXCARD; i++)
	    if ( cardtype1[i] ) {
		if (conf[i].busType == 1)
		    sprintf (buf,"BusNo=%d,DevNo=%d",conf[i].Pci.BusNum,conf[i].Pci.DevNum);
		else
		    sprintf (buf,"MemBank=%x",conf[i].baseAddr);

		if ( memconflict[i] ) {
		    printf ("WARNING! PCL %s(%s) MemBank conflict.\n",szBoard[conf[i].boardType], buf);
		    continue;
		}
		if ( pciindex[i] == -1) {
		    printf("WARNING! PCL %s(%s) not found.\n",szBoard[conf[i].boardType], buf);
		    continue;
		}
		dl_blk.cardno = i;
		if (ioctl(fd, MOXA_FIND_BOARD, &dl_blk) == -1) {
	            if (errno == 1) {
		        printf("WARNING! PCL %s(%s) not found.\n",szBoard[conf[i].boardType], buf);
                    }else if (errno == 2){
		        printf("WARNING! PCL %s(%s) CPU/Basic module not found.\n",szBoard[conf[i].boardType], buf);
                    }
                    fail[i] = 1;
		}
                
		if ( fail[i] == 0 ) 
		    printf("Board %2d: PCL %s(%s) is found.\n", i+1, szBoard[conf[i].boardType], buf);
		if (fail[i] == 0 && (cardtype1[i] != C218TSERIES) && (cardtype1[i] != CP204JSERIES)) {
			dl_blk.buf = c320bcomm_buf;
			dl_blk.len = c320_len[BCOMM];
			dl_blk.cardno = i;
			if (ioctl(fd, MOXA_LOAD_C320B, &dl_blk) == -1) {
			   printf("WARNING! PCL device ioctl (MOXA_LOAD_C320B) error\n");
			   errexit();
			}
		}
	    }
            
	for (i=0; i<C218320_MAXCARD; i++) {
	    if ( memconflict[i] )
		continue;
	    if (fail[i] == 0) {
		switch ( cardtype1[i] ) {
		case C218TSERIES:
		     ld218comm(i);
		     break;
		case C320TSERIES:
		     ld320comm(i);
		     break;
		case CP204JSERIES:
		     ld204comm(i);
		     break;
		}
	    }
	}
	return 0;	
}

void cp204file()
{
	int	j, len;

	if ((ffd = open(cp204_file, O_RDWR)) == -1) {
		printf("ERROR! Firmware file (%s) not found.\n", cp204_file);
		errexit();
	}
	if (read(ffd, cp204_bios, 9) != 9) {
		printf("ERROR! Firmware file (%s) read error.\n", cp204_file);
		errexit();
	}
	if (cp204_bios[0] == '4' && cp204_bios[1] == '0' && cp204_bios[2] == '4') {
	    for (j=0; j<3; j++) {
		cp204_len[j] = cp204_bios[4+j*2];
		cp204_len[j] <<= 8;
		cp204_len[j] += cp204_bios[3+j*2];
	    }
	} else {
		printf("ERROR! Firmware file (%s) format error.\n", cp204_file);
		errexit();
	}
	len = read(ffd, cp204_bios, cp204_len[BIOS]);
	if (len != cp204_len[BIOS]) {
		printf("ERROR! Firmware file (%s) read error.\n", cp204_file);
		errexit();
	}
	len = read(ffd, cp204comm_buf, cp204_len[TTYS]);
	if (len != cp204_len[TTYS]) {
		printf("ERROR! Firmware file (%s) read error.\n", cp204_file);
		errexit();
	}

	len = read(ffd, cp204comm_buf, cp204_len[COMM]);
	if (len != cp204_len[COMM]) {
		printf("ERROR! Firmware file (%s) read error.\n", cp204_file);
		errexit();
	}
	close(ffd);
}


void c218file()
{
	int	j, len;

	if ((ffd = open(c218_file, O_RDWR)) == -1) {
		printf("ERROR! Firmware file (%s) not found.\n", c218_file);
		errexit();
	}
	if (read(ffd, c218_bios, 9) != 9) {
		printf("ERROR! Firmware file (%s) read error.\n", c218_file);
		errexit();
	}
	if (c218_bios[0] == '4' && c218_bios[1] == '0' && c218_bios[2] == '4') {
	    for (j=0; j<3; j++) {
		c218_len[j] = c218_bios[4+j*2];
		c218_len[j] <<= 8;
		c218_len[j] += c218_bios[3+j*2];
	    }
	} else {
		printf("ERROR! Firmware file (%s) format error.\n", c218_file);
		errexit();
	}
	len = read(ffd, c218_bios, c218_len[BIOS]);
	if (len != c218_len[BIOS]) {
		printf("ERROR! Firmware file (%s) read error.\n", c218_file);
		errexit();
	}
	len = read(ffd, c218ttys_buf, c218_len[TTYS]);
	if (len != c218_len[TTYS]) {
		printf("ERROR! Firmware file (%s) read error.\n", c218_file);
		errexit();
	}
	len = read(ffd, c218comm_buf, c218_len[COMM]);
	if (len != c218_len[COMM]) {
		printf("ERROR! Firmware file (%s) read error.\n", c218_file);
		errexit();
	}
	close(ffd);
}

void c320file()
{
	int	j, len;

	if ((ffd = open(c320_file, O_RDWR)) == -1) {
		printf("ERROR! Firmware file (%s) not found.\n", c320_file);
		errexit();
	}
	if (read(ffd, c320_bios, (Total*2+3)) != (Total*2+3)) {
		printf("ERROR! Firmware file (%s) read error.\n", c320_file);
		errexit();
	}
	if (c320_bios[0] == '4' && c320_bios[1] == '0' && c320_bios[2] == '4') {
	    for (j=0; j<Total; j++) {
		c320_len[j] = c320_bios[4+j*2];
		c320_len[j] <<= 8;
		c320_len[j] += c320_bios[3+j*2];
	    }
	} else {
		printf("ERROR! Firmware file (%s) format error.\n", c320_file);
		errexit();
	}
	len = read(ffd, c320_bios, c320_len[ABIOS]);
	if (len != c320_len[ABIOS]) {
		printf("ERROR! Firmware file (%s) read error.\n", c320_file);
		errexit();
	}
	len = read(ffd, c320bttys_buf, c320_len[BTTYS]);
	if (len != c320_len[BTTYS]) {
		printf("ERROR! Firmware file (%s) read error.\n", c320_file);
		errexit();
	}
	len = read(ffd, c320bcomm_buf, c320_len[BCOMM]);
	if (len != c320_len[BCOMM]) {
		printf("ERROR! Firmware file (%s) read error.\n", c320_file);
		errexit();
	}
	len = read(ffd, c320attys_buf, c320_len[ATTYS]);
	if (len != c320_len[ATTYS]) {
		printf("ERROR! Firmware file (%s) read error.\n", c320_file);
		errexit();
	}
	len = read(ffd, c320acomm_buf, c320_len[ACOMM]);
	if (len != c320_len[ACOMM]) {
		printf("ERROR! Firmware file (%s) read error.\n", c320_file);
		errexit();
	}
	close(ffd);
}

void ld204comm(int cardno)
{
	int	i, len;

	len = ((cp204_len[COMM] + 1) >> 1) << 1;
	dl_blk.buf = cp204comm_buf;
	dl_blk.len = len;
	dl_blk.cardno = cardno;
	func_load(cardno);
}

void ld218comm(int cardno)
{
	int	i, len;

	len = ((c218_len[COMM] + 1) >> 1) << 1;
	dl_blk.buf = c218comm_buf;
	dl_blk.len = len;
	dl_blk.cardno = cardno;
	func_load(cardno);
}

void ld218ttys(int cardno)
{
	int	i, len;
	len = ((c218_len[TTYS] + 1) >> 1) << 1;
	dl_blk.buf = c218ttys_buf;
	dl_blk.len = len;
	dl_blk.cardno = cardno;
	func_load(cardno);
}

void ld320comm(int cardno)
{
	int	i;

	dl_blk.buf = c320acomm_buf;
	dl_blk.len = c320_len[ACOMM];
	dl_blk.cardno = cardno;
	func_load(cardno);
}

void ld320ttys(int cardno)
{
	int	i;

	dl_blk.buf = c320attys_buf;
	dl_blk.len = c320_len[ATTYS];
	dl_blk.cardno = cardno;
	func_load(cardno);
}

void func_load(int cardno)
{
	if (ioctl(fd, MOXA_LOAD_CODE, &dl_blk) == -1) {
	   if (errno == EFAULT)
	       printf("ERROR! PCL %s (%x) download fail.\n", szBoard[conf[cardno].boardType], conf[cardno].baseAddr);
	   else if (errno == 100)
	       printf("ERROR! PCL %s (%x) CPU/Basic module download fail.\n", szBoard[conf[cardno].boardType], conf[cardno].baseAddr);
	   else
	       printf("ERROR! PCL %s (%x) UART module not found.\n", szBoard[conf[cardno].boardType], conf[cardno].baseAddr);
	}
}

errexit()
{
	printf("PCL serial I/O Board initialization failure.\n\n");
	exit(1);
}


