/*
 * Dialer support for Telebit Trailblazer Plus modem.
 */

#ifndef lint
static char *RCSid = "$Header: telebit.c,v 1.13 88/05/25 19:48:06 rayan Exp $";
#endif

#undef DEBUG 1

#ifdef	DEBUG
int debug = 1;
#define STATIC
#else
#define STATIC static
#endif

#include "tip.h"
#include <sys/time.h>

/*
 * The following is the Telebit register setup used here with this code, with
 * the TB+ attached to a fixed-speed interface on a remote terminal server.
 * When attaching to a computer the appropriate register should be set so
 * the TB+ interface speed matches the connect baud rate.
 *
E0 F1 M1 Q1 P V1 X1     Version BA4.00
S00=001 S01=000 S02=043 S03=013 S04=010 S05=008 S06=002 S07=040 S08=002 S09=006
S10=007 S11=070 S12=050 
S45=255 S47=004 S48=000 S49=000
S50=000 S51=005 S52=002 S53=004 S54=003 S55=000 S56=017 S57=019 S58=000 S59=000
S60=000 S61=000 S62=003 S63=001 S64=000 S65=000 S66=000 S67=000 S68=255 
S90=000 S91=000 S92=000 S95=000 
S100=000 S101=000 S102=000 S104=000 
S110=001 S111=030 S112=001 
S121=000 
 *
 */

	/* commands */
STATIC char	NAME[] =		"telebit";
STATIC char	WARMUP[] =		"\rat\r";
STATIC char	RESULT_CODES[] =	"\ratq0\r";
STATIC char	DONT_ANSWER[] =		"\rats0=0\r";
STATIC char	IGN_ESCAPE[] =		"\rats55=3\r";
STATIC char	CODES_4[] =		"\ratx1\r";
STATIC char	DIAL_PREFIX[] =		"\ratdt";

	/* result codes */;
STATIC char	MDM_OK[] =		"\r\nOK\r\n";
STATIC char	RING[] =		"\r\nRING\r\n";
STATIC char	RRING[] =		"\r\nRRING\r\n";
STATIC char	NO_CARRIER[] =		"\r\nNO CARRIER\r\n";
STATIC char	ERROR[] =		"\r\nERROR\r\n";
STATIC char	CONNECT[] =		"\r\nCONNECT 300\r\n";
STATIC char	CONNECT_1200[] =	"\r\nCONNECT 1200\r\n";
STATIC char	CONNECT_2400[] =	"\r\nCONNECT 2400\r\n";
STATIC char	CONNECT_FAST[] =	"\r\nCONNECT FAST\r\n";
STATIC char	NO_DIALTONE[] =		"\r\nNO DIALTONE\r\n";
STATIC char	NO_ANSWER[] =		"\r\nNO ANSWER\r\n";
STATIC char	BUSY[] =		"\r\nBUSY\r\n";

STATIC char	*notbcon[] =	{	BUSY,
					NO_DIALTONE,
					NO_ANSWER,
					ERROR,
					RING,
					NO_CARRIER,
					0
				};

/* The baudrate starts at 300, and doubles for next element of array */
STATIC char	*tbcon[] =	{	CONNECT,
					"ignore",
					CONNECT_1200,
					CONNECT_2400,
					"ignore",
					"ignore",
					CONNECT_FAST,
					0
				};
#ifdef	USG
STATIC struct termio termio;		/* needed on USG to set HUPCL */
#endif	/* USG */

#define	CMD_DELAY	5	/* number of seconds to wait for response
					while in command mode */
#define DIAL_DELAY	120	/* number of seconds to wait for response
					after sending dial string */
#define	MAXSTR	100

static jmp_buf	jmpbuf;

int 	tbread(), tbwrite();

tb_dialer(num, acu)
register char	*num;
char	*acu;
{
	int	ret;
	int	i, j;
	char	buf1[MAXSTR], buf2[MAXSTR], buf3[MAXSTR];
#ifdef ACULOG
	char	line[MAXSTR];
#endif

	/*
	 * wake up the modem if it's listening...
	 */
	if (boolean(value(VERBOSE)))
		printf("diddling dialer...");
#ifdef DEBUG
	if (debug)
		fprintf(stderr, "BR=%d\n", BR);
#endif

	sleep(1);	/* Give modem time to settle down after reset */
	if ((!tbset(RESULT_CODES, buf1, num, "is naughty")
		     && !tbset(RESULT_CODES, buf1, num, "not listening"))
	    || !tbset(DONT_ANSWER, buf1, num, "auto-answer not turned off")
	    || !tbset(IGN_ESCAPE, buf1, num, "escape code not turned off")
	    || !tbset(CODES_4, buf1, num, "result codes not set"))
		return 0;

	/* insurance ? */
#ifdef	USG
	if (ioctl(FD, TCGETA, &termio) < 0)
		perror("tip: TCGETA ioctl");
	termio.c_cflag |= HUPCL;
	if (ioctl(FD, TCSETA, &termio) < 0)
		perror("tip: TCSETA ioctl");
#else	/* USG */
	if (ioctl(FD, TIOCHPCL, 0) < 0)
		perror("tip: TIOCHPCL ioctl");
#endif	/* USG */

	buf2[0] = '\0';
	for (i = 0, j = 300; tbcon[i] != (char *)0; i++, j*=2) {
		if (BR == j) {
			strcpy(buf2, tbcon[i]);
			break;
		}
	}
	if (buf2[0] == '\0') {
		sprintf(buf2, "unsupported baud rate (%d)", BR);
		goto error;
	}

	/* Is it safe to just say ATD and let the options setting rule? */
	tbwrite(DIAL_PREFIX);
	tbwrite(num);
	tbwrite("\r");
	if (boolean(value(VERBOSE)))
		printf(" dialing... ");
	do {
		if (ret = tbread(buf1, DIAL_DELAY, (u_int) strlen(buf2)) == 0) {
			strcpy(buf2, "TIMEOUT on dial");
			goto error;
		}
		if (ret == -1) {
			strcpy(buf2, "ioctl failed");
			goto error;
		}
	} while (strncmp(buf1, RRING, strlen(RRING)) == 0
		&& (!boolean(value(VERBOSE)) || (printf("rrring... "), 1)));
	if (strncmp(buf1, buf2, strlen(buf2)) != 0) {
		for (i = 0, j = 300; tbcon[i] != (char *)0; i++, j *= 2) {
			if (!strncmp(buf1, tbcon[i], strlen(tbcon[i]))) {
				/* This should never fail */
				if ((i = speed(j)) != NULL) {
					BR = j;
					ttysetup(i);
				} else {
					printf("speed(%d) failed\n", j);
					return 0;
				}
				goto ok;
			}
		}
		for (i = 0; notbcon[i] != (char *)0; i++) {
			if (!strncmp(buf1, notbcon[i], strlen(notbcon[i]))){
				char	*bp, *cp;
				for (bp = buf2, cp = notbcon[i]; *cp; cp++) {
					if (*cp != '\r' && *cp != '\n')
						*bp++ = *cp;
				}
				*bp = '\0';
				goto error;
			}
		}
		(void) strcpy(buf2, "UNKNOWN ERROR [");
		(void) strcat(buf2, buf1);
		(void) strcat(buf2, "]");
error:
		printf("dial failed (%s)\n", buf2);
		sprintf(buf3, "dial failed (%s)", buf2);
#ifdef ACULOG
		logent(value(HOST), num, NAME, buf3);
#endif
		return 0;
	}
ok:
	if (boolean(value(VERBOSE)))
		printf("(%d baud) ", BR);
	i = 2;
#ifdef	USG
	if (ioctl(FD, TCFLSH, i) < 0)
		perror("tip: TCFLSH ioctl");
#else	/* USG */
	if (ioctl(FD, TIOCFLUSH, &i) < 0)
		perror("tip: TIOCFLUSH ioctl");
#endif	/* USG */
	return 1;
}


tb_disconnect()
{
#ifdef	USG
	if (ioctl(FD, TCGETA, &termio) < 0)
		perror("tip: TCGETA ioctl");
	termio.c_cflag &= (CBAUD & B0);
	if (ioctl(FD, TCSETA, &termio) < 0)
		perror("tip: TCSETA ioctl");
	sleep(2);
#endif	/* USG */
	(void) close(FD);
}


tb_abort()
{
	tb_disconnect();
}

STATIC
tbwrite(cp)
register char	*cp;
{
	struct timeval t;

	for (; *cp != '\0' ; cp++) {
		if (write(FD, cp, 1) != 1) {
			perror("tip: tbwrite: write error");
			return;
		}
#ifdef	DEBUG
		if (debug) {
			fprintf(stderr, "written '%c'\n", *cp);
		}
#endif
		t.tv_sec = 0;
		t.tv_usec = (20 * 10 * 1000000)/BR;
		(void) select(32, 0, 0, 0, &t);
	}
}

STATIC
tbread(buf, timeout, max)
register char	*buf;
unsigned int timeout, max;
{
	int	alarmtr();
	struct timeval t;
	register char	*rp;
	unsigned int numread, numprev = 0, numpndg = 0;
	unsigned int toread;

#ifdef	DEBUG
	if (debug)
		fprintf(stderr, "\n---------\nentering tbread(): timeout is: %d, max is: %d\n", timeout, max);
#endif
	rp = buf;
	*rp = '\0';
	(void) signal(SIGALRM, alarmtr);
	if (setjmp(jmpbuf)) {
		*rp = '\0';
		return 0;
	}
	(void) alarm(timeout);
		/* wait till modem says something or until timeout */
	while (numpndg == 0 || numpndg != numprev) {
		numprev = numpndg;
		if (ioctl(FD, FIONREAD, &numpndg) == -1) {
			perror("tip: FIONREAD ioctl");
			return -1;
		}
		t.tv_sec = 0;
		t.tv_usec = (100 * 10 * 1000000)/BR;
		(void) select(32, 0, 0, 0, &t);
	}
	(void) alarm(0);
#ifdef	DEBUG
	if (debug)
		fprintf(stderr, "number of chars waiting is: %d\n", numpndg);
#endif
	toread = ((numpndg > max) ? max : numpndg);
  	if ((numread = read(FD, rp, toread)) < toread)
		perror("read error");
#ifdef	DEBUG
	if (debug) {
		int i; char *p;
		fprintf(stderr, "read %d chars:", numread);
		for (i=0, p=rp; i<numread; i++, p++)
			fprintf(stderr, "%0.2x ", *p);
		fprintf(stderr, "\n");
		fflush(stderr);
	}
#endif
	rp += numread;
	*rp = '\0';
#ifdef	DEBUG
	if (debug) {
		fprintf(stderr, "\nbuf contains: ->%s<-\n", buf);
		fprintf(stderr, "leaving tbread()\n-----------\n");
		fflush(stderr);
	}
#endif
	return 1;
}


STATIC
accept(baudrate)
int	baudrate;
{
	int i;

	BR = baudrate;
	if ((i = speed(baudrate)) != NULL)
		ttysetup(i);
}


STATIC
alarmtr()
{
#ifdef	DEBUG
	if (debug)
		fprintf(stderr, "alarm went off!\n");
#endif
	longjmp(jmpbuf, 1);
}

STATIC int
tbset(tosend, buf, num, errmsg)
char	*tosend, *buf, *num, *errmsg;
{
	tbwrite(tosend);
	tbread(buf, CMD_DELAY, (u_int) (sizeof MDM_OK) - 1);
	if (strncmp(buf, MDM_OK, (sizeof MDM_OK) - 1) != 0) {
		printf(" %s modem %s\n", NAME, errmsg);
#ifdef ACULOG
		logent(value(HOST), num, NAME, errmsg);
#endif
		return 0;
	}
	return 1;
}
