/*
 *****************************************************************
 *                                                               *
 *    Copyright (c) Essential Communications Corp, 1997          *
 *                                                               *
 *   All Rights Reserved.  Unpublished rights  reserved  under   *
 *   the copyright laws of the United States.                    *
 *                                                               *
 *   RESTRICTED RIGHTS LEGEND   Use, duplication, or disclosure  *
 *   by the U.S. Government is subject to restrictions  as  set  *
 *   forth in Subparagraph (c)(1)(ii)  of  DFARS  252.227-7013,  *
 *   or  in  FAR 52.227-19, as applicable.                       *
 *                                                               *
 *****************************************************************
 *
 * File: blast.c
 *
 * $Source: /usr/users/releng/repos/src/utils/blast.c,v $
 * $Revision: 1.28 $ $Locker:  $
 * $Date: 1999/01/05 23:25:31 $ $Author: droux $ $State: Exp $
 */

#ifndef __lint
static char rcsid[] = "$Header: /usr/users/releng/repos/src/utils/blast.c,v 1.28 1999/01/05 23:25:31 droux Exp $";
#endif /* __lint */

/*
 * blast 
 *
 * Test HIPPI-FP interface output performance.
 *
 */

#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/times.h>
#include <sys/ioctl.h>

#if defined(__sgi)
#include <getopt.h>
#include <time.h>
#include <sys/mman.h>
#include <sys/endian.h>
#endif

#ifdef sun
#include <sys/param.h>
#endif /* sun */

#include "essioctl.h"
#include <errno.h>
extern int errno;

static char *src_err_name[] =
{ "no error", "HIPPI source sequence error", "HIPPI source lost DSIC",
      "HIPPI source timeout", "HIPPI source lost CONNECT",
      "HIPPI connect rejection", "HIPPI interface shutdown",
      "Unexpected driver failure" };

#if defined(__sgi)
static char *usage =
	"%s [-2PMrkCcU] [-D <unit>] [-I <ifield>] [-l <size>] \n"
	"[-n <writes-per-pass>] [-m <passes>] [-u <ulp>] [-R <file>]\n";
#else /* defined(__sgi) */
static char *usage =
	"%s [-2PrCcU] [-D <unit>] [-I <ifield>] [-l <size>] \n"
	"[-n <writes-per-pass>] [-m <passes>] [-u <ulp>] [-R <file>]\n";
#endif /* defined(__sgi) */

char *dev[] = {NIC0, NIC1, NIC2, NIC3, NIC4, NIC5, NIC6, NIC7, NIC8,
		NIC9, NIC10, NIC11, NIC12, NIC13, NIC14, NIC15};
int devno, nics;

#if __osf__ || __hpux || __NetBSD__ || _AIX || defined(sun)
#define DEV_NAME "hip"
#endif
#if __sgi
#define DEV_NAME "ess"
#endif

#define MEG             (1024*1024)

#if defined(__hpux) || defined(__osf__) || defined(__NetBSD__) || _AIX
static void *memalign(int, int);
#endif /* defined(__hpux) || defined(__osf__) || defined(__NetBSD__) || _AIX */
#if defined(__sgi)
#if defined(ESS_IRIX64)
#define MEM_ALIGN 16384
#else
#define MEM_ALIGN 4096
#endif /* defined(ESS_IRIX64) */
#endif /* defined(__sgi) */
#if defined(__hpux)
#define MEM_ALIGN 4096
#endif /* defined(__hpux) */
#if defined(__osf__) || defined(__NetBSD__)
#if defined(__alpha)
#define MEM_ALIGN 8192
#else /* defined(__alpha) */
#define MEM_ALIGN 4096
#endif /* defined(__alpha) */
#endif /* defined(__osf__) || defined(__NetBSD__) */
#if _AIX
#define MEM_ALIGN 4096
#endif	/* _AIX */
#ifdef sun
#define MEM_ALIGN PAGESIZE
#endif /* sun */


main(int argc, char *argv[])
{
    register int i, j, k;
    int	c, fd, status, retv, child, errs=0, rc;
    u_int	*buf, *bufw;
    u_char      *cbuf, curChar;
    struct tms tm_dummy;
    clock_t	start_time, end_time;
    float	mbytes, t;
    
    int	len1, len = 4 * 1024;
    int	n = 1, m = 1;
    int	ulp = 0x82;
    int	verbose = 0;
    int random = 0;
    int	double_thread = 0;
    int	bigPacket = 0;
    int conns = 0;
    int check = 0;
    int	Mflag = 0;
    int infinite = 0;
    int is_ph = 0;
    long seed;
    u_int cksm = 0, pkt_off = 0;
    u_int	I = 0x00000000;
    char	*device_name = DEV_NAME"0";
    char	*report_file = "";
#if defined(__sgi)
    int mapkern = 0;
    int spfd;
#endif /* defined(__sgi) */
    
    while ((c = getopt(argc, argv, "UvrPCcD:I:2l:n:m:u:R:Mhk")) != EOF)
	switch (c) {
	  case 'U': infinite++;                                 break;
	  case 'r': random++;                                   break;
	  case 'v': verbose++;					break;
	  case 'D': device_name = optarg;			break;
	  case 'I': I = (u_int)strtoul(optarg, (char **)0, 0);	break;
	  case '2': double_thread++;				break;
	  case 'l': len = (u_int)strtoul(optarg, (char **)0, 0);	break;
	  case 'c': check++;					break;
	  case 'n': n = atoi(optarg);				break;
	  case 'm': m = atoi(optarg);				break;
	  case 'u': ulp = (u_int)strtoul(optarg, (char **) 0, 0);	break;
	  case 'P': bigPacket++;				break;
	  case 'C': conns++;				        break;
	  case 'R': report_file = optarg;		        break;
#if defined(__sgi)
	  case 'M': Mflag++;					break;
	  case 'k': mapkern++;                                  break;
#endif /* defind(__sgi) */
	    
	  case 'h':
	  default:
	    errs++;
	}

    if (double_thread && (conns || bigPacket)) {
	(void) fprintf(stderr,"blast: parameter error\n" );
	(void) fprintf(stderr,"blast: (-2 && (-C || -P))\n" );
	errs++;
    }
    if (len < 0 || n < 1 || m < 1) {
	(void) fprintf(stderr,"blast: parameter error\n" );
	(void) fprintf(stderr,"blast: (len < 0 || n < 1 || m < 1)\n" );
	errs++;
    }
    if ((len & 7) && bigPacket) {
	(void) fprintf(stderr,"blast: parameter error\n" );
	(void) fprintf(stderr,"blast: Length must be multiple of 8 with -P\n" );
	errs++;
    }

    if (errs) {
	(void) fprintf(stderr, usage, argv[0]);
	exit(1);
    }

    /* Convert the unit to the devno */
    if (strncmp(device_name, DEV_NAME, 3) == 0 &&
	device_name[3] >= '0' && device_name[3] <= '9') {
	    devno = atoi(&device_name[3]);
    } else {
	fprintf(stderr, "%s is invalid\n", device_name);
	exit(1);
    }
  
    /* find out how many cards are in the system */
    if ((fd = open(dev[0], O_RDWR)) < 0) {
	perror("open failed");
	exit(1);
    }
    if ((rc = ioctl(fd, HIPIOC_GET_NICS, &nics)) < 0) {
	perror(" HIPIOC_GET_NICS ioctl failed");
	exit(1);
    }
    /* Get the operating mode so that we can do the proper processing */
    if (ioctl(fd, HIPIOC_GET_DEVICE_STATE, &is_ph) < 0) {
	perror("couldn't get device state");
	exit(1);
    }
    is_ph &= HIP_PH;
    if (is_ph)
	infinite++;

    if ((rc = close(fd)) < 0) {
	perror("close failed");
	exit(1);
    }
    /* Make sure we are in range */
    if (devno >= nics) {
	fprintf(stderr, "%s is invalid, there are %d NICs\n", 
		device_name, nics);
	exit(1);
    }

#if defined(__sgi)
    if (mapkern) {
	spfd = open(device_name, O_RDWR);
	if (spfd < 0) {
	    perror("couldn't open special hippi device");
	    exit(1);
	}
	buf = mmap((void *)0, len, PROT_READ | PROT_WRITE,
		    MAP_SHARED, spfd, 0);
	if ((void *)buf == (void *)-1L) {
	    perror("couldn't map first buffer");
	    exit(1);
	}
	bufw = buf;
    } else {
#endif /* defined(__sgi) */

	/* The fill pattern repeats at 256 bytes so make sure actual buf
	 * is a multiple of fill patterns */
	len1 = len + 256;
	buf = memalign(MEM_ALIGN, (len<1024)?1024:len1);
	if (buf == NULL) {
	  perror("couldn't allocate buffer");
	  exit(1);
	}
	bufw = buf;

#if defined(__sgi)
	if (Mflag && mpin(buf, len) < 0)
	    perror("couldn't mpin()");
    }
#endif /* defined(__sgi) */
    
    if (random) {
	seed = time((time_t*)0);
	srand48(seed);
    
	/* fill buf with pattern (also faults in)
	 * checksum at end is used by sink to validate data */
	cksm = 0;
	for (k=0; k < (len+3)/4-1; k++) {
	    buf[k] = (u_int)mrand48();
	    cksm += buf[k];
	}
	buf[k] = 0 - cksm;
    } else {
	/* fill buf with pattern (also faults in) */
	for (i=0, curChar = 0xa0, cbuf = (u_char *)buf; i < len1; i++) {
	    *cbuf++ = curChar++;
	}
    }


    (void) printf("blast(%s): %u packets, length %lu, I=0x%08X",
	   device_name, (bigPacket)? m : m*n, 
	   (bigPacket)? (u_long)len*(u_long)n: len, I);
    if (is_ph == 0)
	printf(", ulp=0x%02x", ulp);
    if (double_thread)
	(void) printf(", two processes");
    if (Mflag)
	(void) printf(", mpin'ed buffer");
    (void) printf("\n");
    fflush(stdout);
    
    for (i=0; i<m; i++) {
	
	if (double_thread)
	    child = (fork() == 0);
	else
	    child = 0;

    
	HIP_APP_OPEN(dev[devno], O_WRONLY, fd);
	if (fd < 0) {
	    perror("blast: couldn't open hippi device");
	    exit(1);
	  }
    
	if (is_ph == 0)
	    /* Set ULP */
	    if (ioctl(fd, HIPIOCW_SET_ULP, ulp) < 0) {
	        perror("blast: couldn't set ULP");
	        exit(1);
	    }
    
	/* Set I-field, if not under connection control */
	if (!conns) {
#if defined(__alpha) || defined(__i386)
	    I = htonl(I);
#endif
	    if (ioctl(fd, HIPIOCW_I, I) < 0) {
	        perror("blast: couldn't set I-field");
	        exit(1);
	    }
#if defined(__alpha) || defined(__i386)
	    I = htonl(I);
#endif
	}

	/* START */
	start_time = times(&tm_dummy);

	if (conns) {
#if defined(__alpha) || defined(__i386)
	    I = htonl(I);
#endif
	    if (ioctl(fd, HIPIOCW_CONNECT, I) < 0) {
		perror("couldn't HIPIOCW_CONNECT");
		exit(1);
	    }
#if defined(__alpha) || defined(__i386)
	    I = htonl(I);
#endif
	}

	if (bigPacket || infinite)
	    if (ioctl(fd, HIPIOCW_START_PKT, 
			(infinite)?D2SIZE_UNKNOWN:n*len) < 0) {
		perror("couldn't HIPIOCW_START_PKT");
		exit(1);
	    }
	
	pkt_off = 0;

	for (j= (double_thread ? (child ? n/2 : (n+1)/2) : n); j > 0; j--) {
	    /* If random and check are requested fill each buffer
	     * with random data and a checksum. This is the "real world"
	     * version of the test where the application loads the
	     * buffer before sending it */
	    if (check && random) {
		cksm = 0;
		for (k=0; k < (len+3)/4-1; k++) {
	    	    buf[k] = (u_int)mrand48();
	    	    cksm += buf[k];
		}
		buf[k] = 0 - cksm;
	    }
	    retv = (int)write(fd, (void *)&bufw[(pkt_off & 0xff) >>2],
					(size_t)len);
	    pkt_off += len;
	    if (retv != len) {
		(void) printf(
			      "blast: (%s.%s) %d: write return value: %d\n",
			      device_name,
			      child ? "child" : "parent",
			      i, retv);
		fflush(stdout);
		perror("blast: trouble writing");
		if (retv < 0 && errno == EIO) {
		    rc = ioctl(fd, HIPIOCW_ERR, &retv);
		    (void) printf("HIPPI error no %d %s\n",
				  rc, (retv>0) ? src_err_name[retv] :
				  "huh?");
		    fflush(stdout);
		}
		exit(1);
	    }

	    /* In ph mode and with infinite packets each write is a 
	     * packet (except when bigPacket) is specified. */
	    if (infinite && bigPacket == 0) {
	        if (ioctl(fd, HIPIOCW_END_PKT) < 0) {
		    perror("couldn't HIPIOCW_END_PKT");
		    exit(1);
	        }
		if (j - 1 > 0) 
		    /* If there is more to do start the next packet */
		    if (ioctl(fd, HIPIOCW_START_PKT, 
			(infinite)?D2SIZE_UNKNOWN:n*len) < 0) {
			perror("couldn't HIPIOCW_START_PKT");
			exit(1);
		    }
	    }

	}

	if (infinite && bigPacket)
	    if (ioctl(fd, HIPIOCW_END_PKT) < 0) {
		perror("couldn't HIPIOCW_END_PKT");
		exit(1);
	    }


	if (conns)
	    if (ioctl(fd, HIPIOCW_DISCONN) < 0) {
		perror("couldn't HIPIOCW_DISCONN");
		exit(1);
	    }

	if (child)
	    exit(0);
	else
	    (void) wait(&status);
	
	/* END */
	end_time = times(&tm_dummy);
	
	t = (float)(end_time - start_time)/(float)sysconf(_SC_CLK_TCK);
	mbytes = (float)n * len / (1024*1024);
	(void) printf("blast(%s): %.2f Meg, %.2f s  (%.2f MB/s)\n",
	       device_name, mbytes, t, (t)?mbytes/t:0);
	

	/* Append entry to report file */
	if (*report_file) {
	    FILE *rpt;
	    if ((rpt = fopen(report_file, "a")) == NULL) {
		printf("Can't open report file - errno %d\n", errno);
	    } else {
		fprintf(rpt, "len %9d %10.6f MB/sec %10.0f Usec/pkt\n",
		    len, (t)?mbytes/t:0, t*1000000.0/(float)n);
		fclose(rpt);
	    }
	}

        (void) close(fd);

    }
    
#if defined(__sgi) 
    if (Mflag && munpin(buf, len) < 0) {
	perror("munpin() failed");
	exit(1);
    }
#endif /* defined(__sgi) */
    return(0);
}

#if defined(__hpux) || defined(__osf__) || defined(__NetBSD__) || _AIX
/* memalign function for HP systems, goes to no trouble for freeing,
 * so doing a free following a memalign could likely cause an error */

static void *
memalign(int align, int size)
{
    char *tmp_addr;
    int i;

    tmp_addr = malloc((unsigned)(size + align));
    if (tmp_addr == NULL)
      return NULL;
    i = align - (long)tmp_addr & (align-1);
    return((void *)&tmp_addr[i]);
}
#endif /* __hpux || __osf__ || __NetBSD__ || _AIX */
