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

	block.c

	ブロック転送プロトコル

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

#include	<stdio.h>
#include	<stdlib.h>
#include	<string.h>
#include	<ctype.h>

#include	"xn_xx.h"


#define		ON		1
#define		OFF		0


#define		LONGSIZE	1024
#define		BLOCKSIZE	(LONGSIZE+5)
#define		TIMEOUT		10

#define		SOH		0x1
#define		STX		0x2
#define		ETX		0x3
#define		EOT		0x4
#define		ENQ		0x5
#define		ACK		0x6
#define		LF		0xa
#define		CR		0xd
#define		NAK		0x15
#define		CAN		0x18
#define		ESC		0x1b


/*-------------------------------------------------------------------*/
void	block_mode(int sw);
void	rcvack(void);
void	senddata(size_t size, unsigned char *data);
void	sendblock(unsigned char head, size_t size, unsigned char *data);
size_t	getdata(char **data);
static int		xn_tmgetc(void);
static unsigned short	crc_calc(unsigned short crc, unsigned short c);

extern	int	dbg;

/*---------------------------------------------------------------
	ブロック転送モードへの移行、戻し
---------------------------------------------------------------*/

void	block_mode(int sw)
{
	xn_putc(sw == ON ? ENQ: EOT);
	if(dbg == 2) printf("%02X\n", sw == ON ? ENQ: EOT);
	rcvack();

}


/*---------------------------------------------------------------
	ACK受信　受信できなかった時は強制終了
---------------------------------------------------------------*/

void	rcvack(void)
{
	int	c;

	c = xn_getc_timer(TIMEOUT);
	if(dbg == 2) printf("%02X\n", c);
	if(c == ACK)
		return;

	if(c == -1){
		printf("wait for ACK, time out.\n");
	}else{
		printf("received code 0x%02x, instead ACK.\n", c); 
	}
	xn_close();
	exit(1);

}


/*---------------------------------------------------------------
	データのブロック送信
---------------------------------------------------------------*/

void	senddata(size_t size, unsigned char *data)
{

	while (size > LONGSIZE){
		sendblock(STX,LONGSIZE,data);	
		data += LONGSIZE;
		size -= LONGSIZE;
	}
	sendblock(ETX,size,data);
	rcvack();

}


void	sendblock(unsigned char head, size_t size, unsigned char *data)
{
	unsigned short crc;
	size_t	count;
	unsigned char	buf[BLOCKSIZE], *p;

	p = buf;
	*p++ = head;
	*p++ = ((size >> 8) & 0xff);
	*p++ = (size & 0xff);
	crc = 0;
	for(count = 0; count < size; count++){
		*p++ = *data;
		crc = crc_calc(crc, *data++);
	}
	*p++ = ((crc >> 8) & 0xff);
	*p++ = (crc & 0xff);

	xn_puts(buf, size + 5);	

	if(dbg == 2){
		for(p = buf, count = 0; count < size+5; count++){
			printf("%02X", *p++);
		}
		printf("\n");
	}
}	


/*---------------------------------------------------------------
	データのブロック受信
---------------------------------------------------------------*/

size_t	getdata(char **data)
{
	char *buf;
	char head;
	size_t	size, sz, count;
	int	c;

	sz = 0;
	*data = (char *)malloc(1);

	do{
		head = xn_tmgetc();
		c = xn_tmgetc();
		size = (c << 8) & 0xff00;
		c = xn_tmgetc();
		size =  size | (c & 0xff);

		*data =(char *) realloc(*data, sz + size +1);
		buf = *data + sz;
		
		for(count = 0; count < size; count++){
			c = xn_tmgetc();
			*buf++ = c;		
		}

		/* rcv crc, skip crc calcurate compare */
		xn_tmgetc();
		xn_tmgetc();

		sz += size;

	}while( head == STX );
	
	*buf = '\0'; 	/* null terminate */
	if(dbg == 2) printf("\n");

	xn_putc(ACK);	/* send ACK */ 
	if(dbg == 2) printf("%02X\n", ACK);
	return sz;
}

static int		xn_tmgetc(void)
{
	int	c;

	c = xn_getc_timer(TIMEOUT);
	if(dbg == 2) printf("%02X", c);
	if(c != -1) return c & 0xff;

	printf("rxchar received timeout.\n");
	xn_close();
	exit(1);
}


/*---------------------------------------------------------------
	CRCの計算
---------------------------------------------------------------*/

static unsigned short	crc_calc(unsigned short crc, unsigned short c)
{
	int i;

	crc ^= (c << 8);
	for(i = 8; i > 0; i--) {
		if(crc & 0x8000) {
			crc = (crc << 1) ^ 0x1021;
		}else{
			crc = (crc << 1);
		}
	}
	return(crc);
}

