FreeBSD-5.3/sbin/atm/fore_dnld/fore_dnld.c

Compare this file to the similar file:
Show the results in this format:

/*
 *
 * ===================================
 * HARP  |  Host ATM Research Platform
 * ===================================
 *
 *
 * This Host ATM Research Platform ("HARP") file (the "Software") is
 * made available by Network Computing Services, Inc. ("NetworkCS")
 * "AS IS".  NetworkCS does not provide maintenance, improvements or
 * support of any kind.
 *
 * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
 * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
 * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
 * In no event shall NetworkCS be responsible for any damages, including
 * but not limited to consequential damages, arising from or relating to
 * any use of the Software or related support.
 *
 * Copyright 1994-1998 Network Computing Services, Inc.
 *
 * Copies of this Software may be made, however, the above copyright
 * notice must be reproduced on all copies.
 */

/*
 * User utilities
 * --------------
 *
 * Download (pre)processed microcode into Fore Series-200 host adapter
 * Interact with i960 uart on Fore Series-200 host adapter
 *
 */

#include <sys/param.h>
#include <sys/mman.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <net/if.h>
#include <netatm/atm.h>
#include <netatm/atm_if.h>
#include <netatm/atm_sap.h>
#include <netatm/atm_sys.h>
#include <netatm/atm_ioctl.h>
#include <netinet/in.h>
#include <dev/hfa/fore.h>
#include <dev/hfa/fore_aali.h>
#include <dev/hfa/fore_slave.h>

#include <ctype.h>
#include <fcntl.h>
#include <paths.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#if (defined(BSD) && (BSD >= 199103))
#include <termios.h>
#else
#include <termio.h>
#endif	/* !BSD */
#include <unistd.h>

#ifndef lint
__RCSID("@(#) $FreeBSD: src/sbin/atm/fore_dnld/fore_dnld.c,v 1.17 2004/02/10 20:48:08 cperciva Exp $");
#endif

extern u_char pca200e_microcode_3[];
extern int pca200e_microcode_size_3;
extern u_char pca200e_microcode_4[];
extern int pca200e_microcode_size_4;

#ifdef sun
#define	DEV_NAME "/dev/sbus%d"
#endif	/* sun */
#if (defined(BSD) && (BSD >= 199103))
#define	DEV_NAME _PATH_KMEM
#endif	/* BSD */

#define	MAX_CHECK	60

static int	comm_mode = 0;
static const char *progname;

static int	tty;
static cc_t	vmin, vtime;
#if (defined(BSD) && (BSD >= 199103))
static struct termios sgtty;
#define	TCSETA	TIOCSETA
#define	TCGETA	TIOCGETA
#else
static struct termio sgtty;
#endif	/* !BSD */

static int	endian = 0;
static int	verbose = 0;
static int	reset = 0;

static char	line[132];
static u_int	lineptr;

static Mon960 *Uart;

static void
delay(int cnt)
{
	usleep(cnt);
}

static uint32_t
CP_READ(uint32_t val)
{
	if ( endian )
		return ( ntohl ( val ) );
	else
		return ( val );
}

static uint32_t
CP_WRITE(uint32_t val)
{
	if ( endian )
		return ( htonl ( val ) );
	else
		return ( val );
}

/*
 * Print an error message and exit.
 *
 * Arguments:
 *	none
 *
 * Returns:
 *	none
 */
static void
error(const char *msg)
{
	printf ( "%s\n", msg );
	exit (1);
}

/*
 * Get a byte for the uart and if printing, display it.
 *
 * Returns:
 *	c				Character from uart
 */
static char
getbyte(void)
{
	char c;

	while ( ! ( CP_READ(Uart->mon_xmithost) & UART_VALID ) )
		delay(10);

	c = CP_READ(Uart->mon_xmithost) & UART_DATAMASK;
	Uart->mon_xmithost = CP_WRITE(UART_READY);

	/*
	 * We need to introduce a delay in here or things tend to hang...
	 */
	delay(10000);

	if ( lineptr >= sizeof(line) )
		lineptr = 0;

	/*
	 * Save character into line
	 */
	line[lineptr++] = c;

	if (verbose) {
		if (isprint(c) || (c == '\n') || (c == '\r'))
			putc(c, stdout);
	}
	return (c);
}

/*
 * Loop getting characters from uart into static string until eol. If printing,
 * display the line retrieved.
 *
 * Arguments:
 *	prn				Are we displaying characters
 *
 * Returns:
 *	none				Line in global string 'line[]'
 */
static void
getline(int prn)
{
	char	c = '\0';
	u_int	i = 0;

	while ( c != '>' && c != '\n' && c != '\r' )
	{
		c = getbyte();
		if ( ++i >= sizeof(line) )
		{
			if ( prn )
				printf ( "%s", line );
			i = 0;
		}
	}

	/*
	 * Terminate line
	 */
	line[lineptr] = 0;
	lineptr = 0;

}

/*
 * Send a byte to the i960
 *
 * Arguments:
 *	c				Character to send
 *
 * Returns:
 *	none
 */
static void
xmit_byte(u_char c, int dn)
{
	int	val;

	while ( CP_READ(Uart->mon_xmitmon) != UART_READY )
	{
		if ( CP_READ(Uart->mon_xmithost) & UART_VALID )
			getbyte();
		if ( !dn ) delay ( 10000 );
	}
	val = (int)c | UART_VALID;
	Uart->mon_xmitmon = CP_WRITE( val );
	if ( !dn ) delay ( 10000 );
	if ( CP_READ(Uart->mon_xmithost) & UART_VALID )
		getbyte();

}

/*
 * Transmit a line to the i960. Eol must be included as part of text to transmit.
 *
 * Arguments:
 *	msg			Character string to transmit
 *	len			len of string. This allows us to include NULL's
 *					in the string/block to be transmitted.
 *
 * Returns:
 *	none
 */
static void
xmit_to_i960(const char *msg, int len, int dn)
{
	int i;

        for (i = 0; i < len; i++)
		xmit_byte(msg[i], dn);
}

/*
 * Send autobaud sequence to i960 monitor
 *
 * Arguments:
 *	none
 *
 * Returns:
 *	none
 */
static void
autobaud(void)
{
	if ( strncmp ( line, "Mon960", 6 ) == 0 )
		xmit_to_i960 ( "\r\n\r\n\r\n\r\n", 8, 0 );
}

/*
 * Reset tty to initial state
 *
 * Arguments:
 *	ret		error code for exit()
 *
 * Returns:
 *	none
 *
 */
static void
finish(int ret)
{
	sgtty.c_lflag |= ( ICANON | ECHO );
	sgtty.c_cc[VMIN] = vmin;
	sgtty.c_cc[VTIME] = vtime;
	ioctl ( tty, TCSETA, &sgtty );
	exit ( ret );
}

/*
 * Utility to strip off any leading path information from a filename
 *
 * Arguments:
 *	path		pathname to strip
 *
 * Returns:
 *	fname		striped filename
 *
 */
static const char *
basename(const char *path)
{
	const char *fname;

	if ( ( fname = strrchr ( path, '/' ) ) != NULL )
		fname++;
	else
		fname = path;

	return ( fname );
}

/*
 * ASCII constants
 */
#define		SOH		001
#define		STX		002
#define		ETX		003
#define		EOT		004
#define		ENQ		005
#define		ACK		006
#define		LF		012
#define		CR		015
#define		NAK		025
#define		SYN		026
#define		CAN		030
#define		ESC		033

#define		NAKMAX		2
#define		ERRORMAX	10
#define		RETRYMAX	5

#define		CRCCHR		'C'
#define		CTRLZ		032

#define		BUFSIZE		128

#define		W		16
#define		B		8

/*
 * crctab - CRC-16 constant array...
 *     from Usenet contribution by Mark G. Mendel, Network Systems Corp.
 *     (ihnp4!umn-cs!hyper!mark)
 */
static unsigned short crctab[1<<B] = {
    0x0000,  0x1021,  0x2042,  0x3063,  0x4084,  0x50a5,  0x60c6,  0x70e7,
    0x8108,  0x9129,  0xa14a,  0xb16b,  0xc18c,  0xd1ad,  0xe1ce,  0xf1ef,
    0x1231,  0x0210,  0x3273,  0x2252,  0x52b5,  0x4294,  0x72f7,  0x62d6,
    0x9339,  0x8318,  0xb37b,  0xa35a,  0xd3bd,  0xc39c,  0xf3ff,  0xe3de,
    0x2462,  0x3443,  0x0420,  0x1401,  0x64e6,  0x74c7,  0x44a4,  0x5485,
    0xa56a,  0xb54b,  0x8528,  0x9509,  0xe5ee,  0xf5cf,  0xc5ac,  0xd58d,
    0x3653,  0x2672,  0x1611,  0x0630,  0x76d7,  0x66f6,  0x5695,  0x46b4,
    0xb75b,  0xa77a,  0x9719,  0x8738,  0xf7df,  0xe7fe,  0xd79d,  0xc7bc,
    0x48c4,  0x58e5,  0x6886,  0x78a7,  0x0840,  0x1861,  0x2802,  0x3823,
    0xc9cc,  0xd9ed,  0xe98e,  0xf9af,  0x8948,  0x9969,  0xa90a,  0xb92b,
    0x5af5,  0x4ad4,  0x7ab7,  0x6a96,  0x1a71,  0x0a50,  0x3a33,  0x2a12,
    0xdbfd,  0xcbdc,  0xfbbf,  0xeb9e,  0x9b79,  0x8b58,  0xbb3b,  0xab1a,
    0x6ca6,  0x7c87,  0x4ce4,  0x5cc5,  0x2c22,  0x3c03,  0x0c60,  0x1c41,
    0xedae,  0xfd8f,  0xcdec,  0xddcd,  0xad2a,  0xbd0b,  0x8d68,  0x9d49,
    0x7e97,  0x6eb6,  0x5ed5,  0x4ef4,  0x3e13,  0x2e32,  0x1e51,  0x0e70,
    0xff9f,  0xefbe,  0xdfdd,  0xcffc,  0xbf1b,  0xaf3a,  0x9f59,  0x8f78,
    0x9188,  0x81a9,  0xb1ca,  0xa1eb,  0xd10c,  0xc12d,  0xf14e,  0xe16f,
    0x1080,  0x00a1,  0x30c2,  0x20e3,  0x5004,  0x4025,  0x7046,  0x6067,
    0x83b9,  0x9398,  0xa3fb,  0xb3da,  0xc33d,  0xd31c,  0xe37f,  0xf35e,
    0x02b1,  0x1290,  0x22f3,  0x32d2,  0x4235,  0x5214,  0x6277,  0x7256,
    0xb5ea,  0xa5cb,  0x95a8,  0x8589,  0xf56e,  0xe54f,  0xd52c,  0xc50d,
    0x34e2,  0x24c3,  0x14a0,  0x0481,  0x7466,  0x6447,  0x5424,  0x4405,
    0xa7db,  0xb7fa,  0x8799,  0x97b8,  0xe75f,  0xf77e,  0xc71d,  0xd73c,
    0x26d3,  0x36f2,  0x0691,  0x16b0,  0x6657,  0x7676,  0x4615,  0x5634,
    0xd94c,  0xc96d,  0xf90e,  0xe92f,  0x99c8,  0x89e9,  0xb98a,  0xa9ab,
    0x5844,  0x4865,  0x7806,  0x6827,  0x18c0,  0x08e1,  0x3882,  0x28a3,
    0xcb7d,  0xdb5c,  0xeb3f,  0xfb1e,  0x8bf9,  0x9bd8,  0xabbb,  0xbb9a,
    0x4a75,  0x5a54,  0x6a37,  0x7a16,  0x0af1,  0x1ad0,  0x2ab3,  0x3a92,
    0xfd2e,  0xed0f,  0xdd6c,  0xcd4d,  0xbdaa,  0xad8b,  0x9de8,  0x8dc9,
    0x7c26,  0x6c07,  0x5c64,  0x4c45,  0x3ca2,  0x2c83,  0x1ce0,  0x0cc1,
    0xef1f,  0xff3e,  0xcf5d,  0xdf7c,  0xaf9b,  0xbfba,  0x8fd9,  0x9ff8,
    0x6e17,  0x7e36,  0x4e55,  0x5e74,  0x2e93,  0x3eb2,  0x0ed1,  0x1ef0
    };

/*
 * Hacked up xmodem protocol. Transmits the file 'filename' down to the i960
 * using the xmodem protocol.
 *
 * Arguments:
 *	filename			name of file to transmit
 *
 * Returns:
 *	0				file transmitted
 *	-1				unable to send file
 */
static int
xmitfile(const char *filename)
{
	int	fd;
	int	numsect;
	int	sectnum;
	struct stat stb;
	char	c;
	char	sendresp;
	int	crcmode = 0;
	int	attempts = 0;
	int	errors;
	int	sendfin;
	int	extrachr;
	char	buf[BUFSIZE + 6];
	char	blockbuf[BUFSIZE + 6];
	int	bufcntr;
	int	bbufcntr;
	int	bufsize = BUFSIZE;
	int	checksum;

	/*
	 * Try opening file
	 */
	if ( ( fd = open ( filename, O_RDONLY ) ) < 0 )
	{
		return -1;
	}
	stat ( filename, &stb );

	/*
	 * Determine number of 128 bytes sectors to transmit
	 */
	numsect = ( stb.st_size / 128 ) + 1;

	if ( verbose )
		fprintf ( stderr, "Downloading %d sectors from %s\n",
			numsect, filename );

	/*
	 * Send DO'wnload' command to i960
	 */
	xmit_to_i960 ( "do\r\n", 4, 0 );
	/*
	 * Wait for response from i960 indicating download in progress
	 */
	while ( strncmp ( line, "Downloading", 11 ) != 0 )
		getline ( verbose );
	

	/*
	 * Get startup character from i960
	 */
	do {
		while ((c = getbyte()) != NAK && c != CRCCHR)
			if ( ++attempts > NAKMAX )
				error ( "Remote system not responding" );

		if ( c == CRCCHR )
			crcmode = 1;

	} while ( c != NAK && c != CRCCHR );

	sectnum = 1;
	attempts = errors = sendfin = extrachr = 0;

	/*
	 * Loop over each sector to be sent
	 */
	do {
		if ( extrachr >= 128 )
		{
			extrachr = 0;
			numsect++;
		}

		if ( sectnum > 0 )
		{
			/*
			 * Read a sectors worth of data from the file into
			 * an internal buffer.
			 */
			for ( bufcntr = 0; bufcntr < bufsize; )
			{
				int n;
				/*
				 * Check for EOF
				 */
				if ( ( n = read ( fd, &c, 1 ) ) == 0 )
				{
					sendfin = 1;
					if ( !bufcntr )
						break;
					buf[bufcntr++] = CTRLZ;
					continue;
				}
				buf[bufcntr++] = c;
			}
			if ( !bufcntr )
				break;
		}

		/*
		 * Fill in xmodem protocol values. Block size and sector number
		 */
		bbufcntr = 0;
		blockbuf[bbufcntr++] = (bufsize == 1024) ? STX : SOH;
		blockbuf[bbufcntr++] = sectnum;
		blockbuf[bbufcntr++] = ~sectnum;

		checksum = 0;

		/*
		 * Loop over the internal buffer computing the checksum of the
		 * sector
		 */
		for ( bufcntr = 0; bufcntr < bufsize; bufcntr++ )
		{
			blockbuf[bbufcntr++] = buf[bufcntr];

			if ( crcmode )
				checksum = (checksum<<B) ^ crctab[(checksum>>(W-B)) ^ buf[bufcntr]];
			else
				checksum = ((checksum + buf[bufcntr]) & 0xff);

		}

		/*
		 * Place the checksum at the end of the transmit buffer
		 */
		if ( crcmode )
		{
			checksum &= 0xffff;
			blockbuf[bbufcntr++] = ((checksum >> 8) & 0xff);
			blockbuf[bbufcntr++] = (checksum & 0xff);
		} else
			blockbuf[bbufcntr++] = checksum;

		attempts = 0;

		/*
		 * Make several attempts to send the data to the i960
		 */
		do
		{
			/*
			 * Transmit the sector + protocol to the i960
			 */
			xmit_to_i960 ( blockbuf, bbufcntr, 1 );

			/*
			 * Inform user where we're at
			 */
			if ( verbose )
				printf ( "Sector %3d %3dk\r",
				    sectnum, (sectnum * bufsize) / 1024 );

			attempts++;
			/*
			 * Get response from i960
			 */
			sendresp = getbyte();

			/*
			 * If i960 didn't like the sector
			 */
			if ( sendresp != ACK )
			{
				errors++;

				/*
				 * Are we supposed to cancel the transfer?
				 */
				if ( ( sendresp & 0x7f ) == CAN )
					if (getbyte() == CAN)
						error ( "Send canceled at user's request" );
			}

		} while ( ( sendresp != ACK ) && ( attempts < RETRYMAX ) && ( errors < ERRORMAX ) );

		/*
		 * Next sector
		 */
		sectnum++;

	} while ( !sendfin && ( attempts < RETRYMAX ) && ( errors < ERRORMAX ) );

	/*
	 * Did we expire all our allows attempts?
	 */
	if ( attempts >= RETRYMAX )
	{
		xmit_byte ( CAN, 1 ), xmit_byte ( CAN, 1 ), xmit_byte ( CAN, 1 );
		error ( "Remote system not responding" );
	}

	/*
	 * Check for too many transmission errors
	 */
	if ( errors >= ERRORMAX )
	{
		xmit_byte ( CAN, 1 ), xmit_byte ( CAN, 1 ), xmit_byte ( CAN, 1 );
		error ( "Too many errors in transmission" );
	}

	attempts = 0;

	/*
	 * Indicate the transfer is complete
	 */
	xmit_byte ( EOT, 1 );

	/*
	 * Wait until i960 acknowledges us
	 */
	while ((c = getbyte()) != ACK && (++attempts < RETRYMAX))
		xmit_byte ( EOT, 1 );

	if ( attempts >= RETRYMAX )
		error ( "Remote system not responding on completion" );

	/*
	 * After download, we'll see a few more command 
	 * prompts as the CP does its stuff. Ignore them.
	 */
	while ( strncmp ( line, "=>", 2 ) != 0 )
		getline ( verbose );

	while ( strncmp ( line, "=>", 2 ) != 0 )
		getline ( verbose );

	while ( strncmp ( line, "=>", 2 ) != 0 )
		getline ( verbose );

	/*
	 * Tell the i960 to start executing the downloaded code
	 */
	xmit_to_i960 ( "go\r\n", 4, 0 );

	/*
	 * Get the messages the CP will spit out
	 * after the GO command.
	 */
	getline ( verbose );
	getline ( verbose );

	close ( fd );

	return ( 0 );
}


static int
loadmicrocode(u_char *ucode, int size, u_char *ram)
{
	struct {
		uint32_t	Id;
		uint32_t	fver;
		uint32_t	start;
		uint32_t	entry;
	} binhdr;
#ifdef sun
	union {
		uint32_t	w;
		char	c[4];
	} w1, w2;
	int	n;
#endif
	u_char	*bufp;
	uint32_t *lp;


	/*
	 * Check that we understand this header
	 */
	memcpy(&binhdr, ucode, sizeof(binhdr));
	if ( strncmp ( (caddr_t)&binhdr.Id, "fore", 4 ) != 0 ) {
		fprintf ( stderr, "Unrecognized format in micorcode file." );
		return ( -1 );
	}

#ifdef	sun
	/*
	 * We always swap the SunOS microcode file...
	 */
	endian = 1;

	/*
	 * We need to swap the header start/entry words...
	 */
	w1.w = binhdr.start;
	for ( n = 0; n < sizeof(uint32_t); n++ )
		w2.c[3-n] = w1.c[n];
	binhdr.start = w2.w;
	w1.w = binhdr.entry;
	for ( n = 0; n < sizeof(uint32_t); n++ )
		w2.c[3-n] = w1.c[n];
	binhdr.entry = w2.w;
#endif	/* sun */

	/*
	 * Set pointer to RAM load location
	 */
	bufp = (ram + binhdr.start);

	/*
	 * Load file
	 */
	if ( endian ) {
		u_int	i;

		lp = (uint32_t *)(void *)ucode;
		/* Swap buffer */
		for ( i = 0; i < size / sizeof(uint32_t); i++ )
#ifndef	sun
			lp[i] = CP_WRITE(lp[i]);
#else
		{
			int	j;

			w1.w = lp[i];
			for ( j = 0; j < 4; j++ )
				w2.c[3-j] = w1.c[j];
			lp[i] = w2.w;
		}
#endif
	}
	bcopy ( (caddr_t)ucode, bufp, size );

	/*
	 * With .bin extension, we need to specify start address on 'go'
	 * command.
	 */
	{
		char	cmd[80];

		sprintf ( cmd, "go %x\r\n", binhdr.entry );

		xmit_to_i960 ( cmd, strlen ( cmd ), 0 );

		while ( strncmp ( line, cmd, strlen(cmd) - 3 ) != 0 ) 
			getline ( verbose );

		if ( verbose )
			printf("\n");
	}
	return ( 0 );
}

static int
sendbinfile(const char *fname, u_char *ram)
{
	struct {
		uint32_t	Id;
		uint32_t	fver;
		uint32_t	start;
		uint32_t	entry;
	} binhdr;
#ifdef sun
	union {
		uint32_t	w;
		char	c[4];
	} w1, w2;
#endif
	int	fd;
	int	n;
	int	cnt = 0;
	u_char	*bufp;
	uint32_t buffer[1024];

	/*
	 * Try opening file
	 */
	if ( ( fd = open ( fname, O_RDONLY ) ) < 0 )
		return ( -1 );

	/*
	 * Read the .bin header from the file
	 */
	if ( ( read ( fd, &binhdr, sizeof(binhdr) ) ) != sizeof(binhdr) )
	{
		close ( fd );
		return ( -1 );
	}

	/*
	 * Check that we understand this header
	 */
	if ( strncmp ( (caddr_t)&binhdr.Id, "fore", 4 ) != 0 ) {
		fprintf ( stderr, "Unrecognized format in micorcode file." );
		close ( fd );
		return ( -1 );
	}

#ifdef	sun
	/*
	 * We always swap the SunOS microcode file...
	 */
	endian = 1;

	/*
	 * We need to swap the header start/entry words...
	 */
	w1.w = binhdr.start;
	for ( n = 0; n < sizeof(uint32_t); n++ )
		w2.c[3-n] = w1.c[n];
	binhdr.start = w2.w;
	w1.w = binhdr.entry;
	for ( n = 0; n < sizeof(uint32_t); n++ )
		w2.c[3-n] = w1.c[n];
	binhdr.entry = w2.w;
#endif	/* sun */

	/*
	 * Rewind the file
	 */
	lseek ( fd, 0, 0 );

	/*
	 * Set pointer to RAM load location
	 */
	bufp = (ram + binhdr.start);

	/*
	 * Load file
	 */
	if ( endian ) {
		/*
		 * Need to swap longs - copy file into temp buffer
		 */
		while ( ( n = read ( fd, (char *)buffer, sizeof(buffer))) > 0 )
		{
			u_int i;

			/* Swap buffer */
			for (i = 0; i < sizeof(buffer) / sizeof(uint32_t); i++)
#ifndef	sun
				buffer[i] = CP_WRITE(buffer[i]);
#else
			{
				int	j;

				w1.w = buffer[i];
				for ( j = 0; j < 4; j++ )
					w2.c[3-j] = w1.c[j];
				buffer[i] = w2.w;
			}
#endif

			/*
			 * Copy swapped buffer into CP RAM
			 */
			cnt++;
			bcopy ( (caddr_t)buffer, bufp, n );
			if ( verbose )
				printf ( "%d\r", cnt );
			bufp += n;
		}
	} else {
	    while ( ( n = read ( fd, bufp, 128 ) ) > 0 )
	    {
		cnt++;
		if ( verbose )
			printf ( "%d\r", cnt );
		bufp += n;
	    }
	}

	/*
	 * With .bin extension, we need to specify start address on 'go'
	 * command.
	 */
	{
		char	cmd[80];

		sprintf ( cmd, "go %x\r\n", binhdr.entry );

		xmit_to_i960 ( cmd, strlen ( cmd ), 0 );

		while ( strncmp ( line, cmd, strlen(cmd) - 3 ) != 0 )
			getline ( verbose );

		if ( verbose )
			printf("\n");
	}

	close ( fd );
	return ( 0 );
}


/*
 * Program to download previously processed microcode to series-200 host adapter
 */
int
main(int argc, char *argv[])
{
	int	fd;			/* mmap for Uart */
	u_char	*ram;			/* pointer to RAM */
	Mon960	*Mon;			/* Uart */
	Aali	*aap;
	int	c, i, err;
	int	binary = 0;		/* Send binary file */
	caddr_t	buf;			/* Ioctl buffer */
	char	bus_dev[80];		/* Bus device to mmap on */
	struct atminfreq req;
	struct air_cfg_rsp *air;	/* Config info response structure */
	int	buf_len;		/* Size of ioctl buffer */
	const char *dev = "\0";		/* Device to download */
	char	*dirname = NULL;	/* Directory path to objd files */
	char	*objfile = NULL;	/* Command line object filename */
	u_char	*ucode = NULL;		/* Pointer to microcode */
	int	ucode_size = 0;		/* Length of microcode */
	char	*sndfile = NULL;	/* Object filename to download */
	char	filename[64];		/* Constructed object filename */
	char	base[64];		/* sba200/sba200e/pca200e basename */
	int	ext = 0;		/* 0 == bin 1 == objd */
	struct stat sbuf;		/* Used to find if .bin or .objd */
	int	pca_vers = 4;

	progname = basename(argv[0]);
	comm_mode = strcmp ( progname, "fore_comm" ) == 0;

	while ( ( c = getopt ( argc, argv, "3i:d:f:berv" ) ) != -1 )
	    switch ( c ) {
		case '3':
			pca_vers = 3;
			break;
		case 'b':
			binary++;
			break;
		case 'd':
			dirname = (char *)strdup ( optarg );
			break;
		case 'e':
			endian++;
			break;
		case 'i':
			dev = (char *)strdup(optarg);
			break;
		case 'f':
			objfile = (char *)strdup ( optarg );
			break;
		case 'v':
			verbose++;
			break;
		case 'r':
			reset++;
			break;
		case '?':
			printf ( "usage: %s [-v] [-i intf] [-d dirname] [-f objfile]\n", argv[0] );
			exit ( 2 );
	    }
	
	/*
	 * Unbuffer stdout
	 */
	setbuf ( stdout, NULL );
		
	if ( ( fd = socket ( AF_ATM, SOCK_DGRAM, 0 ) ) < 0 )
	{
		perror ( "Cannot create ATM socket" );
		exit ( 1 );
	}
	/*
	 * Over allocate memory for returned data. This allows
	 * space for IOCTL reply info as well as config info.
	 */
	buf_len = 4 * sizeof(struct air_cfg_rsp);
	if ( ( buf = (caddr_t)malloc(buf_len) ) == NULL )
	{
		perror ( "Cannot allocate memory for reply" );
		exit ( 1 );
	}
	/*
	 * Fill in request paramaters
	 */
	req.air_opcode = AIOCS_INF_CFG;
	req.air_buf_addr = buf;
	req.air_buf_len = buf_len;

	/*
	 * Copy interface name into ioctl request
	 */
	strcpy(req.air_cfg_intf, dev);

	/*
	 * Issue ioctl
	 */
	if ( ( ioctl ( fd, AIOCINFO, (caddr_t)&req ) ) ) {
		perror ( "ioctl (AIOCSINFO)" );
		exit ( 1 );
	}
	/*
	 * Reset buffer pointer
	 */
	req.air_buf_addr = buf;

	/*
	 * Close socket
	 */
	close ( fd );

	/*
	 * Loop through all attached adapters
	 */
	for (; req.air_buf_len >= sizeof(struct air_cfg_rsp); 
			buf += sizeof(struct air_cfg_rsp),
			req.air_buf_len -= sizeof(struct air_cfg_rsp)) {

		/*
		 * Point to vendor info
		 */
		air = (struct air_cfg_rsp *)(void *)buf;

		if (air->acp_vendapi == VENDAPI_FORE_1 && air->acp_ram != 0)
		{
			/*
			 * Create /dev name
			 */
#ifdef sun
			sprintf ( bus_dev, DEV_NAME, air->acp_busslot );
#else
			sprintf ( bus_dev, DEV_NAME );
#endif

			/*
			 * Setup signal handlers
			 */
			signal ( SIGINT, SIG_IGN );
			signal ( SIGQUIT, SIG_IGN );
		
			/*
			 * If comm_mode, setup terminal for single char I/O
			 */
			if ( comm_mode ) {
				tty = open ( _PATH_TTY, O_RDWR );
				ioctl ( tty, TCGETA, &sgtty );
				sgtty.c_lflag &= ~( ICANON | ECHO );
				vmin = sgtty.c_cc[VMIN];
				vtime = sgtty.c_cc[VTIME];
				sgtty.c_cc[VMIN] = 0;
				sgtty.c_cc[VTIME] = 0;
				ioctl ( tty, TCSETA, &sgtty );
			}

			/*
			 * Open bus for memory access
			 */
			if ( ( fd = open ( bus_dev, O_RDWR ) ) < 0 )
			{
				perror ( "open bus_dev" );
				fprintf(stderr, "%s download failed (%s)\n",
					air->acp_intf, bus_dev);
				continue;
			}

			/*
			 * Map in the RAM memory to get access to the Uart
			 */
#ifdef __FreeBSD__ /*XXX*/
			ram = (u_char *) mmap(0, PCA200E_MMAP_SIZE,
#else
			ram = (u_char *) mmap(0, air->acp_ramsize,
#endif
				PROT_READ | PROT_WRITE, MAP_SHARED | MAP_HASSEMAPHORE,
				fd, air->acp_ram);
			if (ram == (u_char *)-1) {
				perror ( "mmap ram" );
				fprintf(stderr, "%s download failed\n",
					air->acp_intf);
				(void) close(fd);
				continue;
			}
			Mon = (Mon960 *)(volatile void *)(ram + MON960_BASE);
			Uart = (Mon960 *)(volatile void *)&(Mon->mon_xmitmon);

			/*
			 * Determine endianess
			 */
			switch ( Mon->mon_bstat ) {
			case BOOT_COLDSTART:
			case BOOT_MONREADY:
			case BOOT_FAILTEST:
			case BOOT_RUNNING:
				break;

			default:
				switch (ntohl(Mon->mon_bstat)) {
				case BOOT_COLDSTART:
				case BOOT_MONREADY:
				case BOOT_FAILTEST:
				case BOOT_RUNNING:
					endian++;
					break;

				default:
					fprintf(stderr, "%s unknown status\n",
						air->acp_intf);
					(void) close(fd);
					continue;
				}
				break;
			}

#ifdef __FreeBSD__
			if (reset) {
				u_int	*hcr = (u_int *)(void *)(ram + PCA200E_HCR_OFFSET);
				PCA200E_HCR_INIT(*hcr, PCA200E_RESET_BD);
				delay(10000);
				PCA200E_HCR_CLR(*hcr, PCA200E_RESET_BD);
				delay(10000);
			}
#endif

			if ( comm_mode ) {
			    static struct timeval timeout = { 0, 0 };
			    int	esc_seen = 0;

			    /*
			     * We want to talk with the i960 monitor
			     */

			    /*
			     * Loop forever accepting characters
			     */
			    for ( ; ; ) {
				fd_set	fdr;
				int	ns;

				/*
				 * Check for data from the terminal
				 */
				FD_ZERO ( &fdr );
				FD_SET ( fileno(stdin), &fdr );

				if ( ( ns = select ( FD_SETSIZE, &fdr, NULL, NULL,
					&timeout ) ) < 0 ) {
						perror ( "select" );
						finish( -1 );
				}

				if ( ns ) {
					char c1;
					int nr;

					nr = read ( fileno(stdin), &c1, 1 );
					c1 &= 0xff;
					if ( !esc_seen ) {
					    if ( c1 == 27 )
						esc_seen++;
					    else
						xmit_byte ( c1, 0 );
					} else {
					    if ( c1 == 27 ) 
						finish( -1 );
					    else {
						xmit_byte ( 27, 0 );
						esc_seen = 0;
					    }
					    xmit_byte ( c1, 0 );
					}
				}

				/*
				 * Check for data from the i960
				 */
				if ( CP_READ(Uart->mon_xmithost) & UART_VALID ) {
					c = getbyte();
					putchar ( c );
				}
				if ( strcmp ( line, "Mon960" )  == 0 )
					autobaud();

			    }
			} else {
			    /*
			     * Make sure the driver is loaded and that the CP
			     * is ready for commands
			     */
			    if ( CP_READ(Mon->mon_bstat) == BOOT_RUNNING )
			    {
				fprintf ( stderr, 
				"%s is up and running - no download allowed.\n",
					air->acp_intf );
				(void) close(fd);
				continue;
			    }
		
			    if ( CP_READ(Mon->mon_bstat) != BOOT_MONREADY )
			    {
				fprintf ( stderr, 
					"%s is not ready for downloading.\n", 
					air->acp_intf );
				(void) close(fd);
				continue;
			    }
		
			    /*
			     * Indicate who we're downloading
			     */
			    if ( verbose )
				printf ( "Downloading code for %s\n",
					air->acp_intf );

			    /*
			     * Look for the i960 monitor message. 
			     * We should see this after a board reset.
			     */
			    while ( strncmp ( line, "Mon960", 6 ) != 0 && 
				strncmp ( line, "=>", 2 ) != 0 )
				getline( verbose );	/* Verbose */
		
			    /*
			     * Autobaud fakery
			     */
			    if ( strncmp ( line, "Mon960", 6 ) == 0 ) {
				xmit_to_i960 ( "\r\n\r\n\r\n\r\n", 8, 0 );
				delay ( 10000 );
			    }

			    /*
			     * Keep reading until we get a command prompt
			     */
			    while ( strncmp ( line, "=>", 2 ) != 0 )
				getline( verbose );	/* Verbose */

			    /*
			     * Choose the correct microcode file based on the
			     * adapter type the card claims to be.
			     */
			    switch ( air->acp_device )
			    {
			    case DEV_FORE_SBA200:
				sprintf ( base, "sba200" );
				break;

			    case DEV_FORE_SBA200E:
				sprintf ( base, "sba200e" );
				break;

			    case DEV_FORE_PCA200E:
				sprintf ( base, "pca200e" );
				break;
 
			    default:
				err = 1;
				fprintf(stderr, "Unknown adapter type: %d\n", 
					air->acp_device );
			    }

			    sndfile = NULL;

			    if ( objfile == NULL ) {
				switch ( air->acp_device ) {
				case DEV_FORE_SBA200:
				case DEV_FORE_SBA200E:
				    sprintf ( filename, "%s.bin%d", base,
					air->acp_bustype );
				    if ( stat ( filename, &sbuf ) == -1 ) {
					sprintf ( filename, "%s/%s.bin%d",
					    dirname, base,
						air->acp_bustype );
					if ( stat ( filename, &sbuf ) == -1 ) {
					    ext = 1;
					    sprintf ( filename, "%s.objd%d",
						base, air->acp_bustype );
					    if ( stat(filename, &sbuf) == -1 ) {
						sprintf ( filename,
						    "%s/%s.objd%d", dirname,
							base,
							    air->acp_bustype );
						if ( stat ( filename, &sbuf ) != -1 )
						    sndfile = filename;
					    } else
						sndfile = filename;
					} else
					    sndfile = filename;
				    } else
					sndfile = filename;
				    break;
				case DEV_FORE_PCA200E:
				    /* Use compiled in microcode */
				    if (pca_vers == 3) {
					    ucode = pca200e_microcode_3;
					    ucode_size = pca200e_microcode_size_3;
				    } else {
					    ucode = pca200e_microcode_4;
					    ucode_size = pca200e_microcode_size_4;
				    }
				    break;
				default:
				    break;
			        }
			    } else
				sndfile = objfile;

			    if ( ext && !binary )
				err = xmitfile ( sndfile );
			    else if (sndfile != NULL) 
				err = sendbinfile ( sndfile, ram );
			    else 
				err = loadmicrocode( ucode, ucode_size, ram );

			    if ( err ) {
				fprintf(stderr, "%s download failed\n",
					air->acp_intf);
				(void) close(fd);
				continue;
			    }

			    /*
			     * Download completed - wait around a while for
			     * the driver to initialize the adapter
			     */
			     aap = (Aali *)(void *)(ram + CP_READ(Mon->mon_appl));
			     for (i = 0; i < MAX_CHECK; i++, sleep(1)) {
				uint32_t hb1, hb2, hb3;

				hb3 = CP_READ(Mon->mon_bstat);
				if (hb3 != BOOT_RUNNING) {
					if (verbose)
						printf("bstat %x\n", hb3);
					continue;
				}

				hb1 = CP_READ(aap->aali_heartbeat);
				delay(1);
				hb2 = CP_READ(aap->aali_heartbeat);
				if (verbose)
					printf("hb %x %x\n", hb1, hb2);
				if (hb1 < hb2)
					break;
			     }
			}

			close ( fd );
		}
	}

	/*
	 * Exit
	 */
	exit (0);
}