/*
 * Spew
 *
 * USAGE: spew dev [startdevno enddevno]
 *
 * Copyright (c) DigiBoard, Inc. 1990
 * --Dave Updegraff
 * 
 * To compile: CL /Od /MT spew.c (MSC 6.0)
 */
#define INCL_DOS
#include <os2.h>
#include <malloc.h>

#define VERSION	"1.00"
#define BUFFER_SIZE	256
#define STACKSIZE 	0x2000
#define MAXDEVICES	16

// These are the IBM defined FIFO bits in the DCB-flag3 (fbTimeout)
// that are not in the MicroSoft 'bsedev.h' definitions for the DCB.
#define	EXT_HARDWARE_DISABLE	0x08
#define	EXT_HARDWARE_ENABLE		0x10
#define	EXT_HARDWARE_AUTO		0x18

#define	EXT_HARDWARE_RX1		0x00
#define	EXT_HARDWARE_RX4		0x20
#define	EXT_HARDWARE_RX8		0x40
#define	EXT_HARDWARE_RX14		0x60

#define	EXT_HARDWARE_TX1		0x00
#define	EXT_HARDWARE_TX16		0x80

char		Devname[10], *Dev , *Bp;
char		Buffer[BUFFER_SIZE+1];
LONG		Speed, Basespeed, Output, Sem;
HFILE 		handles[MAXDEVICES];
USHORT		result, type, attrib, SizeK = 0;
SHORT		End, Start;
long		time ();


void spew_data();
void loop_data();
void busy_work ();

unsigned Devno;

DCBINFO		DCB_setting;
unsigned 	Commerr, Tracking, Loopback, Signals;
long		Rounds, sem_count;

main ( argc, argv )
int argc;
char *argv[];
{
int	left, err, j, tid, i , devno;
char	*Stack, wait;
TID	ThreadID;
long	speed, output;
MODEMSTATUS M_sigs;


// If insufficient arguments or '?', just give a USAGE message
	if ( argc < 2 )
	{
		printf ("DigiBoard serial port test program SPEW version %s\n",VERSION);
		printf ("USAGE: spew device_name [-[ln]]  [starno,endno] \n");
		printf ("       -l : No LOOPBACK test -- Transmit only\n");
		printf ("       -c : No CPU utilization information\n");
		printf ("       -s : No modem SIGNAL testing\n");
		printf ("  eg.   spew -l com31\n");
		printf ("MODEM signal test, then Tx-only throughput on COM31 w/CPU statistics\n");
		printf ("  eg.   spew -cs com 8,12\n");
		printf ("LOOPBACK test on COM8,COM9,..COM12\n");
		exit (0);
	}

// Parse out the args

	Dev = NULL;
	Start = End = -1;
// by default do it all
	Tracking = Loopback = Signals = 1;
	while ( argc-- > 1)
	{
		if ( isdigit (*argv[argc] ) )
		{
			sscanf (argv[argc],"%d,%d", &Start, &End );
			if ( (End - Start ) >= MAXDEVICES )
			{
				printf ("Too many devices.  Maximum = %d\n", MAXDEVICES);
				exit (1);
			}
			continue;
		}
		if ( *argv[argc] == '-' )
		{
			for (i=1; isalpha (*(argv[argc] + i)); i++)
			{
				switch (*(argv[argc]+i))
				{
					case 'c':
						Tracking = 0;
						break;
					case 's':
						Signals = 0;
						break;
					case 'l':
						Loopback = 0;
						break;
					default:
						printf ("unknown argument <%s>\n", argv[argc] );
						exit (1);
				}
			}
			continue;
		}
		if ( Dev == NULL )
		{
			Dev = argv[argc];
			continue;
		}
		printf ("Too many arguments.\n <%s>\n", argv[argc]);
		printf ("USAGE: spew device_name [starno,endno] \n");
		exit (1);
	}

	Speed = Basespeed = Output = 0L;

// All the IO threads pend on this which is not released 'till I'm ready.
	DosSemSet( &Sem );


	for ( i = Start, devno = 0; i <= End; i++,devno++ )
	{
		if ( i == -1 )		// Singe device only
		{
			strcpy ( Devname, Dev );
			i = End+1;
		}
		else
			sprintf ( Devname, "%s%d", Dev, i );
// Open the device{s}:
		if ( DosOpen ( 	Devname,
						&handles[devno],
						&result,
						0L,
						FILE_NORMAL,
						FILE_OPEN,
						(OPEN_ACCESS_READWRITE | OPEN_SHARE_DENYREADWRITE), 
						0L ) )
			{
				printf ("Failed to open device <%s>\n", Devname );
				exit (1);
			}

// Options for setting up the DCB.  Set them to whatever you like.  I just
// turn off FLOWCONTROL and enable such FIFO'ing as there may be..

		if (DosDevIOCtl( 	&DCB_setting,
							0L,
							ASYNC_GETDCBINFO,
							IOCTL_ASYNC,
							handles[devno]))
			return -1;

// If we're gonna test modem signals, need to take handshaking off
// I wanna test the modem signal connectors so first
// clear any flowcontrol that might be in effect for RTS, DTR

		if ( Signals )
		{
			DCB_setting.fbCtlHndShake &= ~(MODE_DTR_HANDSHAKE);
			DCB_setting.fbFlowReplace &= ~(MODE_RTS_HANDSHAKE);
			if ( DosDevIOCtl( 	0L,
								&DCB_setting,
								ASYNC_SETDCBINFO,
								IOCTL_ASYNC,
								handles[devno]))
					return -1;
// Current settings 
		
			printf ("<%s> : Initial conditions:\n", Devname);
			show_modem(&handles[devno]);

// Raise DTR and RTS - this should have the effect of having DTR, DCD, DSR up
			printf ("\nRaising DTR and RTS has effect of: \n");
			M_sigs.fbModemOn = DTR_ON | RTS_ON ;
			M_sigs.fbModemOff = 0xff;
			DosDevIOCtl(&err,&M_sigs,ASYNC_SETMODEMCTRL,
									IOCTL_ASYNC,handles[devno]);
			DosSleep ( 100 );
			show_modem(&handles[devno]);

// Drop DTR 
			printf ("\nDropping DTR and Raising RTS has effect of: \n");
			M_sigs.fbModemOn = RTS_ON ;
			M_sigs.fbModemOff = DTR_OFF;
			DosDevIOCtl(&err,&M_sigs,ASYNC_SETMODEMCTRL,
									IOCTL_ASYNC,handles[devno]);
			DosSleep ( 100 );
			show_modem(&handles[devno]);

// Drop RTS 
			printf ("\nRaising DTR and Dropping RTS has effect of: \n");
			M_sigs.fbModemOn = DTR_ON ;
			M_sigs.fbModemOff = RTS_OFF;
			DosDevIOCtl(&err,&M_sigs,ASYNC_SETMODEMCTRL,
									IOCTL_ASYNC,handles[devno]);
			DosSleep ( 100 );
			show_modem(&handles[devno]);

// Drop them both.  Note BIZZARRE way of doing this!!
			printf ("\nDropping both DTR and RTS has effect of:\n");
			M_sigs.fbModemOn = 0;
			M_sigs.fbModemOff = DTR_OFF & RTS_OFF ;
			DosDevIOCtl(&err,&M_sigs,ASYNC_SETMODEMCTRL,
									IOCTL_ASYNC,handles[devno]);
			DosSleep ( 100 );
			show_modem(&handles[devno]);
// end of modem signal testing..
		}

// If going to do Loopback,set HW flow

		if ( Loopback )
		{
			DCB_setting.fbCtlHndShake =	MODE_CTS_HANDSHAKE | MODE_DTR_CONTROL;
			DCB_setting.fbFlowReplace &= ~(MODE_RTS_HANDSHAKE|MODE_RTS_CONTROL);
			DCB_setting.fbFlowReplace |= MODE_RTS_HANDSHAKE;
		}
		else
		{
			DCB_setting.fbCtlHndShake &=
							~(MODE_CTS_HANDSHAKE | MODE_DSR_HANDSHAKE);
		}

// make sure the FIFO is active if we have one.
		if ( DCB_setting.fbTimeout & EXT_HARDWARE_AUTO )
			DCB_setting.fbTimeout  |= EXT_HARDWARE_ENABLE;

// force READs to wait longer, attempt to fulfill the whole request
		DCB_setting.fbTimeout |= MODE_READ_TIMEOUT;

// Set the timeouts to < our wakeup increment(5 secs)
		DCB_setting.usReadTimeout = 950;	// = 9.5 sec
		DCB_setting.usWriteTimeout = 950;	// = 9.5 sec

		if (DosDevIOCtl(	0L,
							&DCB_setting,
							ASYNC_SETDCBINFO,
							IOCTL_ASYNC,
							handles[devno]))
			return -1;


// Spawn off the data spewing.  
		if ( Loopback )
			_beginthread( loop_data, NULL, STACKSIZE, &handles[devno] );	
		else
			_beginthread( spew_data, NULL, STACKSIZE, &handles[devno] );	
	}
// and the busywork thread if we want to track ourselves
	if ( Tracking )
		_beginthread( busy_work, NULL, STACKSIZE, NULL ) ;


	if ( Tracking )
	{
		printf ("10 seconds while baseline CPU availability calculated..\n");

// After waiting for 10 seconds, we have a guess as to machine MIPs
		DosEnterCritSec();
		Speed = 0;
		DosExitCritSec();
		DosSleep (10000);
		DosEnterCritSec();
		Basespeed = Speed;
		DosExitCritSec();

		printf ("Basespeed=%ld, %d devices....\n", Basespeed, devno);
	}
	else
		DosSleep(100);	// pause 1/10 sec

// Let the spewing begin!
	DosSemClear( &Sem );

	DosEnterCritSec();
	Speed = Output = 0;
	DosExitCritSec();
	while ( 1 )
	{
		DosSleep(10000);
		DosEnterCritSec();
		speed = Speed;
		output = Output;
		Speed = Output = 0;
		DosExitCritSec();
		
// Lets not fool ourselves that this will never happen...
		if ( speed > Basespeed )
			Basespeed = speed;

		if ( Tracking )
			printf ("CPU: %ld%%, %ld bytes/sec/channel (%d channels). ",
				((Basespeed - speed)*100)/Basespeed,
				((output*BUFFER_SIZE)/(devno*10)), 
				devno);
		else
			printf ("%ld bytes/sec/channel (%d channels).",
				((output*BUFFER_SIZE)/(devno*10)), 
				devno);

		if ( Loopback )
			printf (" -Full duplex-\r");
		else
			printf (" -Transmit only-\r");
	}

	exit (0);
}

// unverified pure transmit
void spew_data (HFILE *fp)
{
USHORT bytes ;

// Wait for the main thread to release us
	if  ( DosSemWait(&Sem, SEM_INDEFINITE_WAIT) )
		return ;
// crank it out 
	while ( 1 )
	{
		if (  DosWrite (*fp, Buffer, BUFFER_SIZE, &bytes) )
		{
			printf ("Failed to write!! \n");
			DosExit (EXIT_PROCESS, 1);
		}
		Output++;
	}
}

// Tx and Rx, token check of first & last bytes 
void loop_data (HFILE *fp)
{
USHORT bytes, request, gotback, jnk ;
unsigned char *rbuf;
LONG	slem ;

	if (( rbuf = malloc ( BUFFER_SIZE )) == NULL )
	{
		printf ("Failed to malloc space in spawned thread\n");
		return;
	}

// markers; 
	Buffer[0] = 'X';
	Buffer[BUFFER_SIZE/2] = 'Y';
	Buffer[BUFFER_SIZE-1] = 'Z';

// Wait for the main thread to release us
	if  ( DosSemWait(&Sem, SEM_INDEFINITE_WAIT) )
		return ;

// Make the writes Asy then pend on the read.
	while ( 1 )
	{
		DosWriteAsync (*fp, &slem, &jnk, Buffer, BUFFER_SIZE, &bytes) ;

// make sure that these values are initially wrong so Read will overwrite
		*rbuf = *(rbuf + BUFFER_SIZE - 1) = '\0';

		for (	gotback=0, memset(rbuf, 0, BUFFER_SIZE), request = BUFFER_SIZE; 
				gotback < BUFFER_SIZE;
				gotback += bytes, request = BUFFER_SIZE - gotback 
			)
			if ( DosRead (*fp, rbuf+gotback, request, &bytes) || bytes == 0)
			{
				printf ("Failed to READ!! \n");
				DosExit (EXIT_PROCESS, 1);
			}

// token checks of first, middle, and last bytes
		if (	*rbuf == Buffer[0] && 
				*(rbuf+BUFFER_SIZE/2) == Buffer[BUFFER_SIZE/2] &&
				*(rbuf+BUFFER_SIZE-1) == Buffer[BUFFER_SIZE-1] )
			Output++;
		else
			printf ("Lost Data on Round %d\n", Output );

	}
}

void busy_work ()
{
unsigned char far	*there;
int				i,j;

// `nice` myself

	DosSetPrty (PRTYS_THREAD, PRTYC_IDLETIME, PRTYD_MINIMUM, 0);

	if (( there = (unsigned char far *)malloc ( 0x200 ))== NULL )
	{
		printf ("Failed to malloc a scratch space\n");
		exit (1);
	}


// this is NOT a 'stone of any kind and you MUST disable ALL optimizations
// if this busy work is to pretend to be busy
	while ( 1 )
	{
		for (i=0; i < 0x200; i++ )
			*(there+i) = 0xff;
		for (i=0; i < 0x200; i++ )
			*(there+i) /= 0x11;
	Speed++;
	}
}


show_modem (HFILE *fp)
{
unsigned char min, mout;

	DosDevIOCtl(&mout,0L,ASYNC_GETMODEMOUTPUT, IOCTL_ASYNC, *fp);
	DosDevIOCtl(&min, 0L,ASYNC_GETMODEMINPUT,  IOCTL_ASYNC, *fp);

	printf ("Modem OUTPUT signals : DTR=%d, RTS=%d\n",
			((mout & DTR_ON)>0),((mout & RTS_ON)>0));
	printf ("Modem INPUT signals: CTS=%d, DSR_ON=%d, RI_ON=%d, DCD_ON=%d\n",
			((min & CTS_ON)>0),((min & DSR_ON)>0),
			((min & RI_ON)>0),((min & DCD_ON)>0) );
	printf ("..press ENTER..");
	getchar();
}
