Revision b03c720211ea5ec027ad85f1e147e3d8122429ba authored by Pyun YongHyeon on 14 January 2010, 21:54:20 UTC, committed by Pyun YongHyeon on 14 January 2010, 21:54:20 UTC
  Add bus_dma(9) and endianness support to ste(4).
   o Sorted includes and added missing header files.
   o Added basic endianness support. In theory ste(4) should work on
     any architectures.
   o Remove the use of contigmalloc(9), contigfree(9) and vtophys(9).
   o Added 8 byte alignment limitation of TX/RX descriptor.
   o Added 1 byte alignment requirement for TX/RX buffers.
   o ste(4) controllers does not support DAC. Limit DMA address space
     to be within 32bit address.
   o Added spare DMA map to gracefully recover from DMA map failure.
   o Removed dead code for checking STE_RXSTAT_DMADONE bit. The bit
     was already checked in each iteration of loop so it can't be true.
   o Added second argument count to ste_rxeof(). It is used to limit
     number of iterations done in RX handler. ATM polling is the only
     consumer.
   o Removed ste_rxeoc() which was added to address RX stuck issue
     (cvs rev 1.66). Unlike TX descriptors, ST201 supports chaining
     descriptors to form a ring for RX descriptors. If RX descriptor
     chaining is not supported it's possible for controller to stop
     receiving incoming frames once controller pass the end of RX
     descriptor which in turn requires driver post new RX
     descriptors to receive more frames. For TX descriptors which
     does not support chaning, we exactly do manual chaining in
     driver by concatenating new descriptors to the end of previous
     TX chain.
     Maybe the workaround was borrowed from other drivers that does
     not support RX descriptor chaining, which is not valid for ST201
     controllers. I still have no idea how this address RX stuck
     issue and I can't reproduce the RX stuck issue on DFE-550TX
     controller.
   o Removed hw.ste_rxsyncs sysctl as the workaround was removed.
   o TX/RX side bus_dmamap_load_mbuf_sg(9) support.
   o Reimplemented optimized ste_encap().
   o Simplified TX logic of ste_start_locked().
   o Added comments for TFD/RFD requirements.
   o Increased number of RX descriptors to 128 from 64. 128 gave much
     better performance than 64 under high network loads.
1 parent d982c88
Raw File
sconfig.c
/*
 * Channel configuration utility for Cronyx serial adapters.
 *
 * Copyright (C) 1997-2002 Cronyx Engineering.
 * Author: Serge Vakulenko, <vak@cronyx.ru>
 *
 * Copyright (C) 1999-2005 Cronyx Engineering.
 * Author: Roman Kurakin, <rik@cronyx.ru>
 *
 * This software is distributed with NO WARRANTIES, not even the implied
 * warranties for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 *
 * Authors grant any other persons or organisations permission to use
 * or modify this software as long as this message is kept with the software,
 * all derivative works or modified versions.
 *
 * Cronyx Id: sconfig.c,v 1.4.2.2 2005/11/09 13:01:35 rik Exp $
 */

#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <ctype.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <net/if.h>
#include <machine/cserial.h>

#define MAXCHAN 128

int vflag, eflag, sflag, mflag, cflag, fflag, iflag, aflag, xflag;
int tflag, uflag;
char mask[64];
int adapter_type;		/* 0-sigma, 1-tau, 2-taupci, 3-tau32 */
char chan_name[16];

static void
usage (void)
{
	printf(
"Serial Adapter Configuration Utility\n"
"Copyright (C) 1998-2005 Cronyx Engineering.\n"
"See also man sconfig (8)\n"
"Usage:\n"
"\tsconfig [-aimsxeftuc] [device [parameters ...]]\n"
"\n"
"Options:\n"
"\t<no options>\t\t -- print channel options\n"
"\t-a\t\t\t -- print all settings of the channel\n"
"\t-i\t\t\t -- print network interface status\n"
"\t-m\t\t\t -- print modem signal status\n"
"\t-s\t\t\t -- print channel statistics\n"
"\t-x\t\t\t -- print extended channel statistics\n"
"\t-e\t\t\t -- print short E1/G703 statistics\n"
"\t-f\t\t\t -- print full E1/G703 statistics\n"
"\t-t\t\t\t -- print short E3/T3/STS-1 statistics\n"
"\t-u\t\t\t -- print full E3/T3/STS-1 statistics\n"
"\t-c\t\t\t -- clear statistics\n"
"\nParameters:\n"
"\t<number>\t\t -- baud rate, internal clock\n"
"\textclock\t\t -- external clock (default)\n"
"\nProtocol options:\n"
"\tasync\t\t\t -- asynchronous protocol\n"
#ifdef __linux__
"\tsync\t\t\t -- synchronous protocol\n"
#endif
"\tcisco\t\t\t -- Cisco/HDLC protocol\n"
"\tfr\t\t\t -- Frame Relay protocol\n"
#ifdef __linux__
"\t    dlci<number>\t -- Add new DLCI\n"
#endif
"\tppp\t\t\t -- PPP protocol\n"
#ifdef __linux__
"\trbrg\t\t\t -- Remote bridge\n"
"\traw\t\t\t -- raw HDLC protocol\n"
"\tpacket\t\t\t -- packetized HDLC protocol\n"
"\tidle\t\t\t -- no protocol\n"
#else
"\t    keepalive={on,of}\t -- Enable/disable keepalive\n"
#endif
"\nInterface options:\n"
"\tport={rs232,v35,rs449}\t -- port type (for old models of Sigma)\n"
"\tcfg={A,B,C}\t\t -- adapter configuration\n"
"\tloop={on,off}\t\t -- internal loopback\n"
"\trloop={on,off}\t\t -- remote loopback\n"
"\tdpll={on,off}\t\t -- DPLL mode\n"
"\tnrzi={on,off}\t\t -- NRZI encoding\n"
"\tinvclk={on,off}\t\t -- invert receive and transmit clock\n"
"\tinvrclk={on,off}\t -- invert receive clock\n"
"\tinvtclk={on,off}\t -- invert transmit clock\n"
"\thigain={on,off}\t\t -- E1 high non linear input sensitivity \n\t\t\t\t    (long line)\n"
"\tmonitor={on,off}\t -- E1 high linear input sensitivity \n\t\t\t\t    (interception mode)\n"
"\tphony={on,off}\t\t -- E1 telepnony mode\n"
"\tunfram={on,off}\t\t -- E1 unframed mode\n"
"\tscrambler={on,off}\t -- G.703 scrambling mode\n"
"\tuse16={on,off}\t\t -- E1 timeslot 16 usage\n"
"\tcrc4={on,off}\t\t -- E1 CRC4 mode\n"
#ifdef __linux__
"\tami={on,off}\t\t -- E1 AMI or HDB3 line code\n"
"\tmtu={size}\t\t -- set MTU in bytes\n"
#endif
"\tsyn={int,rcv,rcvX}\t -- G.703 transmit clock\n"
"\tts=...\t\t\t -- E1 timeslots\n"
"\tpass=...\t\t -- E1 subchannel timeslots\n"
"\tdir=<num>\t\t -- connect channel to link<num>\n"
/*"\trqken={size}\t\t -- set receive queue length in packets\n"*/
/*"\tcablen={on,off}\t\t -- T3/STS-1 high transmitter output for long cable\n"*/
"\tdebug={0,1,2}\t\t -- enable/disable debug messages\n"
	);
	exit (0);
}

static unsigned long
scan_timeslots (char *s)
{
	char *e;
	long v;
	int i;
	unsigned long ts, lastv;

	ts = lastv = 0;
	for (;;) {
		v = strtol (s, &e, 10);
		if (e == s)
			break;
		if (*e == '-') {
			lastv = v;
			s = e+1;
			continue;
		}
		if (*e == ',')
			++e;

		if (lastv)
			for (i=lastv; i<v; ++i)
				ts |= 1L << i;
		ts |= 1L << v;

		lastv = 0;
		s = e;
	}
	return ts;
}

static int
ppp_ok (void)
{
#ifdef __linux__
	int s, p;
	struct ifreq ifr;
	char pttyname[32];
	char *p1, *p2;
	int i, j;
	int ppp_disc = N_PPP;

	/*
	 * Open a socket for doing the ioctl operations.
	 */
	s = socket (AF_INET, SOCK_DGRAM, 0);
	if (s < 0) {
		fprintf (stderr, "Error opening socket.\n");
		return 0;
	}
	strncpy (ifr.ifr_name, "ppp0", sizeof (ifr.ifr_name));
	if (ioctl (s, SIOCGIFFLAGS, (caddr_t) &ifr) >= 0) {
		/* Ok. */
		close (s);
		return 1;
	}
	close (s);

	/* open pseudo-tty and try to set PPP discipline */
	sprintf (pttyname, "/dev/ptyXX");
	p1 = &pttyname[8];
	p2 = &pttyname[9];
	for (i=0; i<16; i++) {
		struct stat stb;

		*p1 = "pqrstuvwxyzabcde"[i];
		*p2 = '0';
		if (stat (pttyname, &stb) < 0)
			continue;
		for (j=0; j<16; j++) {
			*p2 = "0123456789abcdef"[j];
			p = open (pttyname, 2);
			if (p > 0) {
				if (ioctl (p, TIOCSETD, &ppp_disc) < 0) {
					fprintf (stderr, "No PPP discipline in kernel.\n");
					close (p);
					return 0;
				}
				close (p);
				return 1;
			}
		}
	}
	fprintf (stderr, "Cannot get pseudo-tty.\n");
	return 0;
#else
	return 1;
#endif
}

static char *
format_timeslots (unsigned long s)
{
	static char buf [100];
	char *p = buf;
	int i;

	for (i=1; i<32; ++i)
		if ((s >> i) & 1) {
			int prev = (i > 1)  & (s >> (i-1));
			int next = (i < 31) & (s >> (i+1));

			if (prev) {
				if (next)
					continue;
				*p++ = '-';
			} else if (p > buf)
				*p++ = ',';

			if (i >= 10)
				*p++ = '0' + i / 10;
			*p++ = '0' + i % 10;
		}
	*p = 0;
	return buf;
}

static void
print_modems (int fd, int need_header)
{
	int status;

	if (ioctl (fd, TIOCMGET, &status) < 0) {
		perror ("getting modem status");
		return;
	}
	if (need_header)
		printf ("Channel\tLE\tDTR\tDSR\tRTS\tCTS\tCD\n");
	printf ("%s\t%s\t%s\t%s\t%s\t%s\t%s\n", chan_name,
		status & TIOCM_LE  ? "On" : "-",
		status & TIOCM_DTR ? "On" : "-",
		status & TIOCM_DSR ? "On" : "-",
		status & TIOCM_RTS ? "On" : "-",
		status & TIOCM_CTS ? "On" : "-",
		status & TIOCM_CD  ? "On" : "-");
}

static void
#ifdef __linux__
print_ifconfig (int fd)
#else
print_ifconfig (int fd __unused)
#endif
{
	char buf [64];
#ifdef __linux__
	char protocol [8];

	if (ioctl (fd, SERIAL_GETPROTO, &protocol) >= 0 &&
	    strcmp (protocol, "fr") == 0)
		sprintf (buf, "ifconfig %sd16 2>/dev/null", chan_name);
	else
#endif
	sprintf (buf, "ifconfig %s 2>/dev/null", chan_name);
	system (buf);
}

static void
set_debug_ifconfig (int on)
{
	char buf [64];
	sprintf (buf, "ifconfig %s %sdebug 2>/dev/null", chan_name,
		 on ? "" : "-");
	system (buf);
}

static char *
format_long (unsigned long val)
{
	static char s[32];
	int l;
	l = sprintf (s, "%lu", val);
	if (l>7 && !sflag) {
		s[3] = s[2];
		s[2] = s[1];
		s[1] = '.';
		s[4] = 'e';
		sprintf (s + 5, "%02d", l-1);
	}
	return s;
}

static void
print_stats (int fd, int need_header)
{
	struct serial_statistics st;
	unsigned long sarr [9];
	int i;

	if (ioctl (fd, SERIAL_GETSTAT, &st) < 0) {
		perror ("getting statistics");
		return;
	}
	if (need_header) {
		if (sflag) {
			printf ("        ------------Receive-----------      "
				"------------Transmit----------\n");
			printf ("Channel Interrupts  Packets     Errors      "
				"Interrupts  Packets     Errors\n");
		}
		else    {
			printf ("        --------Receive---------------  "
				"--------Transmit--------------  Modem\n");
			printf ("Channel Intrs   Bytes   Packets Errors  "
				"Intrs   Bytes   Packets Errors  Intrs\n");
		}
	}
	
	sarr [0] = st.rintr;
	sarr [1] = st.ibytes;
	sarr [2] = st.ipkts;
	sarr [3] = st.ierrs;
	sarr [4] = st.tintr;
	sarr [5] = st.obytes;
	sarr [6] = st.opkts;
	sarr [7] = st.oerrs;
	sarr [8] = st.mintr;
	printf ("%s", chan_name);
	if (sflag) {
		printf ("\t%-12lu%-12lu%-12lu%-12lu%-12lu%-12lu", sarr[0],
			sarr[2], sarr[3], sarr[4], sarr[6], sarr[7]);
	} else {
		for (i = 0; i < 9; i++)
			printf ("\t%s", format_long (sarr [i]));
		printf ("\n");
	}
}

static void
clear_stats (int fd)
{
	if (ioctl (fd, SERIAL_CLRSTAT, 0) < 0) {
		perror ("clearing statistics");
		exit (-1);
	}
}

static char *
format_e1_status (unsigned long status)
{
	static char buf [80];

	if (status == 0)
		return "n/a";
	if (status & E1_NOALARM)
		return "Ok";
	buf[0] = 0;
	if (status & E1_LOS)     strcat (buf, ",LOS");
	if (status & E1_AIS)     strcat (buf, ",AIS");
	if (status & E1_LOF)     strcat (buf, ",LOF");
	if (status & E1_LOMF)    strcat (buf, ",LOMF");
	if (status & E1_CRC4E)   strcat (buf, ",CRC4E");
	if (status & E1_FARLOF)  strcat (buf, ",FARLOF");
	if (status & E1_AIS16)   strcat (buf, ",AIS16");
	if (status & E1_FARLOMF) strcat (buf, ",FARLOMF");
/*	if (status & E1_TSTREQ)  strcat (buf, ",TSTREQ");*/
/*	if (status & E1_TSTERR)  strcat (buf, ",TSTERR");*/
	if (buf[0] == ',')
		return buf+1;
	return "Unknown";
}

static void
print_frac (int leftalign, unsigned long numerator, unsigned long divider)
{
	int n;

	if (numerator < 1 || divider < 1) {
		printf (leftalign ? "/-   " : "    -");
		return;
	}
	n = (int) (0.5 + 1000.0 * numerator / divider);
	if (n < 1000) {
		printf (leftalign ? "/.%-3d" : " .%03d", n);
		return;
	}
	putchar (leftalign ? '/' : ' ');

	if      (n >= 1000000) n = (n+500) / 1000 * 1000;
	else if (n >= 100000)  n = (n+50)  / 100 * 100;
	else if (n >= 10000)   n = (n+5)   / 10 * 10;

	switch (n) {
	case 1000:    printf (".999"); return;
	case 10000:   n = 9990;   break;
	case 100000:  n = 99900;  break;
	case 1000000: n = 999000; break;
	}
	if (n < 10000)        printf ("%d.%d", n/1000, n/10%100);
	else if (n < 100000)  printf ("%d.%d", n/1000, n/100%10);
	else if (n < 1000000) printf ("%d.", n/1000);
	else                  printf ("%d", n/1000);
}

static void
print_e1_stats (int fd, int need_header)
{
	struct e1_statistics st;
	int i, maxi;

	if (need_header)
		printf ("Chan\t Unav/Degr  Bpv/Fsyn  CRC/RCRC  Err/Lerr  Sev/Bur   Oof/Slp  Status\n");

	if (ioctl (fd, SERIAL_GETESTAT, &st) < 0)
		return;
	printf ("%s\t", chan_name);

	/* Unavailable seconds, degraded minutes */
	print_frac (0, st.currnt.uas, st.cursec);
	print_frac (1, 60 * st.currnt.dm, st.cursec);

	/* Bipolar violations, frame sync errors */
	print_frac (0, st.currnt.bpv, st.cursec);
	print_frac (1, st.currnt.fse, st.cursec);

	/* CRC errors, remote CRC errors (E-bit) */
	print_frac (0, st.currnt.crce, st.cursec);
	print_frac (1, st.currnt.rcrce, st.cursec);

	/* Errored seconds, line errored seconds */
	print_frac (0, st.currnt.es, st.cursec);
	print_frac (1, st.currnt.les, st.cursec);

	/* Severely errored seconds, bursty errored seconds */
	print_frac (0, st.currnt.ses, st.cursec);
	print_frac (1, st.currnt.bes, st.cursec);

	/* Out of frame seconds, controlled slip seconds */
	print_frac (0, st.currnt.oofs, st.cursec);
	print_frac (1, st.currnt.css, st.cursec);

	printf (" %s\n", format_e1_status (st.status));

	if (fflag) {
		/* Print total statistics. */
		printf ("\t");
		print_frac (0, st.total.uas, st.totsec);
		print_frac (1, 60 * st.total.dm, st.totsec);

		print_frac (0, st.total.bpv, st.totsec);
		print_frac (1, st.total.fse, st.totsec);

		print_frac (0, st.total.crce, st.totsec);
		print_frac (1, st.total.rcrce, st.totsec);

		print_frac (0, st.total.es, st.totsec);
		print_frac (1, st.total.les, st.totsec);

		print_frac (0, st.total.ses, st.totsec);
		print_frac (1, st.total.bes, st.totsec);

		print_frac (0, st.total.oofs, st.totsec);
		print_frac (1, st.total.css, st.totsec);

		printf (" -- Total\n");

		/* Print 24-hour history. */
		maxi = (st.totsec - st.cursec) / 900;
		if (maxi > 48)
			maxi = 48;
		for (i=0; i<maxi; ++i) {
			printf ("       ");
			print_frac (0, st.interval[i].uas, 15*60);
			print_frac (1, 60 * st.interval[i].dm, 15*60);

			print_frac (0, st.interval[i].bpv, 15*60);
			print_frac (1, st.interval[i].fse, 15*60);

			print_frac (0, st.interval[i].crce, 15*60);
			print_frac (1, st.interval[i].rcrce, 15*60);

			print_frac (0, st.interval[i].es, 15*60);
			print_frac (1, st.interval[i].les, 15*60);

			print_frac (0, st.interval[i].ses, 15*60);
			print_frac (1, st.interval[i].bes, 15*60);

			print_frac (0, st.interval[i].oofs, 15*60);
			print_frac (1, st.interval[i].css, 15*60);

			if (i < 3)
				printf (" -- %dm\n", (i+1)*15);
			else
				printf (" -- %dh %dm\n", (i+1)/4, (i+1)%4*15);
		}
	}
}

static char *
format_e3_status (unsigned long status)
{
	static char buf [80];

	buf[0] = 0;
	if (status & E3_LOS)     strcat (buf, ",LOS");
	if (status & E3_TXE)     strcat (buf, ",XMIT");
	if (buf[0] == ',')
		return buf+1;
	return "Ok";
}

static char *
format_e3_cv (unsigned long cv, unsigned long baud, unsigned long atime)
{
	static char buf[80];
	
	if (!cv || !baud || !atime)
		sprintf (buf, "         -         ");
	else
		sprintf (buf, "%10lu (%.1e)", cv, (double)cv/baud/atime);
	return buf;
}

static void
print_e3_stats (int fd, int need_header)
{
	struct e3_statistics st;
	int i, maxi;
	long baud;

	if (need_header)
		printf ("Chan\t--Code Violations---\t\t\t\t\t ----Status----\n");

	if (ioctl (fd, SERIAL_GETE3STAT, &st) < 0 ||
	    ioctl (fd, SERIAL_GETBAUD, &baud) < 0)
		return;
		
	if (!st.cursec)
		st.cursec = 1;

	printf ("%s\t%s\t\t\t\t\t", chan_name,
		format_e3_cv (st.ccv, baud, st.cursec));

	printf (" %s\n", format_e3_status (st.status));


	if (uflag) {
		/* Print total statistics. */
		printf ("\t%s\t\t\t\t\t",
			format_e3_cv (st.tcv, baud, st.totsec));
		printf (" -- Total\n");

		/* Print 24-hour history. */
		maxi = (st.totsec - st.cursec) / 900;
		if (maxi > 48)
			maxi = 48;
		for (i=0; i<maxi; ++i) {
			printf ("\t%s\t\t\t\t\t",
				format_e3_cv (st.icv[i], baud, 15*60));
			if (i < 3)
				printf (" -- %2dm\n", (i+1)*15);
			else
				printf (" -- %2dh %2dm\n", (i+1)/4, (i+1)%4*15);
		}
	}
}

static void
print_chan (int fd)
{
	char protocol [8];
	char cfg;
	int loop, dpll, nrzi, invclk, clk, higain, phony, use16, crc4;
	int level, keepalive, debug, port, invrclk, invtclk, unfram, monitor;
	int cable, dir, scrambler, ami, mtu;
	int cablen, rloop, rqlen;
	long baud, timeslots, subchan;
	int protocol_valid, baud_valid, loop_valid, use16_valid, crc4_valid;
	int dpll_valid, nrzi_valid, invclk_valid, clk_valid, phony_valid;
	int timeslots_valid, subchan_valid, higain_valid, level_valid;
	int keepalive_valid, debug_valid, cfg_valid, port_valid;
	int invrclk_valid, invtclk_valid, unfram_valid, monitor_valid;
	int cable_valid, dir_valid, scrambler_valid, ami_valid, mtu_valid;
	int cablen_valid, rloop_valid, rqlen_valid;

	protocol_valid  = ioctl (fd, SERIAL_GETPROTO, &protocol) >= 0;
	cfg_valid       = ioctl (fd, SERIAL_GETCFG, &cfg) >= 0;
	baud_valid      = ioctl (fd, SERIAL_GETBAUD, &baud) >= 0;
	loop_valid      = ioctl (fd, SERIAL_GETLOOP, &loop) >= 0;
	dpll_valid      = ioctl (fd, SERIAL_GETDPLL, &dpll) >= 0;
	nrzi_valid      = ioctl (fd, SERIAL_GETNRZI, &nrzi) >= 0;
	invclk_valid    = ioctl (fd, SERIAL_GETINVCLK, &invclk) >= 0;
	invrclk_valid	= ioctl (fd, SERIAL_GETINVRCLK, &invrclk) >= 0;
	invtclk_valid	= ioctl (fd, SERIAL_GETINVTCLK, &invtclk) >= 0;
	clk_valid       = ioctl (fd, SERIAL_GETCLK, &clk) >= 0;
	timeslots_valid = ioctl (fd, SERIAL_GETTIMESLOTS, &timeslots) >= 0;
	subchan_valid   = ioctl (fd, SERIAL_GETSUBCHAN, &subchan) >= 0;
	higain_valid    = ioctl (fd, SERIAL_GETHIGAIN, &higain) >= 0;
	phony_valid     = ioctl (fd, SERIAL_GETPHONY, &phony) >= 0;
	unfram_valid    = ioctl (fd, SERIAL_GETUNFRAM, &unfram) >= 0;
	monitor_valid   = ioctl (fd, SERIAL_GETMONITOR, &monitor) >= 0;
	use16_valid     = ioctl (fd, SERIAL_GETUSE16, &use16) >= 0;
	crc4_valid      = ioctl (fd, SERIAL_GETCRC4, &crc4) >= 0;
	ami_valid	= ioctl (fd, SERIAL_GETLCODE, &ami) >= 0;
	level_valid     = ioctl (fd, SERIAL_GETLEVEL, &level) >= 0;
	keepalive_valid = ioctl (fd, SERIAL_GETKEEPALIVE, &keepalive) >= 0;
	debug_valid     = ioctl (fd, SERIAL_GETDEBUG, &debug) >= 0;
	port_valid	= ioctl (fd, SERIAL_GETPORT, &port) >= 0;
	cable_valid	= ioctl (fd, SERIAL_GETCABLE, &cable) >= 0;
	dir_valid	= ioctl (fd, SERIAL_GETDIR, &dir) >= 0;
	scrambler_valid	= ioctl (fd, SERIAL_GETSCRAMBLER, &scrambler) >= 0;
	cablen_valid	= ioctl (fd, SERIAL_GETCABLEN, &cablen) >= 0;
	rloop_valid	= ioctl (fd, SERIAL_GETRLOOP, &rloop) >= 0;
	mtu_valid	= ioctl (fd, SERIAL_GETMTU, &mtu) >= 0;
	rqlen_valid	= ioctl (fd, SERIAL_GETRQLEN, &rqlen) >= 0;

	printf ("%s", chan_name);
	if (port_valid)
		switch (port) {
		case 0:	printf (" (rs232)"); break;
		case 1:	printf (" (v35)"); break;
		case 2:	printf (" (rs530)"); break;
		}
	else if (cable_valid)
		switch (cable) {
		case 0:	printf (" (rs232)"); break;
		case 1:	printf (" (v35)"); break;
		case 2:	printf (" (rs530)"); break;
		case 3:	printf (" (x21)"); break;
		case 4:	printf (" (rs485)"); break;
		case 9:	printf (" (no cable)"); break;
		}
	if (debug_valid && debug)
		printf (" debug=%d", debug);
	if (protocol_valid && *protocol)
		printf (" %.8s", protocol);
	else
		printf (" idle");
	if (cablen_valid)
		printf (" cablen=%s", cablen ? "on" : "off");
	if (keepalive_valid)
		printf (" keepalive=%s", keepalive ? "on" : "off");

	if (cfg_valid)
		switch (cfg) {
		case 'a' :	printf (" cfg=A");	break;
		case 'b' :	printf (" cfg=B");	break;
		case 'c' :	printf (" cfg=C");	break;
		case 'd' :	printf (" cfg=D");	break;
		default  :	printf (" cfg=unknown");
		}
	if (dir_valid)
		printf (" dir=%d", dir);

	if (baud_valid) {
		if (baud)
			printf (" %ld", baud);
		else
			printf (" extclock");
	}
	if (mtu_valid)
		printf (" mtu=%d", mtu);

	if (aflag && rqlen_valid)
		printf (" rqlen=%d", rqlen);

	if (clk_valid)
		switch (clk) {
		case E1CLK_INTERNAL:	  printf (" syn=int");     break;
		case E1CLK_RECEIVE:	  printf (" syn=rcv");     break;
		case E1CLK_RECEIVE_CHAN0: printf (" syn=rcv0");    break;
		case E1CLK_RECEIVE_CHAN1: printf (" syn=rcv1");    break;
		case E1CLK_RECEIVE_CHAN2: printf (" syn=rcv2");    break;
		case E1CLK_RECEIVE_CHAN3: printf (" syn=rcv3");    break;
		default:                  printf (" syn=%d", clk); break;
		}

	if (dpll_valid)
		printf (" dpll=%s", dpll ? "on" : "off");
	if (nrzi_valid)
		printf (" nrzi=%s", nrzi ? "on" : "off");
	if (invclk_valid)
		printf (" invclk=%s", invclk ? "on" : "off");
	if (invrclk_valid)
		printf (" invrclk=%s", invrclk ? "on" : "off");
	if (invtclk_valid)
		printf (" invtclk=%s", invtclk ? "on" : "off");
	if (unfram_valid)
		printf (" unfram=%s", unfram ? "on" : "off");
	if (use16_valid)
		printf (" use16=%s", use16 ? "on" : "off");
	if (aflag) {
		if (crc4_valid)
			printf (" crc4=%s", crc4 ? "on" : "off");
		if (higain_valid)
			printf (" higain=%s", higain ? "on" : "off");
		if (monitor_valid)
			printf (" monitor=%s", monitor ? "on" : "off");
		if (phony_valid)
			printf (" phony=%s", phony ? "on" : "off");
		if (scrambler_valid)
			printf (" scrambler=%s", scrambler ? "on" : "off");
		if (loop_valid)
			printf (" loop=%s", loop ? "on" : "off");
		if (rloop_valid)
			printf (" rloop=%s", rloop ? "on" : "off");
		if (ami_valid)
			printf (" ami=%s", ami ? "on" : "off");
	}
	if (timeslots_valid)
		printf (" ts=%s", format_timeslots (timeslots));
	if (subchan_valid)
		printf (" pass=%s", format_timeslots (subchan));
	if (level_valid)
		printf (" (level=-%.1fdB)", level / 10.0);
	printf ("\n");
}

static void
setup_chan (int fd, int argc, char **argv)
{
	int i, mode, loop, nrzi, dpll, invclk, phony, use16, crc4, unfram, ami;
	int higain, clk, keepalive, debug, port, dlci, invrclk, invtclk;
	int monitor, dir, scrambler, rloop, cablen;
	int mode_valid;
	long baud, timeslots, mtu, rqlen;

	for (i=0; i<argc; ++i) {
		if (argv[i][0] >= '0' && argv[i][0] <= '9') {
			baud = strtol (argv[i], 0, 10);
			ioctl (fd, SERIAL_SETBAUD, &baud);
		} else if (strcasecmp ("extclock", argv[i]) == 0) {
			baud = 0;
			ioctl (fd, SERIAL_SETBAUD, &baud);
		} else if (strncasecmp ("cfg=", argv[i], 4) == 0) {
			if (strncasecmp ("a", argv[i]+4, 1) == 0)
				ioctl (fd, SERIAL_SETCFG, "a");
			else if (strncasecmp ("b", argv[i]+4, 1) == 0)
				ioctl (fd, SERIAL_SETCFG, "b");
			else if (strncasecmp ("c", argv[i]+4, 1) == 0)
				ioctl (fd, SERIAL_SETCFG, "c");
			else if (strncasecmp ("d", argv[i]+4, 1) == 0)
				ioctl (fd, SERIAL_SETCFG, "d");
			else {
				fprintf (stderr, "invalid cfg\n");
				exit (-1);
			}
		} else if (strcasecmp ("idle", argv[i]) == 0)
			ioctl (fd, SERIAL_SETPROTO, "\0\0\0\0\0\0\0");
		else if (strcasecmp ("async", argv[i]) == 0) {
			mode = SERIAL_ASYNC;
			if (ioctl (fd, SERIAL_SETMODE, &mode) >= 0)
				ioctl (fd, SERIAL_SETPROTO, "async\0\0");
		} else if (strcasecmp ("sync", argv[i]) == 0) {
			mode = SERIAL_HDLC;
			if (ioctl (fd, SERIAL_SETMODE, &mode) >= 0)
				ioctl (fd, SERIAL_SETPROTO, "sync\0\0\0");
		} else if (strcasecmp ("cisco", argv[i]) == 0) {
			mode = SERIAL_HDLC;
			ioctl (fd, SERIAL_SETMODE, &mode);
			ioctl (fd, SERIAL_SETPROTO, "cisco\0\0");
		} else if (strcasecmp ("rbrg", argv[i]) == 0) {
			mode = SERIAL_HDLC;
			ioctl (fd, SERIAL_SETMODE, &mode);
			ioctl (fd, SERIAL_SETPROTO, "rbrg\0\0\0");
		} else if (strcasecmp ("raw", argv[i]) == 0) {
			mode = SERIAL_HDLC;
			ioctl (fd, SERIAL_SETMODE, &mode);
			ioctl (fd, SERIAL_SETPROTO, "raw\0\0\0\0");
		} else if (strcasecmp ("packet", argv[i]) == 0) {
			mode = SERIAL_HDLC;
			ioctl (fd, SERIAL_SETMODE, &mode);
			ioctl (fd, SERIAL_SETPROTO, "packet\0");
		} else if (strcasecmp ("ppp", argv[i]) == 0) {
			/* check that ppp line discipline is present */
			if (ppp_ok ()) {
				mode = SERIAL_HDLC;
				ioctl (fd, SERIAL_SETMODE, &mode);
				ioctl (fd, SERIAL_SETPROTO, "ppp\0\0\0\0");
			}
		} else if (strncasecmp ("keepalive=", argv[i], 10) == 0) {
			keepalive = (strcasecmp ("on", argv[i] + 10) == 0);
			ioctl (fd, SERIAL_SETKEEPALIVE, &keepalive);
		} else if (strcasecmp ("fr", argv[i]) == 0) {
			mode = SERIAL_HDLC;
			ioctl (fd, SERIAL_SETMODE, &mode);
			ioctl (fd, SERIAL_SETPROTO, "fr\0\0\0\0\0");
		} else if (strcasecmp ("zaptel", argv[i]) == 0) {
			mode = SERIAL_HDLC;
			ioctl (fd, SERIAL_SETMODE, &mode);
			ioctl (fd, SERIAL_SETPROTO, "zaptel\0");
		} else if (strncasecmp ("debug=", argv[i], 6) == 0) {
			debug = strtol (argv[i]+6, 0, 10);
			mode_valid = ioctl (fd, SERIAL_GETMODE, &mode) >= 0;
			if (!mode_valid || mode != SERIAL_ASYNC) {
				if (debug == 0) {
					set_debug_ifconfig(0);
				} else {
					ioctl (fd, SERIAL_SETDEBUG, &debug);
					set_debug_ifconfig(1);
				}
			} else {
				ioctl (fd, SERIAL_SETDEBUG, &debug);
			}
		} else if (strncasecmp ("loop=", argv[i], 5) == 0) {
			loop = (strcasecmp ("on", argv[i] + 5) == 0);
			ioctl (fd, SERIAL_SETLOOP, &loop);
		} else if (strncasecmp ("rloop=", argv[i], 6) == 0) {
			rloop = (strcasecmp ("on", argv[i] + 6) == 0);
			ioctl (fd, SERIAL_SETRLOOP, &rloop);
		} else if (strncasecmp ("dpll=", argv[i], 5) == 0) {
			dpll = (strcasecmp ("on", argv[i] + 5) == 0);
			ioctl (fd, SERIAL_SETDPLL, &dpll);
		} else if (strncasecmp ("nrzi=", argv[i], 5) == 0) {
			nrzi = (strcasecmp ("on", argv[i] + 5) == 0);
			ioctl (fd, SERIAL_SETNRZI, &nrzi);
		} else if (strncasecmp ("invclk=", argv[i], 7) == 0) {
			invclk = (strcasecmp ("on", argv[i] + 7) == 0);
			ioctl (fd, SERIAL_SETINVCLK, &invclk);
		} else if (strncasecmp ("invrclk=", argv[i], 8) == 0) {
			invrclk = (strcasecmp ("on", argv[i] + 8) == 0);
			ioctl (fd, SERIAL_SETINVRCLK, &invrclk);
		} else if (strncasecmp ("invtclk=", argv[i], 8) == 0) {
			invtclk = (strcasecmp ("on", argv[i] + 8) == 0);
			ioctl (fd, SERIAL_SETINVTCLK, &invtclk);
		} else if (strncasecmp ("higain=", argv[i], 7) == 0) {
			higain = (strcasecmp ("on", argv[i] + 7) == 0);
			ioctl (fd, SERIAL_SETHIGAIN, &higain);
		} else if (strncasecmp ("phony=", argv[i], 6) == 0) {
			phony = (strcasecmp ("on", argv[i] + 6) == 0);
			ioctl (fd, SERIAL_SETPHONY, &phony);
		} else if (strncasecmp ("unfram=", argv[i], 7) == 0) {
			unfram = (strcasecmp ("on", argv[i] + 7) == 0);
			ioctl (fd, SERIAL_SETUNFRAM, &unfram);
		} else if (strncasecmp ("scrambler=", argv[i], 10) == 0) {
			scrambler = (strcasecmp ("on", argv[i] + 10) == 0);
			ioctl (fd, SERIAL_SETSCRAMBLER, &scrambler);
		} else if (strncasecmp ("monitor=", argv[i], 8) == 0) {
			monitor = (strcasecmp ("on", argv[i] + 8) == 0);
			ioctl (fd, SERIAL_SETMONITOR, &monitor);
		} else if (strncasecmp ("use16=", argv[i], 6) == 0) {
			use16 = (strcasecmp ("on", argv[i] + 6) == 0);
			ioctl (fd, SERIAL_SETUSE16, &use16);
		} else if (strncasecmp ("crc4=", argv[i], 5) == 0) {
			crc4 = (strcasecmp ("on", argv[i] + 5) == 0);
			ioctl (fd, SERIAL_SETCRC4, &crc4);
		} else if (strncasecmp ("ami=", argv[i], 4) == 0) {
			ami = (strcasecmp ("on", argv[i] + 4) == 0);
			ioctl (fd, SERIAL_SETLCODE, &ami);
		} else if (strncasecmp ("mtu=", argv[i], 4) == 0) {
			mtu = strtol (argv[i] + 4, 0, 10);
			ioctl (fd, SERIAL_SETMTU, &mtu);
		} else if (strncasecmp ("rqlen=", argv[i], 6) == 0) {
			rqlen = strtol (argv[i] + 6, 0, 10);
			ioctl (fd, SERIAL_SETRQLEN, &rqlen);
		} else if (strcasecmp ("syn=int", argv[i]) == 0) {
			clk = E1CLK_INTERNAL;
			ioctl (fd, SERIAL_SETCLK, &clk);
		} else if (strcasecmp ("syn=rcv", argv[i]) == 0) {
			clk = E1CLK_RECEIVE;
			ioctl (fd, SERIAL_SETCLK, &clk);
		} else if (strcasecmp ("syn=rcv0", argv[i]) == 0) {
			clk = E1CLK_RECEIVE_CHAN0;
			ioctl (fd, SERIAL_SETCLK, &clk);
		} else if (strcasecmp ("syn=rcv1", argv[i]) == 0) {
			clk = E1CLK_RECEIVE_CHAN1;
			ioctl (fd, SERIAL_SETCLK, &clk);
		} else if (strcasecmp ("syn=rcv2", argv[i]) == 0) {
			clk = E1CLK_RECEIVE_CHAN2;
			ioctl (fd, SERIAL_SETCLK, &clk);
		} else if (strcasecmp ("syn=rcv3", argv[i]) == 0) {
			clk = E1CLK_RECEIVE_CHAN3;
			ioctl (fd, SERIAL_SETCLK, &clk);
		} else if (strncasecmp ("ts=", argv[i], 3) == 0) {
			timeslots = scan_timeslots (argv[i] + 3);
			ioctl (fd, SERIAL_SETTIMESLOTS, &timeslots);
		} else if (strncasecmp ("pass=", argv[i], 5) == 0) {
			timeslots = scan_timeslots (argv[i] + 5);
			ioctl (fd, SERIAL_SETSUBCHAN, &timeslots);
		} else if (strncasecmp ("dlci", argv[i], 4) == 0) {
			dlci = strtol (argv[i]+4, 0, 10);
			ioctl (fd, SERIAL_ADDDLCI, &dlci);
		} else if (strncasecmp ("dir=", argv[i], 4) == 0) {
			dir = strtol (argv[i]+4, 0, 10);
			ioctl (fd, SERIAL_SETDIR, &dir);
		} else if (strncasecmp ("port=", argv[i], 5) == 0) {
			if (strncasecmp ("rs232", argv[i]+5, 5) == 0) {
				port = 0;
				ioctl (fd, SERIAL_SETPORT, &port);
			} else if (strncasecmp ("v35", argv[i]+5, 3) == 0) {
				port = 1;
				ioctl (fd, SERIAL_SETPORT, &port);
			} else if (strncasecmp ("rs449", argv[i]+5, 5) == 0) {
				port = 2;
				ioctl (fd, SERIAL_SETPORT, &port);
			} else
				fprintf (stderr, "invalid port type\n");
				exit (-1);
#if 1
		} else if (strcasecmp ("reset", argv[i]) == 0) {
			ioctl (fd, SERIAL_RESET, 0);
		} else if (strcasecmp ("hwreset", argv[i]) == 0) {
			ioctl (fd, SERIAL_HARDRESET, 0);
#endif
		} else if (strncasecmp ("cablen=", argv[i], 7) == 0) {
			loop = (strcasecmp ("on", argv[i] + 7) == 0);
			ioctl (fd, SERIAL_SETCABLEN, &cablen);
		}
	}
}

static void
get_mask (void)
{
#ifdef __linux__
	int fd;

	fd = open ("/dev/serial/ctl0", 0);
	if (fd < 0) {
		perror ("/dev/serial/ctl0");
		exit (-1);
	}
	if (ioctl (fd, SERIAL_GETREGISTERED, &mask) < 0) {
		perror ("getting list of channels");
		exit (-1);
	}
	close (fd);
#else
	int fd, fd1, fd2, fd3, i;
	char buf [80];

	for (i=0, fd=-1; i<12 && fd<0; i++) {
		sprintf (buf, "/dev/cx%d", i*4);
		fd = open (buf, 0);
	}

	for (i=0, fd1=-1; i<3 && fd1<0; i++) {
		sprintf (buf, "/dev/ct%d", i*2);
		fd1 = open (buf, 0);
	}

	for (i=0, fd2=-1; i<3 && fd2<0; i++) {
		sprintf (buf, "/dev/cp%d", i*4);
		fd2 = open (buf, 0);
	}

	/* Try only one */
	for (i=0, fd3=-1; i<1 && fd3<0; i++) {
		sprintf (buf, "/dev/ce%d", i*4);
		fd3 = open (buf, 0);
	}

	if ((fd < 0) && (fd1 < 0) && (fd2 < 0) && (fd3 < 0)) {
		fprintf (stderr, "No Cronyx adapters installed\n");
		exit (-1);
	}

	if (fd >= 0) {
		if (ioctl (fd, SERIAL_GETREGISTERED, &mask) < 0) {
			perror ("getting list of channels");
			exit (-1);
		}
		close (fd);
	}

	if (fd1 >= 0) {
		if (ioctl (fd1, SERIAL_GETREGISTERED, (mask+16)) < 0) {
			perror ("getting list of channels");
			exit (-1);
		}
		close (fd1);
	}

	if (fd2 >= 0) {
		if (ioctl (fd2, SERIAL_GETREGISTERED, (mask+32)) < 0) {
			perror ("getting list of channels");
			exit (-1);
		}
		close (fd2);
	}

	if (fd3 >= 0) {
		if (ioctl (fd3, SERIAL_GETREGISTERED, (mask+48)) < 0) {
			perror ("getting list of channels");
			exit (-1);
		}
		close (fd3);
	}
#endif
}

static int
open_chan_ctl (int num)
{
	char device [80];
	int fd;

#ifdef __linux__
	sprintf (device, "/dev/serial/ctl%d", num);
#else
	switch (adapter_type) {
	case 0:
		sprintf (device, "/dev/cx%d", num);
		break;
	case 1:
		sprintf (device, "/dev/ct%d", num);
		break;
	case 2:
		sprintf (device, "/dev/cp%d", num);
		break;
	case 3:
		sprintf (device, "/dev/ce%d", num);
		break;
	}
#endif
	fd = open (device, 0);
	if (fd < 0) {
		if (errno == ENODEV)
			fprintf (stderr, "chan%d: not configured\n", num);
		else
			perror (device);
		exit (-1);
	}
#ifdef __linux__
	if (ioctl (fd, SERIAL_GETNAME, &chan_name) < 0)
		sprintf (chan_name, "chan%d", num);
#else
	switch (adapter_type) {
	case 0: sprintf (chan_name, "cx%d", num); break;
	case 1: sprintf (chan_name, "ct%d", num); break;
	case 2: sprintf (chan_name, "cp%d", num); break;
	case 3: sprintf (chan_name, "ce%d", num); break;
	}
#endif
	return fd;
}

int
main (int argc, char **argv)
{
	char *p;
	int fd, need_header, chan_num;

	if (argc > 1 && strcmp(argv[1], "help") == 0)
		usage();

	for (;;) {
		switch (getopt (argc, argv, "mseftucviax")) {
		case -1:
			break;
		case 'a':
			++aflag;
			continue;
		case 'm':
			++mflag;
			continue;
		case 's':
			++sflag;
			continue;
		case 'e':
			++eflag;
			continue;
		case 'f':
			++eflag;
			++fflag;
			continue;
		case 't':
			++tflag;
			continue;
		case 'u':
			++tflag;
			++uflag;
			continue;
		case 'c':
			++cflag;
			continue;
		case 'v':
			++vflag;
			continue;
		case 'i':
			++iflag;
			continue;
		case 'x':
			++xflag;
			continue;
		default:
			usage();
		}
		break;
	}
	argc -= optind;
	argv += optind;

	if (argc <= 0) {
		get_mask ();
		need_header = 1;
		adapter_type = 0;
#ifndef __linux__
		for (; adapter_type < 4; ++adapter_type)
#endif
		{
		for (chan_num=0; chan_num<MAXCHAN; ++chan_num)
			if (mask[adapter_type*16+chan_num/8] & 1 << (chan_num & 7)) {
				fd = open_chan_ctl (chan_num);
				if (vflag) {
#ifdef __linux__
				char buf[256];
				if (ioctl (fd, SERIAL_GETVERSIONSTRING, &buf) >= 0) {
					printf ("Version: %s\n", buf);
					close (fd);
					return (0);
				}
#endif
				}
				if (iflag) {
					print_chan (fd);
					print_ifconfig (fd);
				} else if (sflag||xflag)
					print_stats (fd, need_header);
				else if (mflag)
					print_modems (fd, need_header);
				else if (eflag)
					print_e1_stats (fd, need_header);
				else if (tflag)
					print_e3_stats (fd, need_header);
				else if (cflag)
					clear_stats (fd);
				else
					print_chan (fd);
				close (fd);
				need_header = 0;
			}
		}
		return (0);
	}

	p = argv[0] + strlen (argv[0]);
	while (p > argv[0] && p[-1] >= '0' && p[-1] <= '9')
		--p;
	chan_num = strtol (p, 0, 10);
#ifndef __linux__
	if (strncasecmp ("cx", argv[0], 2)==0)
		adapter_type = 0;
	else if (strncasecmp ("ct", argv[0], 2)==0)
		adapter_type = 1;
	else if (strncasecmp ("cp", argv[0], 2)==0)
		adapter_type = 2;
	else if (strncasecmp ("ce", argv[0], 2)==0)
		adapter_type = 3;
	else {
		fprintf (stderr, "Wrong channel name\n");
		exit (-1);
	}
#endif
	argc--;
	argv++;

	fd = open_chan_ctl (chan_num);
	if (vflag) {
#ifdef __linux__
		char buf[256];
		if (ioctl (fd, SERIAL_GETVERSIONSTRING, &buf) >= 0)
			printf ("Version: %s\n", buf);
#endif
	}
	if (iflag) {
		print_chan (fd);
		print_ifconfig (fd);
		close (fd);
		return (0);
	}
	if (sflag||xflag) {
		print_stats (fd, 1);
		close (fd);
		return (0);
	}
	if (mflag) {
		print_modems (fd, 1);
		close (fd);
		return (0);
	}
	if (eflag) {
		print_e1_stats (fd, 1);
		close (fd);
		return (0);
	}
	if (tflag) {
		print_e3_stats (fd, 1);
		close (fd);
		return (0);
	}
	if (cflag) {
		clear_stats (fd);
		close (fd);
		return (0);
	}
	if (argc > 0)
		setup_chan (fd, argc, argv);
	else
		print_chan (fd);
	close (fd);
	return (0);
}
back to top