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