/*
BEGIN DOCUMENTATION

Name: LOOP.C					Created: 04/26/84  DTS
					    Last Update: 04/30/84
Title: Loopback Test for RIA system

Index: Loopback Test, Diagnostics

Abstract: Output to port that has loopback connector attached, check
	  for compare errors on input.

Usage: LOOP term

Parameters: term - Terminal port to which test is run.

	    Special Input characters from terminal:

	    Control-C		Terminate test program.
	    Control-W or
	    Control-R		Refresh entire screen.

Environment: RSX11M V4.0, RSX11M+ V2.1, DECUS C 

See Also:	IICHECK for RIA instrument interface micro computers.

Description: Outputs a continuous stream of characters to named terminal
	     port.  Inputs from same port and checks for match of characters.
	     Any errors are reported to console. Test runs until ^C pressed.
	     Every 32 characters a message is printed with the pass number.
	     Each pass contains 128 different characters which are output to
	     the port under test.  If no characters are returned for 20 seconds
	     the routine will timeout and terminate the current pass.  The
	     port under test and TI: are both set to typeahead & full duplex.
	     LOOP assumes a VT100 or VT52 is the terminal starting the test.
	     In addition to the pass count and count of compare errors a
	     message is output containing the I/O result code returned in 
	     the I/O status block (iosb) whenever it is not IS_SUC (1).

Example(s): Input Command:

			LOOP TT5: (to run test to port TT5)

	    Output Display: (Clear screen)

			Serial loopback test for port TT5:

			Pass Number 2

			Passes with errors = 1   Input timeouts = 1

			Characters output = 32

			Characters input in error = 10

		Last bad compare output = 40, input = 0, pass = 1.


	The test runs continously, updating the counters on the screen until
	a control-C is pressed.  Note that all numbers are displayed in decimal
	except the input & output character values which are displayed in octal.

Uses:	Used for RIA testing with instrument interface running software
	loopback.  In this mode the port under test should be set to 'SLAVE',
	then the instrument interface micro should be started from location
	IICHECK+4, and then the loopback test should be started from the PDP-11.

Internal: Uses AST to asynchonously input from terminal port (TI:)

Update History:

END DOCUMENTATION
*/


#include <stdio.h>
#include <clang.c>
#include <cx.h>

/* #define debug 0 */

#define BUFSIZ  800	/* Output buffer size */
#define BSIZ	128	/* test buffer size */
#define BLOCKSIZ 32	/* I/O block size */
#define TIFLAG 4	/* Event flags for input */
#define INFLAG 5
#define WTMASK 030	/* Or of flag 4 & flag 5 */
#define TILUN 4		/* Lun for terminal TI */
#define TTLUN 5		/* Lun for external port */

#define IS_TMO 002

#define IO_ATT 01400
#define IO_RAL 01010
#define IO_RST 01001
#define IO_WVB 011000
#define IO_WAL 0410
#define IO_KIL 012
#define IO_DET 02000

#define TF_ESQ 020
#define TF_NOT 002
#define TF_RNE 020
#define TF_TMO 0200

#define SF_SMC 02440		/* Set multiple characteristics */
#define	TC_WID 01		/* Terminal Width */
#define TC_RAT 07		/* Typeahead mode - RSX11M only */
#define TC_ACR 024		/* Wrap */
#define TC_FDX 064		/* Full duplex */


extern int qio();	/* DECUS C library - RSX functions */
extern int qiow();
extern wtse();
extern wtlo0();
extern int $dsw;

extern rdast();		/* Forward reference */

    int		iosbin[2];	/* I/O status blocks */
    int		iosbout[2];
    int		iosbti[2];
    int		devpar[6];	/* Qio parms */
    int		indpar[6];
    int		otdpar[6];
    static	char tichar;		/* Character input from console */
    static	char inch = EOS;	/* Last character transmitted in error */
    static	char outch = EOS;	/* Last character read in error */
    static	long int  chcnt = 0;	/* Status counters for display */
    static	int  passcnt = 1;
    static	int  passerr = 0;	/* Error counters for display */
    static	long int  cherr = 0;	/* Characters received in error */
    static	int  timeout = 0;	/* Characters received in error */
    char	obuf[BSIZ];		/* Output buffer for test */
    char	ibuf[BSIZ];		/* Input buffer for test */
    static	char buff[BUFSIZ];
    static	char term[]= "TI0: ";	/* Console Device Name */
    char	port[5];
    boolean	abort,comperr,timoterr;	/* Globals returned to main */


main(argc,argv)

char *argv[];

{
    register	int i,c;
    char	**oldbuf;

	iff argc != 2 or ((c = strlen(argv[1])) > 5) or (c < 2) then
	    error("Format is: LOOP TTn:");
	cpystr(port,argv[1]);


	/* Initialize Test */


    attach(term,TILUN);		/* Attach Terminals */
    attach(port,TTLUN);
    iosbin[0] = iosbout[0] = IS_SUC;	/* Default to good */
    for(i = 0; i < BSIZ; i++)	/* Initialize output buffer */
	obuf[i] = (char) i;
    
    /* Set buffered output & clear screen */

    iff scset(buff,sizeof(buff),&oldbuf) == 0
        then error("Scset error");
    dispscrn();			/* Display static screen */
    readti();			/* Check for control C */

/***********************************************\
* MAIN PROCEDURE:				*
* DO forever:					*
*	Output Pass #				*
*	output error count			*
*	INPUT 256 CHARACTERS (async w/ timeout)	*
*	OUTPUT 256 char Test pattern		*
*	WAIT for input done			*
*	If ^C from TI: abort			*
*	else check for errors			*
*	If timeout error Kill I/O to test port	*
* END DO					*
************************************************/

    repeat			/* Cycle forever */
    {
	comperr = timoterr = false;
	for(c = 0; (c < BSIZ) and (not abort); c += BLOCKSIZ)
	{
	    dispstat();			/* Display status */
	    indpar[0] = &ibuf[c];	/* Start of i/o */
	    indpar[1] = BLOCKSIZ;	/* I/O count */
	    indpar[2] = 2;		/* Timeout, 10 sec intervals */
					/* Input from test port */
	    iff qio(IO_RAL | TF_TMO | TF_RNE,TTLUN,INFLAG,iosbin,0,indpar) < 0 then
		error("\nRead qio error, dsw = %d",$dsw);

	    otdpar[0] = &obuf[c];	/* Start of i/o */
	    otdpar[1] = BLOCKSIZ;	/* I/O count */
	    otdpar[2] = 0;		/* No vfc added to output */
					/* Output test pattern */
	    iff qio(IO_WAL,TTLUN,0,iosbout,0,otdpar) < 0 then
		error("\nRead qio error, dsw = %d",$dsw);

	    wtlo0(WTMASK);		/* Wait for Input Done before Retesting */
	    iff !(abort) then
	    {
		compbuf(&ibuf[c],&obuf[c]);	/* Test for errors */
		chcnt += BLOCKSIZ;
		iff timoterr then		/* Abort pass on timeout */
		{
		    timeout++;
		    iff qio(IO_KIL,TTLUN,INFLAG,iosbin,0,0) < 0 then	/* Stop I/O */
			error("\nTimeout I/O Kill error, dsw = %d",$dsw);
		    wtlo0(WTMASK);		/* Wait for QIO Done or abort */
		    break;			/* Break from for loop */
		}
	    }
	}
	iff comperr then
	    passerr++;
	passcnt++;
    }
    untill(abort));


/*    scout(0,0,NULL);		* Flush buffers *
    scput(oldbuf);
*/
    detach(TILUN);
    detach(TTLUN);

} /* end main */



/*** ATTACH: Attach Named Terminal, Set full duplex & typeahead ***/

proc attach(devnam,lun)

    char *devnam;
    int	 lun;    
{
    union {
	   int	dev;
	   char name[2];
	  } dnam;

    static int	fnc;		/* parm definitions for qio */
    static int	 unit,eflag;
    static int ttchar[6];
    register int i,c;

	/* assign device to given lun */

	for (i = 0; i < 2; i++)
	    dnam.name[i] = ifx islower(c = *devnam++) thenx
		c - ('a' - 'A') elsex c;
	
	unit = otoi(devnam);

#ifdef debug
	printf("Dev = '%c%c', Unit = %d\n",dnam.name[0],dnam.name[1],unit);
#endif

	    alun(lun,dnam.dev,unit);


	/* Attach terminal to capture all characters & allow typeahead */

	fnc = IO_ATT;
	eflag = TIFLAG;
	iosbti[0] = 0;
	devpar[0] = 0;
	devpar[1] = 0;
	devpar[2] = 0;


	iff qiow(fnc,lun,eflag,iosbti,0,devpar) < 0
	    then error("\nAttach error dsw = %d\n",$dsw);



	/* Set terminal characteristics for fullduplex & typeahead */

	fnc = SF_SMC;
	ttchar[0] = TC_FDX + 256;	/* Set full duplex */
	ttchar[1] = TC_ACR + 0;		/* Clear wrap */
	ttchar[2] = TC_RAT + 256;	/* Set typeahead */
	ttchar[3] = TC_WID + (256 * BSIZ);	/* Set width if not TI: */
	devpar[0] = &ttchar;
	devpar[1] = ifx dnam.name[1] == 'I' thenx 6 elsex 8;	/* Bytes in ttchar table */

	iff qiow(fnc,lun,eflag,iosbti,0,devpar) < 0
	    then error("\nATTACH - SMC error dsw = %d\n",$dsw);

} /* end attach */



	/*** Detach terminal & kill outstanding I/O ***/

proc detach(lun)

    int	lun;
{
	iff qio(IO_KIL,lun,0,iosbti,0,0) < 0
	    then error("\nI/O Kill error, lun %d",lun);

	iff qio(IO_DET,lun,lun+2,iosbin,0,0) < 0	/* was qiow */
	    then error("\nDetach error, lun %d",lun);

} /* end detach */




/*** OTOI: Convert Octal Ascii number to Integer ***/

int
proc otoi(octnum)

    char *octnum;
{
    register 	int i, c;

	for (i = 0; ((c = *octnum++) >= '0') and ((c -= '0') < 8); i += c)
	    i <<=3;


	return(i);

} /* end otoi */



/*** DISPSCRN:  Display initial screen ***/

proc dispscrn()
{
    scerpg(1,1);
    scout(2,20,"Serial loopback test for port");
    scout(2,50,port);
    scout(6,20,"Pass Number");
    scout(8,20,"Passes with errors =");
/*    scout(8,50,"Input timeouts ="); */
    scout(10,20,"Characters output =");
    scout(12,20,"Characters input in error =");
} /* end dispscrn */



/*** DISPSTAT:	Display counters to screen ***/

proc dispstat()
{
    char temp[80];

	sprintf(temp,"%u ",passcnt);
	scout(6,32,temp);		/* Pass count */
	sprintf(temp,"%u    Input timeouts = %u   ",passerr,timeout);
	scout(8,41,temp);		/* Passes in error */
	sprintf(temp,"%lu ",chcnt);
	scout(10,41,temp);		/* Characters output */
	sprintf(temp,"%lu ",cherr);
	scout(12,49,temp);		/* Characters input in error */
	iff (iosbin[0] & 0377) != IS_SUC then
	{
	    sprintf(temp,"Input error, iosb = %o, %o        ",iosbin[0],iosbin[1]);
	    scout(16,10,temp);
	}
	else scerln(16,10);
	iff (iosbout[0] & 0377) != IS_SUC then
	{
	    sprintf(temp,"Output error, iosb = %o, %o        ",iosbout[0],iosbout[1]);
	    scout(18,10,temp);
	}
	else scerln(18,10);
	iff inch != outch then
	{
	    sprintf(temp,"Last bad compare output = %o, input = %o, pass = %u.      ",(int) outch, (int) inch,passcnt);
	    scout(20,10,temp);
	}
	scout(23,1,NULL);		/* Flush buffer */
} /* end dispstat */


/*** COMPBUF: Compare input buffer to output buffer, increment error counters ***/

proc compbuf(ich,och)
    register charpointer ich,och;
{
    register int i,count;

	inch = outch = EOS;
	iff (iosbin[0] & 0377) == IS_TMO then
	{
	    timoterr = true;		/* Set timeout flag */
	    count = iosbin[1];
	}
	else count = BLOCKSIZ;
	for(i = 0; i < count; i++)
	    iff *ich++ != *och++ then	/* Compare characters in buffer */
	    {
		cherr++;		/* Increment character error count */
		inch = *(ich - 1);
		outch = *(och - 1);
	    }
	iff (inch != outch) or (count < BLOCKSIZ) then
	    comperr = true;		/* Mark passes in error */
} /* end compbuf */


/*** READTI:	Use Qio to Read one character from TI:    ***/
/***		Set for RDAST routine to check ^C	  ***/
/***		Clears TIFLAG until character ready	  ***/

proc readti()
{
	/*** Do qio IO.RST for 1 character. ***/

	devpar[0] = &tichar;		/* Set start of I/O */
	devpar[1] = 1;			/* Number of characters */
	devpar[2] = 0;			/* No Timeout */

	iff qio(IO_RST | TF_RNE,TILUN,TIFLAG,iosbti,rdast,devpar) < 0
	    then error("TI: Read error, dsw = %d",$dsw);

} /* end readti */


/*** rdast:  entered when read is done,  Sets abort if ^C    ***/

proc rdast()
{
    register pointer iosba;
    register int   stat;

	astset();	/* Save registers */

	iosba = gtdp(0);	/* Get IOSB addr from stack */

	iff (stat = *iosba & 0377) == IS_SUC then
	{
	    iff iosbti[1] == 0 then
		tichar = (char) (iosbti[0] >> 8);	/* High byte is terminator */
	    iff tichar == '\03' then		/* ^c */
		abort = true;
	    else iff (tichar == '\027') or (tichar == '\018') then	/* ^w or ^r */
	    {
		dispscrn();			/* Refresh display */
		dispstat();
	    }
	}
	else iff stat != IS_TMO then
	{
	    abort = true;	/* Note: TIFLAG is still set */
	}
	iff not abort then
	    readti();		/* Fire off next qio, clear flag */

	astx(1);		/* remove iosb from stack on exit */

} /* end rdast */
