Home | History | Annotate | Download | only in usr.sbin
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License (the "License").
      6  *  You may not use this file except in compliance with the License.
      7  *
      8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9  * or http://www.opensolaris.org/os/licensing.
     10  * See the License for the specific language governing permissions
     11  * and limitations under the License.
     12  *
     13  * When distributing Covered Code, include this CDDL HEADER in each
     14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15  * If applicable, add the following below this CDDL HEADER, with the
     16  * fields enclosed by brackets "[]" replaced with your own identifying
     17  * information: Portions Copyright [yyyy] [name of copyright owner]
     18  *
     19  * CDDL HEADER END
     20  */
     21 /*
     22  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
     23  * Use is subject to license terms.
     24  */
     25 
     26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
     27 
     28 /*
     29  * Synchronous loop-back test program
     30  * For installation verification of synchronous lines and facilities
     31  */
     32 
     33 #include <sys/types.h>
     34 #include <ctype.h>
     35 #include <sys/ioctl.h>
     36 #include <fcntl.h>
     37 #include <sys/time.h>
     38 #include <sys/file.h>
     39 #include <stdio.h>
     40 #include <stdlib.h>
     41 #include <unistd.h>
     42 #include <string.h>
     43 #include <errno.h>
     44 #include <sys/stream.h>
     45 #include <sys/stropts.h>
     46 #include <sys/poll.h>
     47 #include <sys/ser_sync.h>
     48 #include <libdlpi.h>
     49 
     50 static void Usage(void);
     51 static void quiet_period(void);
     52 static void first_packet();
     53 static void many_packets();
     54 static void printhex(char *cp, int len);
     55 
     56 static unsigned int speed = 9600;
     57 static int reccount = 100;
     58 static int reclen = 100;
     59 static char loopstr[MAX_INPUT];
     60 static int looptype = 0;
     61 static int loopchange = 0;
     62 static int clockchange = 0;
     63 static int cfd, dfd;		/* control and data descriptors */
     64 static int data = -1;
     65 static int verbose = 0;
     66 
     67 static char *yesno[] = {
     68 	"no",
     69 	"yes",
     70 	"silent",
     71 	0,
     72 };
     73 
     74 static char *txnames[] = {
     75 	"txc",
     76 	"rxc",
     77 	"baud",
     78 	"pll",
     79 	"sysclk",
     80 	"-txc",
     81 	0,
     82 };
     83 
     84 static char *rxnames[] = {
     85 	"rxc",
     86 	"txc",
     87 	"baud",
     88 	"pll",
     89 	"sysclk",
     90 	"-rxc",
     91 	0,
     92 };
     93 
     94 #define	MAXPACKET	4096
     95 
     96 int
     97 main(int argc, char **argv)
     98 {
     99 	char *portname;
    100 	char dnambuf[MAXPATHLEN], *cp;
    101 	char device[DLPI_LINKNAME_MAX];
    102 	struct scc_mode sm;
    103 	struct strioctl sioc;
    104 	uint_t ppa;
    105 	char *devstr = "/dev/";
    106 	int devstrlen;
    107 	int retval;
    108 	dlpi_handle_t dh;
    109 
    110 	argc--;
    111 	argv++;
    112 	while (argc > 0 && argv[0][0] == '-')
    113 		switch (argv[0][1]) {
    114 		case 'c':	/* rec count */
    115 			if (argc < 2)
    116 				Usage();
    117 			reccount = atoi(argv[1]);
    118 			argc -= 2;
    119 			argv += 2;
    120 			break;
    121 		case 'd':
    122 			if (sscanf(argv[1], "%x", (uint_t *)&data) != 1)
    123 				Usage();
    124 			argc -= 2;
    125 			argv += 2;
    126 			break;
    127 		case 'l':	/* rec length */
    128 			if (argc < 2)
    129 				Usage();
    130 			reclen = atoi(argv[1]);
    131 			argc -= 2;
    132 			argv += 2;
    133 			break;
    134 		case 's':	/* line speed */
    135 			if (argc < 2)
    136 				Usage();
    137 			speed = atoi(argv[1]);
    138 			argc -= 2;
    139 			argv += 2;
    140 			break;
    141 		case 't':	/* test type */
    142 			if (argc < 2)
    143 				Usage();
    144 			looptype = atoi(argv[1]);
    145 			argc -= 2;
    146 			argv += 2;
    147 			break;
    148 		case 'v':
    149 			verbose = 1;
    150 			argc--;
    151 			argv++;
    152 			break;
    153 		}
    154 	if (argc != 1)
    155 		Usage();
    156 	portname = argv[0];
    157 
    158 	devstrlen = strlen(devstr);
    159 	if (strncmp(devstr, portname, devstrlen) != 0) {
    160 		if (snprintf(dnambuf, sizeof (dnambuf), "%s%s", devstr,
    161 		    portname) >= sizeof (dnambuf)) {
    162 			(void) fprintf(stderr,
    163 			    "syncloop: invalid device name (too long) %s\n",
    164 			    portname);
    165 			exit(1);
    166 		}
    167 	}
    168 
    169 	dfd = open(dnambuf, O_RDWR);
    170 	if (dfd < 0) {
    171 		(void) fprintf(stderr, "syncloop: cannot open %s\n", dnambuf);
    172 		perror(dnambuf);
    173 		exit(1);
    174 	}
    175 
    176 	cp = portname;
    177 	while (*cp)			/* find the end of the name */
    178 		cp++;
    179 	cp--;
    180 	if (!isdigit(*cp)) {
    181 		(void) fprintf(stderr,
    182 		    "syncloop: %s missing minor device number\n", portname);
    183 		exit(1);
    184 	}
    185 
    186 	if (strlen(portname) >= DLPI_LINKNAME_MAX) {
    187 		(void) fprintf(stderr,
    188 		    "syncloop: invalid device name (too long) %s\n",
    189 		    portname);
    190 		exit(1);
    191 	}
    192 
    193 	if ((retval = dlpi_open(portname, &dh, DLPI_SERIAL)) != DLPI_SUCCESS) {
    194 		(void) fprintf(stderr, "syncloop: dlpi_open %s: %s\n", portname,
    195 		    dlpi_strerror(retval));
    196 		exit(1);
    197 	}
    198 
    199 	(void) dlpi_parselink(portname, device, &ppa);
    200 
    201 	if (reclen < 0 || reclen > MAXPACKET) {
    202 		(void) printf("invalid packet length: %d\n", reclen);
    203 		exit(1);
    204 	}
    205 	(void) printf("[ Data device: %s | Control device: %s, ppa=%u ]\n",
    206 		dnambuf, device, ppa);
    207 
    208 	cfd = dlpi_fd(dh);
    209 
    210 	sioc.ic_cmd = S_IOCGETMODE;
    211 	sioc.ic_timout = -1;
    212 	sioc.ic_len = sizeof (struct scc_mode);
    213 	sioc.ic_dp = (char *)&sm;
    214 	if (ioctl(cfd, I_STR, &sioc) < 0) {
    215 		perror("S_IOCGETMODE");
    216 		(void) fprintf(stderr, "syncloop: can't get sync mode info "
    217 		    "for %s\n", portname);
    218 		exit(1);
    219 	}
    220 	while (looptype < 1 || looptype > 4) {
    221 		(void) printf("Enter test type:\n");
    222 		(void) printf("1: Internal Test\n");
    223 		(void) printf(
    224 "            (internal data loop, internal clocking)\n");
    225 		(void) printf("2: Test using loopback plugs\n");
    226 		(void) printf(
    227 "            (external data loop, internal clocking)\n");
    228 		(void) printf("3: Test using local or remote modem loopback\n");
    229 		(void) printf(
    230 "            (external data loop, external clocking)\n");
    231 		(void) printf("4: Other, previously set, special mode\n");
    232 		(void) printf("> "); (void) fflush(stdout);
    233 		(void) fgets(loopstr, sizeof (loopstr), stdin);
    234 		(void) sscanf(loopstr, "%d", &looptype);
    235 	}
    236 	switch (looptype) {
    237 	case 1:
    238 		if ((sm.sm_txclock != TXC_IS_BAUD) ||
    239 		    (sm.sm_rxclock != RXC_IS_BAUD))
    240 			clockchange++;
    241 		sm.sm_txclock = TXC_IS_BAUD;
    242 		sm.sm_rxclock = RXC_IS_BAUD;
    243 		if ((sm.sm_config & CONN_LPBK) == 0)
    244 			loopchange++;
    245 		sm.sm_config |= CONN_LPBK;
    246 		break;
    247 	case 2:
    248 		if ((sm.sm_txclock != TXC_IS_BAUD) ||
    249 		    (sm.sm_rxclock != RXC_IS_RXC))
    250 			clockchange++;
    251 		sm.sm_txclock = TXC_IS_BAUD;
    252 		sm.sm_rxclock = RXC_IS_RXC;
    253 		if ((sm.sm_config & CONN_LPBK) != 0)
    254 			loopchange++;
    255 		sm.sm_config &= ~CONN_LPBK;
    256 		break;
    257 	case 3:
    258 		if ((sm.sm_txclock != TXC_IS_TXC) ||
    259 		    (sm.sm_rxclock != RXC_IS_RXC))
    260 			clockchange++;
    261 		sm.sm_txclock = TXC_IS_TXC;
    262 		sm.sm_rxclock = RXC_IS_RXC;
    263 		if ((sm.sm_config & CONN_LPBK) != 0)
    264 			loopchange++;
    265 		sm.sm_config &= ~CONN_LPBK;
    266 		break;
    267 	case 4:
    268 		goto no_params;
    269 	}
    270 
    271 	sm.sm_baudrate = speed;
    272 
    273 	sioc.ic_cmd = S_IOCSETMODE;
    274 	sioc.ic_timout = -1;
    275 	sioc.ic_len = sizeof (struct scc_mode);
    276 	sioc.ic_dp = (char *)&sm;
    277 	if (ioctl(cfd, I_STR, &sioc) < 0) {
    278 		perror("S_IOCSETMODE");
    279 		(void) fprintf(stderr,
    280 		    "syncloop: can't set sync mode info for %s\n", portname);
    281 		exit(1);
    282 	}
    283 
    284 no_params:
    285 	/* report state */
    286 	sioc.ic_cmd = S_IOCGETMODE;
    287 	sioc.ic_timout = -1;
    288 	sioc.ic_len = sizeof (struct scc_mode);
    289 	sioc.ic_dp = (char *)&sm;
    290 	if (ioctl(cfd, I_STR, &sioc) < 0) {
    291 		perror("S_IOCGETMODE");
    292 		(void) fprintf(stderr, "syncloop: can't get sync mode info "
    293 			"for %s\n", portname);
    294 		exit(1);
    295 	}
    296 	(void) printf("speed=%d, loopback=%s, nrzi=%s, txc=%s, rxc=%s\n",
    297 		sm.sm_baudrate,
    298 		yesno[((int)(sm.sm_config & CONN_LPBK) > 0)],
    299 		yesno[((int)(sm.sm_config & CONN_NRZI) > 0)],
    300 		txnames[sm.sm_txclock],
    301 		rxnames[sm.sm_rxclock]);
    302 
    303 	quiet_period();
    304 	first_packet();
    305 	many_packets();
    306 	return (0);
    307 }
    308 
    309 static void
    310 Usage()
    311 {
    312 	(void) printf("Usage: syncloop [ options ] portname\n");
    313 	(void) printf("Options: -c packet_count\n");
    314 	(void) printf("         -l packet_length\n");
    315 	(void) printf("         -s line_speed\n");
    316 	(void) printf("         -t test_type\n");
    317 	(void) printf("         -d hex_data_byte\n");
    318 	exit(1);
    319 }
    320 
    321 static int zero_time = 0;
    322 static int short_time = 1000;
    323 static int long_time = 4000;
    324 static char bigbuf[4096];
    325 static char packet[MAXPACKET];
    326 static struct pollfd pfd;
    327 
    328 static void
    329 quiet_period()
    330 {
    331 	(void) printf("[ checking for quiet line ]\n");
    332 	pfd.fd = dfd;
    333 	pfd.events = POLLIN;
    334 	pfd.revents = 0;
    335 	while (poll(&pfd, 1, short_time) == 1) {
    336 		(void) read(dfd, bigbuf, sizeof (bigbuf));
    337 	}
    338 	if (poll(&pfd, 1, long_time) == 1) {
    339 		(void) printf("packet received but none sent!\n");
    340 		(void) printf("quiesce other end before starting syncloop\n");
    341 		exit(1);
    342 	}
    343 }
    344 
    345 static void
    346 first_packet()
    347 {
    348 	int i, len;
    349 	int pollret;
    350 	struct strioctl sioc;
    351 	struct sl_stats start_stats, end_stats;
    352 
    353 	for (i = 0; i < reclen; i++)
    354 		packet[i] = (data == -1) ? rand() : data;
    355 	(void) printf("[ Trying first packet ]\n");
    356 	sioc.ic_cmd = S_IOCGETSTATS;
    357 	sioc.ic_timout = -1;
    358 	sioc.ic_len = sizeof (struct sl_stats);
    359 	sioc.ic_dp = (char *)&start_stats;
    360 	if (ioctl(cfd, I_STR, &sioc) < 0) {
    361 		perror("S_IOCGETSTATS");
    362 		exit(1);
    363 	}
    364 
    365 	for (i = 0; i < 5; i++) {
    366 		if (write(dfd, packet, reclen) != reclen) {
    367 			(void) fprintf(stderr,
    368 				"packet write failed, errno %d\n",
    369 				errno);
    370 			exit(1);
    371 		}
    372 		pfd.fd = dfd;
    373 		pfd.events = POLLIN;
    374 		pollret = poll(&pfd, 1, long_time);
    375 		if (pollret < 0) perror("poll");
    376 		if (pollret == 0)
    377 			(void) printf("poll: nothing to read.\n");
    378 		if (pollret == 1) {
    379 			len = read(dfd, bigbuf, reclen);
    380 			if (len == reclen && memcmp(packet, bigbuf, len) == 0)
    381 				return;	/* success */
    382 			else {
    383 				(void) printf("len %d should be %d\n",
    384 					len, reclen);
    385 				if (verbose) {
    386 					(void) printf("           ");
    387 					printhex(bigbuf, len);
    388 					(void) printf("\nshould be ");
    389 					printhex(packet, reclen);
    390 					(void) printf("\n");
    391 				}
    392 			}
    393 		}
    394 	}
    395 	(void) printf("Loopback has TOTALLY FAILED - ");
    396 	(void) printf("no packets returned after 5 attempts\n");
    397 	sioc.ic_cmd = S_IOCGETSTATS;
    398 	sioc.ic_timout = -1;
    399 	sioc.ic_len = sizeof (struct sl_stats);
    400 	sioc.ic_dp = (char *)&end_stats;
    401 	if (ioctl(cfd, I_STR, &sioc) < 0) {
    402 		perror("S_IOCGETSTATS");
    403 		exit(1);
    404 	}
    405 	if (start_stats.opack == end_stats.opack)
    406 		(void) printf(
    407 			"No packets transmitted - no transmit clock present\n");
    408 	exit(1);
    409 }
    410 
    411 static void
    412 many_packets()
    413 {
    414 	struct strioctl sioc;
    415 	struct sl_stats start_stats, end_stats;
    416 	struct timeval start_time, end_time;
    417 	int baddata = 0;
    418 	float secs, speed;
    419 	int i, len;
    420 	int incount = 0;
    421 	long prev_sec = -1;
    422 	int pollret;
    423 
    424 	(void) printf("[ Trying many packets ]\n");
    425 	sioc.ic_cmd = S_IOCGETSTATS;
    426 	sioc.ic_timout = -1;
    427 	sioc.ic_len = sizeof (struct sl_stats);
    428 	sioc.ic_dp = (char *)&start_stats;
    429 	if (ioctl(cfd, I_STR, &sioc) < 0) {
    430 		perror("S_IOCGETSTATS");
    431 		exit(1);
    432 	}
    433 	(void) gettimeofday(&start_time, 0);
    434 	end_time = start_time;
    435 
    436 	i = 0;
    437 	while (i < reccount) {
    438 		if (end_time.tv_sec != prev_sec) {
    439 			prev_sec = end_time.tv_sec;
    440 			(void) printf("\r %d ", incount);
    441 			(void) fflush(stdout);
    442 		}
    443 		pfd.fd = dfd;
    444 		pfd.events = POLLIN;
    445 		while (pollret = poll(&pfd, 1, zero_time)) {
    446 			if (pollret < 0)
    447 				perror("poll");
    448 			else {
    449 				(void) lseek(dfd, (long)0, 0);
    450 				len = read(dfd, bigbuf, reclen);
    451 				if (len != reclen ||
    452 				    memcmp(packet, bigbuf, len) != 0) {
    453 					(void) printf("len %d should be %d\n",
    454 						len, reclen);
    455 					if (verbose) {
    456 						(void) printf("           ");
    457 						printhex(bigbuf, len);
    458 						(void) printf("\nshould be ");
    459 						printhex(packet, reclen);
    460 						(void) printf("\n");
    461 					}
    462 					baddata++;
    463 				}
    464 				incount++;
    465 				(void) gettimeofday(&end_time, 0);
    466 			}
    467 		}
    468 		pfd.fd = dfd;
    469 		pfd.events = POLLIN|POLLOUT;
    470 		pollret = poll(&pfd, 1, long_time);
    471 		if (pollret < 0)
    472 			perror("poll");
    473 		if (pollret == 0)
    474 			(void) printf("poll: nothing to read or write.\n");
    475 		if (pollret == 1) {
    476 			if (pfd.revents & POLLOUT) {
    477 				(void) write(dfd, packet, reclen);
    478 				i++;
    479 			} else if (!(pfd.revents & POLLIN)) {
    480 				(void) printf("OUTPUT HAS LOCKED UP!!!\n");
    481 				break;
    482 			}
    483 		}
    484 	}
    485 	pfd.fd = dfd;
    486 	pfd.events = POLLIN;
    487 	while ((incount < reccount) && (poll(&pfd, 1, long_time) == 1)) {
    488 		if (end_time.tv_sec != prev_sec) {
    489 			prev_sec = end_time.tv_sec;
    490 			(void) printf("\r %d ", incount);
    491 			(void) fflush(stdout);
    492 		}
    493 		len = read(dfd, bigbuf, reclen);
    494 		if (len != reclen || memcmp(packet, bigbuf, len) != 0) {
    495 			(void) printf("len %d should be %d\n", len, reclen);
    496 			if (verbose) {
    497 				(void) printf("           ");
    498 				printhex(bigbuf, len);
    499 				(void) printf("\nshould be ");
    500 				printhex(packet, reclen);
    501 				(void) printf("\n");
    502 			}
    503 			baddata++;
    504 		}
    505 		incount++;
    506 		(void) gettimeofday(&end_time, 0);
    507 	}
    508 	(void) printf("\r %d \n", incount);
    509 	if (baddata)
    510 		(void) printf("%d packets with wrong data received!\n",
    511 			baddata);
    512 	sioc.ic_cmd = S_IOCGETSTATS;
    513 	sioc.ic_timout = -1;
    514 	sioc.ic_len = sizeof (struct sl_stats);
    515 	sioc.ic_dp = (char *)&end_stats;
    516 	if (ioctl(cfd, I_STR, &sioc) < 0) {
    517 		perror("S_IOCGETSTATS");
    518 		exit(1);
    519 	}
    520 	end_stats.ipack -= start_stats.ipack;
    521 	end_stats.opack -= start_stats.opack;
    522 	end_stats.abort -= start_stats.abort;
    523 	end_stats.crc -= start_stats.crc;
    524 	end_stats.overrun -= start_stats.overrun;
    525 	end_stats.underrun -= start_stats.underrun;
    526 	end_stats.ierror -= start_stats.ierror;
    527 	end_stats.oerror -= start_stats.oerror;
    528 	if (reccount > end_stats.opack)
    529 		(void) printf("%d packets lost in outbound queueing\n",
    530 			reccount - end_stats.opack);
    531 	if (incount < end_stats.ipack && incount < reccount)
    532 		(void) printf("%d packets lost in inbound queueing\n",
    533 			end_stats.ipack - incount);
    534 	(void) printf("%d packets sent, %d received\n", reccount, incount);
    535 	(void) printf("CRC errors    Aborts   Overruns  Underruns         ");
    536 	(void) printf("   In <-Drops-> Out\n%9d  %9d  %9d  %9d  %12d  %12d\n",
    537 		end_stats.crc, end_stats.abort,
    538 		end_stats.overrun, end_stats.underrun,
    539 		end_stats.ierror, end_stats.oerror);
    540 	secs = (float)(end_time.tv_usec - start_time.tv_usec) / 1000000.0;
    541 	secs += (float)(end_time.tv_sec - start_time.tv_sec);
    542 	if (secs) {
    543 		speed = 8 * incount * (4 + reclen) / secs;
    544 		(void) printf("estimated line speed = %d bps\n", (int)speed);
    545 	}
    546 }
    547 
    548 static void
    549 printhex(char *cp, int len)
    550 {
    551 	char c, *hex = "0123456789ABCDEF";
    552 	int i;
    553 
    554 	for (i = 0; i < len; i++) {
    555 		c = *cp++;
    556 		(void) putchar(hex[(c >> 4) & 0xF]);
    557 		(void) putchar(hex[c & 0xF]);
    558 	}
    559 }
    560