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