/*******************************************************************************
*	TPT Tape test utility.  This program tests tape writes.		       *
*									       *
*		tpt :==$sys$manager:tpt					       *
*		tpt msa0: 65534	6250					       *
*	NOTES:								       *
*									       *
*	 - In the above example, 65534 is the blocksize.  This may be between  *
*          14 and 65534 inclusive.  Density may be 1600 or 6250, but defaults  *
*	   to 6250.							       *
*									       *
*	 - This test should be run on a completely idle system.  I/O contention*
*	   will cause more problems than CPU contention.		       *
*									       *
*	 - The size of the IRG (interrecord gaps) have been estimated.	       *
*									       *
*	 - Tape controllers which emulate DEC TS11 and use the DEC TSDRIVER    *
*	   cannot set density.  Default density is selected on the controller  *
*	   and is typically 6250, but may be overidden on the front panel of   *
*	   the drive.							       *
*									       *
*	 - This program buffers up to 32 writes in order to get maximum tape   *
*	   speed.  We use event flags 32 through 63 since event flags 24       *
*	   through 31 are reserved for system use, and we want to allow the    *
*	   program to use a contiguous block of event flags.		       *
*									       *
*	 - TPT only uses 2300 feet of tape.				       *
*******************************************************************************/

#include <stdio.h>
#include <ssdef.h>
#include <mntdef.h>
#include <iodef.h>

#define BFSZ 		65534
#define TAPE_LENGTH	27600		/* Quit after 2300 feet */
#define QSIZE		32		/* May not exceed 32 */

#define DMT$M_NOUNLOAD 1
#define DMT$M_CLUSTER  8

struct DESC
{
	unsigned short length;
	unsigned char type;
	unsigned char class;
	char *addr;
};

struct ITEM
{
	short size;
	short item_code;
	char *ret_addr;
	long *len_addr;
};

struct IOSB
{
	unsigned short status;
	unsigned short count;
	long devdep;
};

#define CLASS_ARRAY 4
#define TYPE_STRING 14

main(argc,argv)
int argc;
char *argv[];
{
	struct DESC drv;
	int density, fd, mntflags, blksz, counter, errors, inc;
	int start_time, elapsed_time, bytes_sec, tape_used, irg;
	int ef_counter, status, sts[QSIZE], lasterr, curr;
	double finc, tape_counter, tape_inc;

	struct IOSB iosb[QSIZE];

	struct 
	{
		struct ITEM dev_item;
		struct ITEM flag_item;
		struct ITEM blk_item;
		struct ITEM dens_item;
		struct ITEM rec_item;
		long endlist;
	} ilst;

	char drive[80];
	char bf[BFSZ];

/* Get drive and blocksize */
	argv++;					/* skip first parm (bounce) */
	if (argc < 3)				/* parse optional parameter */
		{
		printf ("Syntax is TPT MSA0: 63000 6250\n");
		exit(SS$_NORMAL);
		}

	sscanf(*argv,"%s",drive);
	for (counter=0;drive[counter] != 0;counter++)
		drive[counter] = toupper(drive[counter]);
	argv++;
	sscanf(*argv,"%d",&blksz);
	if ((blksz < 14) || (blksz > 65534))
		{
		printf("Blocksize must be between 14 and 65534 inclusive.\n");
		exit(SS$_NORMAL);
		}
	finc = ((1 / (double)blksz) * 6600000);
	inc = finc;
	inc = ((inc / 100) * 100);
	if (inc > 1000)
		inc = 1000;

	drv.addr = drive;
	drv.length = strlen(drv.addr);

	if (argc == 4)
		{
		argv++;
		sscanf(*argv,"%d",&density);
		if (density == 1600)
			tape_inc = ((double)blksz / (double)density) + .6;
 		    else if (density == 6250)
			tape_inc = ((double)blksz / (double)density) + .3;
		    else
			{
			printf("Density must be 1600 or 6250.\n");
			exit(SS$_NORMAL);
			}
		}
	    else
		{
		density = 6250;
		tape_inc = ((double)blksz / (double)density) + .3;
		}

/* Issue warning if TSDRIVER is being used */
	if ((drive[0] == 'M') && (drive[1] == 'S'))
		{
		printf("Note:  Drive %s uses the DEC TSDRIVER, which\n",drive);
		printf("       does not support multi-density drives.  Be\n");
		printf("       sure that the drive is set to %d BPI.\n\n",
			density);
		}

/* Put something in the buffer */
	for (counter=0; counter<BFSZ; counter++)
		bf[counter] = 255;

/* Do mount */
	ilst.dev_item.size = drv.length;
	ilst.dev_item.item_code = MNT$_DEVNAM;
	ilst.dev_item.ret_addr = drv.addr;
	ilst.dev_item.len_addr = 0;

	mntflags = (MNT$M_FOREIGN || MNT$M_NOASSIST);
	ilst.flag_item.size = 4;
	ilst.flag_item.item_code = MNT$_FLAGS;
	ilst.flag_item.ret_addr = &mntflags;
	ilst.flag_item.len_addr = 0;

	ilst.blk_item.size = 4;
	ilst.blk_item.item_code = MNT$_BLOCKSIZE;
	ilst.blk_item.ret_addr = &blksz;
	ilst.blk_item.len_addr = 0;

	ilst.dens_item.size = 4;
	ilst.dens_item.item_code = MNT$_DENSITY;
	ilst.dens_item.ret_addr = &density;
	ilst.dens_item.len_addr = 0;

	ilst.rec_item.size = 4;
	ilst.rec_item.item_code = MNT$_RECORDSIZ;
	ilst.rec_item.ret_addr = &blksz;
	ilst.rec_item.len_addr = 0;

	ilst.endlist = 0;

	status = sys$mount(&ilst);
	if (!(status & 1))
		exit(status);

/* Next, open a channel to the drive */
	status = sys$assign(&drv, &fd, 0, 0);
	if (!(status & 1))
		exit(status);
	
/* Prepare for writing */
	lasterr = SS$_NORMAL;
	curr = errors = counter = 0;
	tape_counter = 0;

/* Prime the queue */
	for(curr = 0, start_time = time();curr < QSIZE;curr++)
		sts[curr] = sys$qio(curr + QSIZE,fd, IO$_WRITEVBLK,&iosb[curr],
			0, 0, bf, blksz, 0, 0, 0, 0);

	for(curr=0;tape_counter < TAPE_LENGTH;)
		{
		sys$waitfr(curr + QSIZE);
		if (!(iosb[curr].status & 1))
			{
			if (iosb[curr].status == SS$_ENDOFTAPE)
				break;
			  else
				{
				errors++;
				lasterr = iosb[curr].status;
				printf("** Error %d on block %d\n",
					iosb[curr].status,(counter+1));
				}
			}
		counter++;
		tape_counter += tape_inc;
		sts[curr] = sys$qio(curr + QSIZE,fd, IO$_WRITEVBLK,&iosb[curr],
			0, 0, bf, blksz, 0, 0, 0, 0);
		if (++curr == QSIZE)
			curr = 0;
		}
/* Quick... stop the timer! */
	elapsed_time = time() - start_time;

/* Cancel any outstanding writes */
	status = sys$cancel(fd);
	if (!(status &1))
		printf("\nError %d cancelling write queue\n",status);

/* Done... display the results */
	bytes_sec = (counter * blksz) / elapsed_time;
	if (density == 1600)
		irg = .6 * counter;
	    else
		irg = .3 * counter;
	tape_used = ((counter * blksz) / density) + irg;

	printf ("\nRun summary:\n");
	printf ("\tDrive:            %s\n",drive);
	printf ("\tBlocksize:        %d\n\n",blksz);
	printf ("\tDensity:          %d\n",density);
	printf ("\tBlocks written:   %d\n",counter);
	printf ("\tBytes written:    %d\n",counter * blksz);
	printf ("\tTape used:        %d inches for data\n",tape_used - irg);
	printf ("\t                  %5d inches for IRG\n",irg);
	printf ("\t                  -----\n");
	printf ("\t                  %d inches total (%d feet)\n",
		tape_used, tape_used / 12);
	printf ("\tElapsed time:     %d seconds\n",elapsed_time);
	printf ("\tData rate:        %d bytes/sec\n",bytes_sec);
	printf ("\tTransport speed:  %d IPS\n",tape_used / elapsed_time);
	printf ("\tErrors:           %d\n",errors);
	printf ("\tLast error:       %d\n",lasterr);

/* Close the channel to the drive */
	status = sys$dassgn(fd);
	if (!(status & 1))
		{
		printf("Error deassigning tape\n");
		exit(status);
		}

/* Dismount the drive */
	status = sys$dismou(&drv,DMT$M_NOUNLOAD | DMT$M_CLUSTER);
	if (!(status & 1))
		{
		printf("Error dismounting tape\n");
		exit(status);
		}
	exit(SS$_NORMAL);
}
