#pragma ident  "@(#)raw.c 1.46     03/07/03 SMI"

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

/*
    This program tests the Tx & Rx of Raw messages over the ba Driver
    Syntax:
        # raw cmd interface vc [bw | peak avg maxb prio] [-c count]
		[-l length] [-p pattern] [-v]
              cmd: i: idle
              cmd: t: transmit CBR, 1 Mbps granularity
              cmd: v: transmit VBR
              cmd: c: transmit CBR, 64 Kbps granularity
              cmd: r: receive
		bw, count, pattern are for transmit only.
		if length is non-zero on receive, the pattern is checked.

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

    To Transmit Data on VC 0x100 @ 50 Mbps:
        # raw t ba0 0x100 50 -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	<atm/atmioctl.h>

#define	ZDEBUG	printf

typedef void	(*fptrv_t)();

#define	SAMPLETIME	10	/* sec */

#define	AAL5_TRAILER_SIZE	8
#define	AAL5_MAX_CELLS		1365
#define	ATM_CELL_SIZE		48
#define	AAL5_MAX_SIZE		AAL5_MAX_CELLS * ATM_CELL_SIZE
#define	PAYLOAD_SIZE		AAL5_MAX_SIZE - AAL5_TRAILER_SIZE

#define	VCSIZE		4
#define	BUFSIZE		VCSIZE + PAYLOAD_SIZE

char	rxbuf[BUFSIZE];
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;
int		peak, maxburst, prio;	/* for VBR support */

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

int		dump = 0;

int		mask = 0xff;

extern	errno;

usage(str)
char	*str;
{
	printf("usage: \n");
	printf("      %s cmd interface vc [bw | peak avg maxburst prio]\n",str);
	printf("              [-c count] [-l length] [-p pattern ] [-v|d]\n\n");
	printf("      %s cmd interface vc [bw | peak avg maxburst prio]\n",str);
	printf("              [-c count] [-m] [-v|d]\n\n");
	printf("       cmd: i: idle; just steal some bw\n");
	printf("       cmd: t: transmit CBR, bw in 1 Mbps units\n");
	printf("       cmd: c: transmit CBR, bw in 64 Kbps units\n");
	printf("       cmd: v: transmit VBR, bw in 64 Kbps units \n");
	printf("       cmd: r: receive up to 9K buffer size\n");
	printf("       cmd: R: receive up to 64K buffer size\n\n");
	printf("       CBR bw (cmd=t or cmd=c) is specified as [bw]\n");
	printf("       VBR bw (cmd=v) is specified as [peak avg maxburst prio]\n\n");
	printf("       -v verbose packet display\n");
	printf("       -n don't print packet count\n");
	printf("       -d dump packet on error \n");
	printf("       -m use a predetermined pattern that was compiled in\n\n");
	printf("       peak, avg, maxburst, prio are for VBR (cmd=v) only\n");
	printf("       count and pattern are for transmit only\n");
	printf("       if length is non-zero on receive, the pattern is checked\n\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);
}

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 > 0) {
		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 > 0) {
		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		pps;
	float		mbps;

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

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

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

	if (pps > 131072.0)	mask = 0xffff;
	else if (pps > 65536.0)	mask = 0x7fff;
	else if (pps > 32768.0)	mask = 0x3fff;
	else if (pps > 16384.0)	mask = 0x1fff;
	else if (pps > 8192.0)	mask = 0xfff;
	else if (pps > 4096.0)	mask = 0x7ff;
	else if (pps > 2048.0)	mask = 0x3ff;
	else if (pps > 1024.0)	mask = 0x1ff;
	else			mask = 0xff;
}

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

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

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

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

	if (pps > 131072.0)	mask = 0xffff;
	else if (pps > 65536.0)	mask = 0x7fff;
	else if (pps > 32768.0)	mask = 0x3fff;
	else if (pps > 16384.0)	mask = 0x1fff;
	else if (pps > 8192.0)	mask = 0xfff;
	else if (pps > 4096.0)	mask = 0x7ff;
	else if (pps > 2048.0)	mask = 0x3ff;
	else if (pps > 1024.0)	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));
}

int
rxcheck(len, rxlen)
int		len;
int		rxlen;
{
	int		err = 0;

	if (len != (rxlen - VCSIZE)) {
		printf("raw: rxcheck: rxlen error, len=%d rxlen=%d\n",
			len, rxlen - VCSIZE);
		err++;
	}
	if (memcmp(&databuf[VCSIZE], &rxbuf[VCSIZE], len) != 0) {
		printf("rxcheck: rx error, data mismatch\n");
		err++;
	}
	if (err) {
		if (dump)
			xdbuf(rxbuf, rxlen);
		return (-1);
	}
	return (0);
}

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

	if (argc < 4 || argc > 16) {
		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++]);

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

	switch (command) {
	case 'i':
		bw = getdata(argv[argi++]);
		if (atm_allocate_bw(atmfd, bw) < 0) {
			printf("atm_allocate_bw failed, errno=%d\n", errno);
			exit(-1);
		}
		break;

	case 'T':
		huge = 1;
	case 't':
		bw = getdata(argv[argi++]);
		if (atm_allocate_bw(atmfd, bw) < 0) {
			printf("atm_allocate_bw failed, errno=%d\n", errno);
			exit(-1);
		}
		break;

	case 'c':
		bw = getdata(argv[argi++]);
		if (atm_allocate_cbr_bw(atmfd, bw) < 0) {
			printf("atm_allocate_cbr_bw failed, errno=%d\n", errno);
			exit(-1);
		}
		break;

	case 'v':
		peak = getdata(argv[argi++]);
		bw = getdata(argv[argi++]);
		maxburst = getdata(argv[argi++]);
		prio = getdata(argv[argi++]);
		if (atm_allocate_vbr_bw(atmfd, peak, bw, maxburst, prio) < 0) {
			printf("atm_allocate_bw failed, errno=%d\n", errno);
			exit(-1);
		}
		break;

	case 'r':
		huge = 0;
		break;

	case 'R':
		huge = 1;
		break;

	default:
		usage(argv[0]);
	}

	while (argi < argc && argv[argi][0] == '-') {
		switch (argv[argi][1]) {
		case 'c':
			++argi;
			if (strncmp(argv[argi], "for", strlen("for")) == 0)
				count = -1;
			else
				count = getdata(argv[argi]);
			break;
		case 'l':
			len = getdata(argv[++argi]);
			if (len > PAYLOAD_SIZE) {
				printf("Max length is %d\n", PAYLOAD_SIZE);
				exit(-1);
			}
			break;
		case 'n':
			verbose = -1;
			break;
		case 'm':
			memcpy(&databuf[VCSIZE], fox, sizeof (fox));
			len = 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 'j':
			pattern = -2;
			break;
		case 'v':
			verbose = 1;
			break;
		case 'd':
			dump = 1;
			break;
		default:
			usage(argv[0]);
			exit(-1);
		}
		argi++;
	}

	if (pattern == -2) {
		for (i = VCSIZE; i < sizeof (databuf); i++) {
			databuf[i] = ((i % 32) < 16) ? 0xaa : 0x55;
		}
	} else if (pattern == -1) {
		for (i = VCSIZE; i < sizeof (databuf); i++) {
			databuf[i] = i;
		}
	} else if (pattern != 0) {
		for (i = VCSIZE; i < sizeof (databuf); i++) {
			databuf[i] = pattern;
		}
	}

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

	switch (command) {
	case 'i':
		for (;;);
		break;

	case 'c':
	case 'v':
	case 'T':
	case 't':
		*(int *) databuf = vc;

		datalen = length = len;
		start = gethrtime();
		t1 = gethrtime();
		for (i = 0; i < count || count == -1; i++) {
			transmit(atmfd, 0, -1, databuf, VCSIZE + datalen);
			ctx++;
			if (verbose == 0) {
				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");
		break;

	case 'r':
	case 'R':
		atm_setraw(atmfd);

		t1 = gethrtime();
		for (;;) {
			receive(atmfd, ctlbuf, &ctllen,
					rxbuf, &datalen, BUFSIZE);

			if (len != 0) {
				if (rxcheck(len, datalen) == -1)
					continue;
			}

			length = datalen;
			crx++;
			if (verbose == 0) {
				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;
	}
}
