/******************************************************************************

	xn_xx.c

	シリアルポート通信処理 

				           (C) 2005.2 Suwa-Koubou
*******************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
#include <termio.h>
#include <errno.h>


/*============================================================================
	グローバル変数宣言
=============================================================================*/

static char RsDevice[256] = "/dev/modem"; /* xn_xx serial device port */
static struct termios Pmode, Omode;    	/* device control string */
static int XNfd = -1;			/* device id */
static int TimeUp;


/*============================================================================
	関数プロトタイプ
=============================================================================*/

void	xn_device(char *device);
int	xn_open(void);
void	xn_close(void);
void	xn_flush( void );
int	xn_get_status(unsigned int *status);
int	xn_set_status(unsigned int *status);
int	xn_set_dtr(int sw);
int	xn_get_cd(void);
int	xn_putc(int ch);
int	xn_puts(char *s, int len);
int	xn_sputs(char *s);
int	xn_getc(void);
int	xn_getc_nb(void);
int	xn_getc_timer(int time_out);
int	xn_rcv_ready(void);
int	xn_gets(char *buff, int len);
void	xn_timer_set(int time_value);
void	xn_timer_reset(void);
int	xn_timer_check(void);
int	xn_gets_timer(char *buff, int len, int time_out);
int	xn_sgets_timer(char *buff, int cp, int time_out);
int	xn_ptrn_gets(char *ptrn[], char *buf, int time_out);
static	void	timer(int sig);


/*============================================================================
	接続デバイスの設定
=============================================================================*/

void	xn_device(char *device)
{
	strcpy(RsDevice, device);
}


/*============================================================================
	シリアルデバイスオープン
=============================================================================*/

int	xn_open(void)
{
	if ((XNfd = open(RsDevice, O_RDWR | O_NOCTTY | O_SYNC /* | O_NDELAY  */)) < 0){
		return -1;
	}

	tcgetattr( XNfd, &Omode );
	Pmode = Omode;
	
	bzero(&Pmode, sizeof(Pmode));

	Pmode.c_cflag |= B115200;
	Pmode.c_cflag |= CS8 | CREAD ;
	Pmode.c_cflag |= CRTSCTS;
	Pmode.c_cflag |= HUPCL;
	Pmode.c_iflag |= IGNBRK | IGNPAR ; 

	if(tcsetattr( XNfd, TCSANOW, &Pmode) == -1){
		return -1;
	}

	if(tcflush( XNfd, TCIFLUSH ) == -1){
		xn_close();
		return -1;
	}

	xn_timer_reset();

	return 0;

}


/*============================================================================
	シリアルデバイスクローズ
=============================================================================*/

void	xn_close(void)
{
	if(XNfd != -1){
		tcsetattr( XNfd, TCSANOW, &Omode);
		close( XNfd );
	}

	XNfd = -1;
}


/*============================================================================
	バッファフラッシュ
=============================================================================*/

void	xn_flush( void )
{
	int c;

	if(XNfd == -1)
		return;
	
	tcflush( XNfd, TCIFLUSH );

}


/*============================================================================
	制御線の情報を取得する
=============================================================================*/

int	xn_get_status(unsigned int *status)
{

	if(XNfd == -1) return -1;

	if(ioctl(XNfd, TIOCMGET, status) == -1)
		return -1;
	return 0;

}


/*============================================================================
	ＣＤの情報を取得する
=============================================================================*/

int	xn_get_cd(void)
{
	unsigned int status;

	if(xn_get_status(&status) == -1) return 0;

	if(status & TIOCM_CAR)
		return 1;
	else
		return 0;

}


/*============================================================================
	制御線を設定する
=============================================================================*/

int	xn_set_status(unsigned int *status)
{

	if(XNfd == -1) return -1;

	if(ioctl(XNfd, TIOCMSET, status) == -1)
		return -1;
	return 0;

}


/*============================================================================
	ＤＴＲをオンオフする
	swが 非0 ならオン、0ならオフにする
=============================================================================*/

int	xn_set_dtr(int sw)
{
	unsigned int status;

	if(xn_get_status(&status) == -1) return -1;
	if(sw){
		status |= TIOCM_DTR;
	}else{
		status &= ~TIOCM_DTR;
	}

	if(xn_set_status(&status) == -1) return -1;

	return 0;
}



/*============================================================================
	一文字送信
	成功なら１，失敗なら−１を返す
=============================================================================*/

int	xn_snd_ready(void)
{
	fd_set w_bit;
	struct timeval tv;
	int    n;


	/* fidクリア */
	FD_ZERO( &w_bit );

	/* XN-xx 通信ポートidセット */		
	FD_SET( XNfd, &w_bit );

	/* 待ち時間セット */
	tv.tv_sec = 10;
	tv.tv_usec = 500000L; /* 0.5 Sec */

	/* 送信待ち */
	if((n = select(FD_SETSIZE, NULL, &w_bit, NULL, &tv)) == -1){
		if(errno == EINTR){
			return 0;
		}
	}

	if(n == 0) return 0;

	if( FD_ISSET(XNfd, &w_bit)){
		return 1;
	}
	
	return 0;

}

int	xn_putc(int ch)
{
	char c;

	c = (ch & 0xff);
	
	while(xn_snd_ready() == 0)
		;
	
	if(write(XNfd, &c, 1) != 1){
		return -1;
	}

	return 1;

}


/*============================================================================
	指定長さの文字列送信（バイナリＯＫ）
	成功なら送信した文字数(=len)を返す。
	失敗なら-1を返す。
=============================================================================*/

int	xn_puts(char *s, int len)
{
	while(xn_snd_ready() == 0)
		;

	return write(XNfd, s, len);


/****
	int	count;


	for(count = 0; count < len; count++, s++){
		if(xn_putc(*s) != 1)
			return -1;
	}
	return count;
*****/
}

int	xn_sputs(char *s)
{
	return xn_puts(s, strlen(s));
}


/*============================================================================
	一文字受信(none block mode)
	受信文字がなければ−１，あればその文字を返す
=============================================================================*/

int	xn_getc_nb(void)
{
	char	c;

	/*
	 * 受信文字が無ければ直ちに -1 で戻る
	 */
	if(read(XNfd, &c, 1) < 1)
		return(-1);

	return((int)c & 0xff);
}

/*============================================================================
	一文字受信
	受信文字がなければ受信するまでまつ。
=============================================================================*/

int	xn_getc(void)
{
	fd_set r_bit;
	int    c;


	/* fidクリア */
	FD_ZERO( &r_bit );

	/* XN-xx 通信ポートidセット */		
	FD_SET( XNfd, &r_bit );

	/* 受信文字待ち */
	if(select(FD_SETSIZE, &r_bit, NULL, NULL, NULL) == -1){
		if(errno == EINTR){
			return -1;	/* 受信タイムアウト発生 */
		}
	}

	/* 受信文字あり？ */
	if( FD_ISSET(XNfd, &r_bit)){
		if(read(XNfd, &c, 1) != 1){
			return -1;
		}
		return ((int)c & 0xff);
	}

	return -1;

}


/*============================================================================
	一文字受信
	受信文字がなければ time_out で指定した秒数
	受信するまでまつ。受信できなければ -1 を返す。
=============================================================================*/

int	xn_getc_timer(int time_out)
{
	fd_set r_bit;
	struct timeval tv;
	int    c, n;


	/* fidクリア */
	FD_ZERO( &r_bit );

	/* XN-xx 通信ポートidセット */		
	FD_SET( XNfd, &r_bit );

	/* タイマーセット */
	tv.tv_usec = 0;
	tv.tv_sec = time_out;

	/* 受信文字待ち */
	if((n = select(FD_SETSIZE, &r_bit, NULL, NULL, &tv)) == -1){
		if(errno == EINTR){
			return -1;	/* 受信タイムアウト発生 */
		}
	}

	if(n == 0) return -1;

	/* 受信文字あり？ */
	if( FD_ISSET(XNfd, &r_bit)){
		if(read(XNfd, &c, 1) != 1){
			return -1;
		}
		return ((int)c & 0xff);
	}

	return -1;

}


/*============================================================================
	受信文字があるか確認する。受信がなければ、500msec まで
	受信待ちを行う。読み出す文字が存在すれば 1 , 受信がな
	ければ 0 を返す。
=============================================================================*/

int	xn_rcv_ready(void)
{
	fd_set r_bit;
	struct timeval tv;
	int    n;


	/* fidクリア */
	FD_ZERO( &r_bit );

	/* XN-xx 通信ポートidセット */		
	FD_SET( XNfd, &r_bit );

	/* 待ち時間セット */
	tv.tv_sec = 0;
	tv.tv_usec = 500000L; /* 0.5 Sec */

	/* 受信文字待ち */
	if((n = select(FD_SETSIZE, &r_bit, NULL, NULL, &tv)) == -1){
		if(errno == EINTR){
			return 0;
		}
	}

	if(n == 0) return 0;

	if( FD_ISSET(XNfd, &r_bit)){
		return 1;
	}
	
	return 0;

}


/*============================================================================
	指定長さの文字列を受信（バイナリも有り）
	受信出来た文字数を返す
=============================================================================*/

int	xn_gets(char *buff, int len)
{
	int	c;	
	int	count;


	/*
	 * 受信文字が無くなるまで，または指定の文字数まで読み続ける
	 */
	count = 0;
	while(count > len){
		if((c = xn_getc_nb()) == -1){
			break;
		}else{
			buff[count++] = c;
		}
	}

	return count;
}


/*=============================================================================
	タイマー
	受信監視用として１本だけのタイマーを提供
=============================================================================*/

static
void	timer(int sig)
{
	TimeUp = 1;
	signal(SIGALRM, timer);
}


void	xn_timer_set(int time_value)
{
	signal(SIGALRM, timer);
	TimeUp = 0;
	alarm(time_value);
}


void	xn_timer_reset(void)
{
	alarm(0);
	TimeUp = 0;
}

int	xn_timer_check(void)
{
	return TimeUp;
}


/*============================================================================
	指定長さの文字列を受信（バイナリも有り）
	受信出来た文字数を返す
	受信文字数が指定した長さに満たない時，指定した時間まで
　　　　受信待ちする
=============================================================================*/

int	xn_gets_timer(char *buff, int len, int time_out)
{
	int	c;	
	int	count;

	xn_timer_set(time_out);

	count = 0;
	while(count < len && TimeUp == 0){
		if((c = xn_getc()) != -1)
			buff[count++] = c;
	}

	xn_timer_reset();

	return count;
}


/*============================================================================
	指定文字が出現するまでの文字列を受信（バイナリも有り）
	受信出来た文字数を返す
	指定文字が出現するまで指定した時間受信待ちする
	引数 time_out の単位は秒とする
	タイムアウト時は，-1を返す
	(注)1000文字以上は受信しないようにする
=============================================================================*/

int	xn_sgets_timer(char *buff, int cp, int time_out)
{
	int	c;	
	int	count;
	xn_timer_set(time_out);

	count = 0;

	do{
		if( (c = xn_getc()) != -1){
			buff[count++] = c;
		}

	}while( c != cp && TimeUp == 0 && count < 1000);

	buff[count] = '\0'; /* set EOS (null char) */


	if(TimeUp){
		TimeUp = 0;
		return -1;
	}

	if(TimeUp == 0){
		alarm(0);
	}

	if(count >= 1000){
		return -1;
	}

	return count;
}

/*=============================================================================
	char *ptrn[] 配列で示す文字列の受信待ちを行う。
	time_out で指定する秒数の時間待ち行い、ptrn 配列で指定
	するいずれの文字列も受信出来なかった場合には -1 を返す。
	受信できた時は、受信文字列を要素とするptrn 配列のイン
	デックスを返す。
=============================================================================*/

int	xn_ptrn_gets(char *ptrn[], char *buff, int time_out)
{
	int	c, p;
	int	count;

	xn_timer_set(time_out);

	count = 0;
	buff[0] = 0;
	while(TimeUp == 0){
		if((c = xn_getc()) != -1){
			buff[count++] = c;
			buff[count] = '\0'; /* set EOS(null) */
 printf("%s\n", buff);
			for(p = 0; ptrn[p] != NULL; p++){
				if(strstr(buff, ptrn[p]) != NULL){
					xn_timer_reset();
					return p;
				}
			}
		}
	}

	xn_timer_reset();

	return -1;
}

/**** End of xn_xx.c *********************************************************/
