#pragma ident  "@(#)dltst.c 1.25     03/07/03 SMI"

/*
 *	Copyright (c) 1994, Sun Microsystems, Inc.
 *	All Rights Reserved.
 */

/*
    This program tests the Tx & Rx of DLPI messages over the ATM Driver
    Syntax:
        # dltst cmd interface vc sap [-c count] [-l length] [-p pattern] 
		[-b bandwidth] [-v]
               cmd: t: transmit
               cmd: r: receive
		       count, length, pattern are for transmit only.

    To Receive Data on VC 0x100:
        # dltst r ba0 0x100 0x200 -v

    To Transmit Data on VC 0x100:
        # dltst t ba0 0x100 0x200 -c 10 -l 32 -p 0x55 -v
 */

#include	<stdio.h>
#include	<sys/types.h>
#include	<sys/time.h>
#include	<sys/stropts.h>
#include	<sys/errno.h>
#include	<fcntl.h>

#include	<sys/dlpi.h>

#include	<atm/atmioctl.h>

typedef void	(*fptrv_t)();

#define	SAMPLETIME	10	/* sec */

#define	BUFSIZE		0x10000		/* 64 K */

char	databuf[BUFSIZE];
char	ctlbuf[0x100];

char	fox[] = "the quick brown fox jumps over the lazy dog\n\0";

char	interface[10];
int		atmfd;
int		ppa;
int		vc;
int		bw = 0;
int		sap;

int		verbose = 0;
u_int	ctx = 0;
u_int	crx = 0;
int		length;

int		mask = 0xff;

extern	errno;

usage(str)
char	*str;
{
	printf("usage:\n");
	printf("      %s cmd interface vc sap [-c count] [-l length]\n ", str);
	printf("              [-p pattern] [-b bandwidth] [-v]\n\n");
	printf("      %s cmd interface vc sap [-m] [-b bandwidth] [-v]\n\n", str);
	printf("       cmd: t: transmit\n");
	printf("       cmd: r: receive\n");
	printf("       count, length, pattern are for transmit only\n");
}

power(x, y)
u_long		x, y;
{
	int		result = 1;

	for (; y; y--) {
		result *= x;
	}
	return (result);
}

u_long
atoh(str)
char		*str;
{
	u_long		result = 0;
	int			i = 0;
	char		*p;

	for (p = str+strlen(str)-1; p >= str; p--, i++) {
		if (*p >= '0' && *p <= '9')
			result = result + ((*p - '0') * power(16, i));
		if (*p >= 'a' && *p <= 'f')
			result = result + ((*p - 'a' + 10) * power(16, i));
		if (*p >= 'A' && *p <= 'F')
			result = result + ((*p - 'A' + 10) * power(16, i));
	}
	return (result);
}

u_long
getdata(str)
char	*str;
{
	u_long	result;

	if (strncmp(str, "0x", 2) == 0)
		result = atoh(&str[2]);
	else
		result = atoi(str);
	return (result);
}

sig_intrpt()
{
	fprintf(stderr, "\r%d\n", crx);
	exit(0);
}

int
transmit(fd, ctlbufp, ctllen, databufp, datalen)
int		fd;
char	*ctlbufp;
int		ctllen;
char	*databufp;
int		datalen;
{
	struct strbuf	ctl;
	struct strbuf	data;

	ctl.buf = (char *) ctlbufp;
	ctl.len = ctllen;
	data.buf = (char *) databufp;
	data.len = datalen;
	if (putmsg(fd, &ctl, &data, 0) < 0) {
		printf("putmsg failed, errno=%d\n", errno);
		perror("");
		return (-1);
	}

	if (verbose) {
		if (ctllen > 0) {
			xdbuf(ctlbufp, ctllen);
		}
		if (datalen > 0) {
			xdbuf(databuf, datalen);
		}
		printf("\n");
	}
	return (0);
}

int
receive(fd, ctlbufp, ctllenp, databufp, datalenp, maxlen)
int			fd;
u_char		*ctlbufp;
int			*ctllenp;
u_char		*databufp;
int			*datalenp;
int			maxlen;
{
	struct strbuf	ctl;
	struct strbuf	data;
	int				flags;

	ctl.buf = (char *) ctlbufp;
	ctl.maxlen = maxlen;
	ctl.len = 0;
	data.buf = (char *) databufp;
	data.maxlen = maxlen;
	data.len = 0;
	flags = 0;
	*ctllenp = 0;
	*datalenp = 0;
	if (getmsg(fd, &ctl, &data, &flags) < 0) {
		/*
		printf("getmsg failed, errno=%d\n", errno);
		perror("");
		*/
		return (-1);
	}
	*ctllenp = ctl.len;
	*datalenp = data.len;

	if (verbose) {
		if (*ctllenp > 0) {
			xdbuf(ctlbufp, *ctllenp);
		}
		if (*datalenp > 0) {
			xdbuf(databufp, *datalenp);
		}
		printf("\n");
	}
	return (0);
}

tx_perf()
{
	static u_int	current = 0;
	u_int		delta;
	float		mbps;

	if (ctx >= current)
		delta = ctx - current;
	else
		delta = 0xffffffff - current + ctx;
	current = ctx;

	mbps = (float) delta*length*8/SAMPLETIME/(1024*1024);

	printf("%.2f Pkts/Sec %.2f Mbits/Sec\n",
			(float) delta/SAMPLETIME, mbps);

	if (mbps > 400)		mask = 0xfff;
	else if (mbps > 300)	mask = 0x7ff;
	else if (mbps > 200)	mask = 0x3ff;
	else if (mbps > 100)	mask = 0x1ff;
	else			mask = 0xff;
}

rx_perf()
{
	static u_int	current = 0;
	u_int		delta;
	float		mbps;

	if (crx >= current)
		delta = crx - current;
	else
		delta = 0xffffffff - current + crx;
	current = crx;

	mbps = (float) delta*length*8/SAMPLETIME/(1000*1000);

	printf("%.2f Pkts/Sec %.2f Mbits/Sec\n",
			(float) delta/SAMPLETIME, mbps);

	if (mbps > 400)		mask = 0xfff;
	else if (mbps > 300)	mask = 0x7ff;
	else if (mbps > 200)	mask = 0x3ff;
	else if (mbps > 100)	mask = 0x1ff;
	else			mask = 0xff;
}

pr_time(pt1, pt2, count, length)
hrtime_t	pt1, pt2;
int			count;
{
	float sec;
	hrtime_t		nsec;

	nsec = pt2 - pt1;
	sec = (float) nsec / 1000000000;
	nsec %= 1000000000;
	printf("\n");
	printf("%d packets of size %d in: %.2f sec\n", 
				count, length, sec);
	printf("%.2f Pkts/Sec %.2f Mbits/Sec\n",
			(float) count / sec,
			(float) count * length * 8 / sec / (1024 * 1024));
}

main(argc, argv)
int		argc;
char	**argv;
{
	int		count = 1;
	int		pattern = -1;
	int		ctllen;
	int		datalen;
	int		argi;
	int		i;
	char	command;
	hrtime_t	t1, t2;
	hrtime_t	start, end;
	hrtime_t	nsec;
	long	sec;
	int		*vcp;
	dl_unitdata_req_t	*dludp;

	if (argc < 4 || argc > 15) {
		usage(argv[0]);
		exit(-1);
	}
	argi = 1;
	command = argv[argi++][0];
	memset(interface, 0, sizeof (interface));
	strcpy(interface, argv[argi++]);
	ppa = atm_get_ppa(interface);
	vc = getdata(argv[argi++]);
	sap = getdata(argv[argi++]);

	if ((atmfd = atm_open(interface)) < 0) {
		printf("open failed, errno=%d\n", errno);
		perror("open");
		exit(-1);
	}
	atm_attach(atmfd, ppa);

	atm_bind(atmfd, sap);

	if (atm_add_vpci(atmfd, vc, LLC_ENCAP, BIG_BUF_TYPE) < 0) {
		printf("atm_add_vpci failed, errno=%d\n", errno);
		exit(-1);
	}

	while (argi < argc && argv[argi][0] == '-') {
		switch (argv[argi][1]) {
		case 'b':
			argi++;
			bw = getdata(argv[argi]);
			break;
		case 'c':
			++argi;
			if (strncmp(argv[argi], "for", strlen("for")) == 0)
				count = -1;
			else
				count = getdata(argv[argi]);
			break;
		case 'l':
			length = getdata(argv[++argi]);
			if (length > sizeof (databuf)) {
				printf("Max length is 8192\n");
				exit(-1);
			}
			break;
		case 'm':
			memcpy(&databuf[4], fox, sizeof (fox));
			length = sizeof (int) + sizeof (fox);
			pattern = 0;
			break;
		case 'p':
			++argi;
			if (strncmp(argv[argi], "inc", strlen("inc")) == 0) {
				pattern = -1;
			} else {
				pattern = getdata(argv[argi]);
			}
			break;
		case 'v':
			verbose = 1;
			break;
		default:
			usage(argv[0]);
			exit(-1);
		}
		argi++;
	}


	if (bw != 0) {
		if (atm_allocate_bw(atmfd, bw) < 0) {
			printf("atm_allocate_bw failed, errno=%d\n", errno);
			exit(-1);
		}
	}
	/* Else we sponge off IP's BWG */

	switch (command) {
	case 't':
		if (pattern == -1) {
			for (i = 0; i < sizeof (databuf); i++) {
				databuf[i] = i;
			}
		} else if (pattern != 0) {
			for (i = 0; i < sizeof (databuf); i++) {
				databuf[i] = pattern;
			}
		}
		datalen = length;

		memset(ctlbuf, 0, sizeof (dl_unitdata_req_t) + 4);
		dludp = (dl_unitdata_req_t *) ctlbuf;
		dludp->dl_primitive = DL_UNITDATA_REQ;
		dludp->dl_dest_addr_length = 4;
		dludp->dl_dest_addr_offset = sizeof (dl_unitdata_req_t);
		vcp = (int *) &ctlbuf[sizeof (dl_unitdata_req_t)];
		*vcp = vc;
		ctllen = sizeof (dl_unitdata_req_t) + 4;

		start = gethrtime();
		t1 = gethrtime();
		for (i = 0; i < count || count == -1; i++) {
			transmit(atmfd, ctlbuf, ctllen, databuf, datalen);
			ctx++;
			if (!verbose) {
				if ((ctx & mask) == 0) {
					fprintf(stderr, "\r%d ", ctx);
				}
			}
			t2 = gethrtime();
			nsec = t2 - t1;
			sec = nsec / 1000000000;
			nsec %= 1000000000;
			if (sec >= SAMPLETIME) {
				tx_perf();
				t1 = gethrtime();
			}
		}
		end = gethrtime();
		pr_time(start, end, count, datalen);
		printf("\n");
		if (atm_delete_vpci(atmfd, vc) < 0) {
			printf("atm_del_vpci failed, errno=%d\n", errno);
			exit(-1);
		}
		break;
	case 'r':
		t1 = gethrtime();
		for (;;) {
			memset(ctlbuf, 0, sizeof (dl_unitdata_ind_t) + 4 + 4);
			receive(atmfd, ctlbuf, &ctllen,
					databuf, &datalen, BUFSIZE);
			length = datalen;
			crx++;
			if (!verbose) {
				if ((crx & mask) == 0) {
					fprintf(stderr, "\r%d ", crx);
				}
			}
			t2 = gethrtime();
			nsec = t2 - t1;
			sec = nsec / 1000000000;
			nsec %= 1000000000;
			if (sec >= SAMPLETIME) {
				rx_perf();
				t1 = gethrtime();
			}
		}
		break;
	default:
		break;
	}
}
