/*
 *  tty.c -- RS-232C 入出力関連
 *
 *  revision history:
 *	0.0: May  19, 1996 by Dai ISHIJIMA
 *	0.1: Jul. 27, 1996
 *	0.2: Aug. 16, 1996
 *	0.3: Aug. 24, 1996
 */

#include <stdio.h>
#include <fcntl.h>
#include <termios.h>
#include <sys/types.h>
#include <time.h>

#ifndef O_NONBLOCK	/* for Sony NEWS, NEWS-OS 4.x */
#include <stream/xti.h>
#endif

#define FILEMODE 0777
#define TIMEOUT 15

#ifdef _POSIX_VDISABLE
#define CC_UNDEF _POSIX_VDISABLE
#else
#define CC_UNDEF 0
#endif

static struct termios t;
static struct termios new;


/* 読み書き用にオープンする */
int opentty(char *name)
{
    int fd;

    if ((fd = open(name, O_RDWR | O_NONBLOCK, FILEMODE)) <= 0) {
	fprintf(stderr, "can't open %s\n", name);
	exit(1);
    }
    return(fd);
}


/* シリアル速度のチェック */
int speed(int sp)
{
    if (sp == 2400) {
	return(B2400);
    }
    else if (sp == 4800) {
	return(B4800);
    }
    else if (sp == 9600) {
	return(B9600);
    }
    else if (sp == 19200) {
	return(B19200);
    }
    else if (sp == 38400) {
	return(B38400);
    }
#ifdef B57600    
    else if (sp == 57600) {
	return(B57600);
    }
#endif
#ifdef B115200
    else if (sp == 115200) {
	return(B115200);
    }
#endif
    else {
	fprintf(stderr, "%d bps unavailable.  9600 bps selected.\n", sp);
    }
    return(B9600);
}


/* シリアルポートの初期化。速度の設定・RAWモードに */
void ttyinit(int fd, int sp)
{
    int i;

    sp = speed(sp);
    tcgetattr(fd, &t);
    memcpy(&new, &t, sizeof(struct termios));
    new.c_iflag = IGNBRK | IGNPAR;
    new.c_oflag = 0;
    new.c_cflag = CS8 | CREAD | CLOCAL;
    new.c_lflag = 0;
    cfsetispeed(&new, sp);
    cfsetospeed(&new, sp);
    for (i = 0; i < NCCS; i++) {
	new.c_cc[i] = CC_UNDEF;
    }
    new.c_cc[VMIN] = t.c_cc[VMIN];
    new.c_cc[VTIME] = t.c_cc[VTIME];
    tcsetattr(fd, TCSANOW, &new);
}    


/* シリアルポートの設定を元に戻す */
void ttyrestore(int fd)
{
    tcsetattr(fd, TCSANOW, &t);
}


/* 文字を送信する */
int ttyputc(int fd, char ch)
{
    int status;
    time_t t;

    t = time(NULL);
    while ((status = write(fd, &ch, 1)) < 1) {
#if 0	
	if (status < 0) {
	    fprintf(stderr, "ttyputc(): write failed\n");
	    return(EOF);
	}
#endif
	if (time(NULL) - t > TIMEOUT) {
	    fprintf(stderr, "ttyputc(): timeout\n");
	    return(EOF);
	}
    }
    return(ch);
}


/* 文字を受信する。受信バッファが空のときは EOF を送る */
int ttygetc(int fd)
{
    unsigned char buf[1];
    
    if (read(fd, &buf[0], 1) != 1) {
	return(EOF);
    }
    return(buf[0]);
}

#ifdef MAIN
/* MAINが定義されているときは、上の関数をチェックするプログラムを生成する */

/*
 *  peeping-com -- Lady Godiva をシリアルポートからのぞき見する :-)
 *
 *  このプログラムは COM1: (/dev/ttyd0) から受信した文字を
 *  COM2: (/dev/ttyd1) に、COM2: からの文字を COM1: に再送
 *  します。RS-232C でやりとりされるデータの解析に最適 (?) です。
 *
 *  コンパイル方法: gcc -DMAIN -o peeping-com tty.c
 *
 *  MAINを定義してコンパイルしたプログラムを使う上での制限事項:
 *     ・「ハッカー (hacker)」を、犯罪者の意味で用いる人が
 *       このプログラムを利用することを禁止します。
 *     ・不可使用悪事 (わるいことにつかうべからず :-)
 *     ・不可使用金儲 (かねもうけにつかうべからず :-)
 */

#define SPEED 9600		/* シリアルの速度 */
#define TTY0 "/dev/ttyd0"	/* COM1: のデバイスファイル */
#define TTY1 "/dev/ttyd1"	/* COM2: のデバイスファイル */
#define ONE2ZERO 1
#define ZERO2ONE 0
#define MAXQ 4096
#define NDUMP 16
#define DUMPTIMEOUT 5

static int nq = 0;
static unsigned char queue[MAXQ];

#define dumpchar(ch) (((' ' <= (ch)) && ((ch) <= '~')) ? (ch) : '.')

flushqueue(int dir)
{
    int i, j;

    for (i = 0; i < nq; i += NDUMP) {
	if (dir == ZERO2ONE) {
	    printf("0->1: ");
	}
	else if (dir == ONE2ZERO) {
	    printf("1->0: ");
	}
	for (j = 0; (j < NDUMP) && (i + j < nq); j++) {
	    printf("%02x ", queue[i + j]);
	    if (j == 7) {
		putchar(' ');
	    }
	}
	for (; j < NDUMP; j++) {
	    printf("   ");
	    if (j == 7) {
		putchar(' ');
	    }
	}
	putchar(' ');
	for (j = 0; (j < NDUMP) && (i + j < nq); j++) {
	    putchar(dumpchar(queue[i + j]));
	}
	putchar('\n');
	fflush(stdout);
    }
    nq = 0;
}


enqueue(unsigned char ch, int dir)
{
    if (nq >= MAXQ) {
	flushqueue(dir);
    }
    queue[nq] = ch;
    ++nq;
}


main()
{
    int ch;
    int fd0, fd1;
    int dir;
    time_t t;

    fd0 = opentty(TTY0);
    ttyinit(fd0, SPEED);
    fd1 = opentty(TTY1);
    ttyinit(fd1, SPEED);
    dir = -1;
    t = time(NULL);
    while (1) {
	if ((ch = ttygetc(fd0)) >= 0) {
	    t = time(NULL);
	    ttyputc(fd1, ch);
	    if (dir != ZERO2ONE) {
		flushqueue(dir);
	    }
	    dir = ZERO2ONE;
	    enqueue(ch, dir);
	}
	if ((ch = ttygetc(fd1)) >= 0) {
	    t = time(NULL);
	    ttyputc(fd0, ch);
	    if (dir != ONE2ZERO) {
		flushqueue(dir);
	    }
	    dir = ONE2ZERO;
	    enqueue(ch, dir);
	}
	if (time(NULL) - t > DUMPTIMEOUT) {
	    flushqueue(dir);
	    t = time(NULL);
	}
    }
    close(fd0); close(fd1);
}

/* Local Variables: */
/* compile-command:"cc -DMAIN -o peeping-com tty.c" */
/* End: */
#endif
