/* $Id: hps_loop.c,v 2.6 91/10/02 10:31:19 leynette Exp $ */
/****************************************************************/
/* hps_loop.c:  Diagnostic: Controller DMA loopback test		*/
/****************************************************************/
/*																*/
/*                 Confidential Information                     */
/*      Limited Distribution to Authorized Persons Only         */
/*     Created 1985 and Protected as an Unpublished Work        */
/*          Under the U.S. Copyright Act of 1976.               */
/*         Copyright (c) 1985, Systech Corporation          	*/
/*                  All Rights Reserved                         */
/*																*/
/*	THIS SOFTWARE IS LICENSED BY AND IS THE	CONFIDENTIAL AND 	*/
/*	PROPRIETARY PROPERTY OF SYSTECH COMPUTER CORPORATION AND 	*/
/*	MAY NOT, WITHOUT THE PRIOR WRITTEN CONSENT OF SYSTECH 		*/
/*	COMPUTER CORPORATION, BE REPRODUCED, USED OR DISCLOSED 		*/
/*	EXCEPT IN ACCORDANCE WITH A SYSTECH COMPUTER CORPORATION 	*/
/*	SOFTWARE LICENSE AGREEMENT.									*/
/****************************************************************/
/*																*/
/*   This program drives the HPS' loopback diagnostics.			*/
/*																*/
/****************************************************************/

#include <signal.h>
#include <stdio.h>
#include "hps.types.h"
#include "hps.iocb.h"
#include "hps.ioctl.h"
#include "hps.onb.h"
#include "hps.net.h"
#include "hps.diag.h"

#define BUFF_SIZE	512

#define DNAME		"hps_dloop"
#define RNAME		"hps_rloop"
#define DLOOP		1
#define RLOOP		2

#ifdef HPS_STREAMS
#define RATE		100000	/* Default data rate */
#else
#define RATE		40000	/* Default data rate */
#endif
#define NUMBER		0		/* Default number of blocks */
#define RANDOM		0		/* Don't use random sized blocks */
#define SIZE		512		/* Use 512-byte blocks */
#define RSIZE		490 		/* Use 490-byte blocks on cluster */
#define TIME		30		/* Run for thirty seconds */

extern int errno;
char  hps_intel		= 0;			/* host byte ordering	*/

static long clock = 0;
static long runtime = TIME;
static int number = NUMBER;
static long	pid;

static long	  mailbox = 0;
static int	gotintr = 0;			/* flag that interrrupt was received  */
void	(*sig_func)();

main(argc, argv)
int argc;
char **argv;
{
	void catcher();

	char *rcp, *wcp, wbuff[BUFF_SIZE], rbuff[BUFF_SIZE];
	char c, *key, *arg, *devname, *tname, result;
	short cd;
	int fd, count, i, error, station, min;
	int check, rate, rate_hi, random, size, verbose, num_sigs, err_size;
	long total, startt;
	short tmp;

	tmp = 0;
	hps_intel = *(char *)&tmp;

	devname	= RAW_DEVNAME;
	rate	= RATE;
	random	= RANDOM;
	size	= SIZE;
	verbose	= 1;
	total	= 0;
	error	= 0;
	station	= 0;
	check	= 0;

	/* Figure out what program we are */
	strippath(argv[0]);
	if (strcmp(argv[0], DNAME) == 0)
	{
		tname = "DMA Loopback Diagnostic";
		min = DLB_DEV;
	}
	else if (strcmp(argv[0], RNAME) == 0)
	{
		tname = "Remote Loopback Diagnostic";
		min = RLB_DEV;
		size = RSIZE;
	}
	else
	{
		fprintf(stderr,
			"%s: This program must be invoked as either %s or %s\n",
			argv[0], DNAME, RNAME);
		usage(argv[0]);
		exit(1);
	}

	/* Set switches according to command line options */
	if (argc >= 2)
	{
		key = argv[1];
		i = argc - 2;
		while (*key)
		{
			switch (c = *key++)
			{
				case 'a':	/* Get station address */
					if (i == 0)
					{
						fprintf(stderr,
							"%s: Keyletter \"a\" needs an argument\n",
							argv[0]);
						usage(argv[0]);
						error = 1;
					}
					else
					{
						if (min != RLB_DEV)
						{
							fprintf(stderr,
								"%s: Cannot use keyletter \"a\"",
								argv[0]);
							usage(argv[0]);
							error = 1;
						}

						station = atoi(argv[argc - i]);
						if (station <= 0  ||  station > 255)
						{
							fprintf(stderr,
							"%s: Station address %d out of range.\n",
							argv[0], station);
							usage(argv[0]);
							error = 1;
						}
						i--;
					}
					break;
					
				case 'c':
					check = 1;
					break;

				case 'd': /* Pick up device name */
					if (i == 0)
					{
						fprintf(stderr,
							"%s: Keyletter \"d\" needs an argument\n",
							argv[0]);
						usage(argv[0]);
						error = 1;
					}
					else
					{
						devname = argv[argc - i];
						i--;
					}
					break;

				case 'l': /* Set throughput rate */
					if (i == 0)
					{
						fprintf(stderr,
							"%s: Keyletter \"l\" needs an argument\n",
							argv[0]);
						usage(argv[0]);
						error = 1;
					}
					else
					{
						rate = atoi(argv[argc - i]);
						if (rate < 0)
						{
							fprintf(stderr,
								"%s: Data rate %d out of range.\n",
								argv[0], rate);
							usage(argv[0]);
							error = 1;
						}
						i--;
					}
					break;

				case 'n': /* Set block count */
					if (i == 0)
					{
						fprintf(stderr,
							"%s: Keyletter \"n\" needs an argument\n",
							argv[0]);
						usage(argv[0]);
						error = 1;
					}
					else
					{
						number = atoi(argv[argc - i]);
						if (number < 0)
						{
							fprintf(stderr,
								"%s: Block count %d out of range.\n",
								argv[0], number);
							usage(argv[0]);
							error = 1;
						}
						else
							runtime = 0;
						i--;
					}
					break;
					
				case 'r': /* Use random sized blocks */
					random = 1;
					break;

				case 's': /* Get fixed block size */
					if (i == 0)
					{
						fprintf(stderr,
							"%s: Keyletter \"s\" needs an argument\n",
							argv[0]);
						usage(argv[0]);
						error = 1;
					}
					else
					{
						size = atoi(argv[argc - i]);
						if (size <= 0 || (min == DLB_DEV && size > 512) ||
							(min == RLB_DEV && size >RSIZE))
						{
							fprintf(stderr,
								"%s: Block size %d out of range.\n",
								argv[0], size);
							usage(argv[0]);
							error = 1;
						}
						else
							random = 0;
						i--;
					}
					break;

				case 't': /* Get running time */
					if (i == 0)
					{
						fprintf(stderr,
							"%s: Keyletter \"t\" needs an argument\n",
							argv[0]);
						usage(argv[0]);
						error = 1;
					}
					else
					{
						runtime = atoi(argv[argc - i]);
						if (runtime <= 0)
						{
							fprintf(stderr,
								"%s: Running time %d out of range.\n",
								argv[0], runtime);
							usage(argv[0]);
							error = 1;
						}
						else
							number = 0;
						i--;
					}
					break;

				case 'q': /* Don't report results to standard output */
					verbose = 0;
					break;

				default:
					fprintf(stderr,
						"%s: \"%c\" bad keyletter\n", argv[0], c);
					usage(argv[0]);
					error = 1;
					break;
			}
		}
	}
	else
	{
		usage(argv[0]);
		exit();
	}

	if (error)
		exit(1);
	
	pid = getpid();

	if (min == RLB_DEV  &&  station == 0)
	{
		fprintf(stderr, "%s: Need station address (keyletter \"a\")\n",
			argv[0]);
		usage(argv[0]);
		exit(1);
	}
#ifdef DEBUG
	fprintf(stderr,"station address = %d\n",station);  
	fprintf(stderr,"check buffer = %d\n",check);  
	fprintf(stderr,"device name = %s\n",devname);
	fprintf(stderr,"throughput rate = %d\n",rate);
	fprintf(stderr,"block count = %d\n",number); 
	fprintf(stderr,"random sized blocks = %d\n",random); 
	fprintf(stderr,"fixed block size = %d\n",size); 
	fprintf(stderr,"running time = %d\n",runtime); 
	fprintf(stderr,"results not to std output = %d\n",verbose);
#endif

	/* Arrange to catch all signals, so we can terminate gracefully. */
	for (i=1; i < NSIG ; i++)
	{
		if ((sig_func = signal(i, catcher)) == SIG_ERR)
			continue;
		if (sig_func == SIG_IGN)
			signal(i,SIG_IGN);
	}

	/* Open HPS raw device */
	if ((fd = open(devname, 2)) < 0)
	{
		if (errno)
			perror(argv[0]);
		fprintf(stderr, "%s: Cannot open device %s\n", argv[0], devname);
		exit(1);
	}

	/* Open on-board application */
	if ((cd = hps_open(fd, min, station, &mailbox)) < 0)
	{
		fprintf(stderr, "%s: Cannot open diagnostic task.\n", argv[0]);
		if (errno)
			perror(argv[0]);
		else
			errtab(mailbox >> 16, argv[0]);

		exit(1);
	}

	rate_hi = rate + rate/10;
	if (verbose)
		printf("%s: maximum rate = %d\n", argv[0], rate_hi);

	/* Initialize write buffer with test pattern */
	for (wcp=wbuff; wcp<wbuff+BUFF_SIZE; wcp++)
		*wcp = (int)wcp & 0xFF;

	/* Execute loopback */

	/* time with calls to time() instead of alarm signal.  9-17-86 jl */
	time ( &startt );
	while (error == 0  &&  (number > 0  ||  clock < runtime))
	{
		/* Write Buffer */
		if (random)
		{
			size = (rand() & 0x1FF) + 1; /* 1 <= size <= 512 */
			if (min == RLB_DEV && size > RSIZE)
				size = RSIZE;
		}

		if ((err_size = 
			hps_write(fd, min, cd, station, wbuff, size, &mailbox)) != size)
		{
			if (!gotintr)
			{
				fprintf(stderr, 
					"%s: Write error. Err size = %d, Expecting: %d\n",
										 argv[0], err_size, size );
				if (errno)
					perror(argv[0]);
				else
				{
					if (mailbox >> 16)
						errtab(mailbox >>16, argv[0]);
				}
				break;
			}
		}

		if (mailbox >> 16)
		{
			errtab(mailbox >>16, argv[0]);
			error = mailbox >> 16; /* Terminate */
			break;
		}

		/* Clear Buffer */
		rcp = rbuff;
		while (rcp < rbuff+BUFF_SIZE)
			*rcp++ = 0;

		/* Read Buffer */
		if ((err_size = 
				hps_read(fd, min, cd, station, rbuff, size, &mailbox)) != size)
		{
			if (!gotintr)
			{
				fprintf(stderr, "%s: Read error got %d expecting %d\n",
							argv[0],err_size, size);
				if (errno)
					perror(argv[0]);
				break;
			}
		}

		if (mailbox >> 16)
		{
			errtab(mailbox >>16, argv[0]);
			error = mailbox >> 16;
			break;
		}

		/* Check Buffer */
		if (check == 0)
		{
			rcp = rbuff;
			wcp = wbuff;
			while (rcp < rbuff + size)
			{
				if ((*rcp++ != *wcp++) && (!gotintr))
				{
					fprintf(stderr, "%s: Data error\n", argv[0], size);
					if (verbose)	
					{
						fprintf(stderr,
"size = 0x%x location = 0x%x  rbuf = %02x %02x %02x %02x %02x wbuf = %02x %02x  %02x %02x %02x\n",
						size, rcp - rbuff -1, 
						*(rcp-3)&0xFF,
						*(rcp-2)&0xFF,
						*(rcp-1)&0xFF,
						*(rcp+0)&0xFF,
						*(rcp+1)&0xFF,
						*(wcp-3)&0xFF,
						*(wcp-2)&0xFF,
						*(wcp-1)&0xFF,
						*(wcp+0)&0xFF,
						*(wcp+1)&0xFF);
					}
					error = 1; /* Terminate */
					break;
				}
			}
		}

		/* Compute throughput */

		/* time with time() instead of alarm signal.  9-17-86 jl */
		clock = time(0) - startt;

		total += (size * 2);
		if (clock > 0)
		{
			while ((total / clock)  >  rate_hi)
			{
				if (verbose)
					printf("*");
				/* change pause() to sleep(), and recalculate clock
				   because there are no longer any alarm signals.  
				   11/04/86 jl								*/
				sleep(1);
				clock = time(0) - startt;
			}
		}

		/* Done? */
		if (number)
			number--;
		if (gotintr)
			gotintr = 0;
	}
	
	/* Close on-board application if board has not been reset */
	if ( error != 0xffff )
	{
		if (hps_close(fd, min, cd, station, &mailbox) < 0)
		{
			if (errno)
				perror(argv[0]);
			else
				errtab(mailbox >> 16, argv[0]);

			exit(1);
		}
	}

	/* Report results to standard output */
	if (verbose)
	{
		if (clock == 0)
			clock = 1;
		printf("%s: Average throughput: %d bytes/second\n",
			argv[0], total / clock);
	}

	/* Close device */
	if (close(fd) < 0)
	{
		fprintf(stderr, "%s: Cannot close device\n", argv[0]);
		if (errno)
			perror(argv[0]);
		exit(1);
	}
	exit(0);
}

/* This routine catches all the signals */
void catcher(sig)
int sig;
{
 	if ((sig == SIGKILL) || (sig == SIGTERM) || (sig == SIGINT))
	{
		printf("killing process id #%ld\n",pid);
 		runtime = number = 0; 		/* Cease */
		gotintr = 1;
	}
	else
	{
		gotintr = 1;
 		printf("ignoring received signal %d\n",sig);
	}
		signal(sig, catcher);
}

usage(name)
char *name;
{
	fprintf (stderr, "%s: usage: %s [crqdlnst] [d_arg] [l_arg(chr/sec)] [n_arg] \n                    [s_arg] [t_arg(max_secs)]\n", name, DNAME);
	fprintf (stderr, "%s: or   : %s a[crqdlnst] [a_arg] [d_arg] [l_arg(chr/sec)]\n                    [n_arg] [s_arg] [t_arg(max_secs)]\n", name, RNAME);
}

strippath(fname)
char *fname;
{
		/* This routine will strip off the path from the
			executed program name					*/

		char *sptr,*outptr;
		char newname[30];
		register i;

		sptr = fname;
		outptr = newname;
		while (*sptr != '\0')
		{
			if (*sptr == '/')
			{
				sptr++;
				outptr = newname;
				*outptr = *sptr;
			}
			else
			{
				*outptr++ = *sptr++;
			}
		}
		*outptr = '\0';
		strcpy(fname,newname);
}

/*
 * MODIFICATION HISTORY:
 *
 * $Log:	hps_loop.c,v $
 * Revision 1.1.2.1  92/06/24  13:53:34  marker
 * Branch RISCOS_5_01_2_FIXES off of trunk.
 * 
 * Revision 2.6  91/10/02  10:31:19  leynette
 * Added in check error condition on hps_write call to see if an error was
 * returned in the case where the write failed.  The code was changed to
 * check if errno was set and call perror, but if the error was returned
 * in the mail field (i.e. sent from the TCS) we wouldn't see it.
 * ENHANCEMENT
 * 
 * Revision 2.5  91/09/03  17:37:15  annette
 * Changed error message handling.
 * INTERNAL
 * 
 * Revision 2.4  91/07/29  14:15:34  annette
 * Changed to catch all signals instead of running through a table of
 * signals.
 * INTERNAL
 * 
 * Revision 2.3  91/07/25  14:32:28  annette
 * Changed definition of catcher() and sig_func to voids.  Changed to lool
 * for SIG_ERR on error.
 * SPR=558
 * 
 * Revision 2.2  91/01/10  17:22:31  dave
 * Modify header; add footer.
 * COSMETIC
 * 
 */
