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