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
50static void Usage(void);
51static void quiet_period(void);
52static void first_packet();
53static void many_packets();
54static void printhex(char *cp, int len);
55
56static unsigned int speed = 9600;
57static int reccount = 100;
58static int reclen = 100;
59static char loopstr[MAX_INPUT];
60static int looptype = 0;
61static int loopchange = 0;
62static int clockchange = 0;
63static int cfd, dfd;		/* control and data descriptors */
64static int data = -1;
65static int verbose = 0;
66
67static char *yesno[] = {
68	"no",
69	"yes",
70	"silent",
71	0,
72};
73
74static char *txnames[] = {
75	"txc",
76	"rxc",
77	"baud",
78	"pll",
79	"sysclk",
80	"-txc",
81	0,
82};
83
84static char *rxnames[] = {
85	"rxc",
86	"txc",
87	"baud",
88	"pll",
89	"sysclk",
90	"-rxc",
91	0,
92};
93
94#define	MAXPACKET	4096
95
96int
97main(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
284no_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
309static void
310Usage()
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
321static int zero_time = 0;
322static int short_time = 1000;
323static int long_time = 4000;
324static char bigbuf[4096];
325static char packet[MAXPACKET];
326static struct pollfd pfd;
327
328static void
329quiet_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
345static void
346first_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
411static void
412many_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
548static void
549printhex(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