
/*============================================================================
 Name        : serial.c
 Author      :
 Date  	     : 2015.9
 Copyright   : ZKSoftware Inc.
 Description : Linux C
 ============================================================================*/
#include "serial.h"
#include "libtype.h"
#include "command_fun.h"
#include "libuserCmd.h"
#include "main.h"

static int fdRS232 = -1;

static int MapBaudrate[V10_NUM_BAUDRATES]={
	B0, B50, B75, B110, B134, B150, B200, B300, B600, B1200, B1800, 
	B2400, B4800, B9600, B19200, B38400, B57600, B115200};

/** This array is used to map the size of the data byte to the value of the
 * target system. The library assume, that we allways have 5-8 bit per byte.
 */
static int MapDatasize[V10_NUM_DATASIZES]={CS5, CS6, CS7, CS8};

int ConvertBaudrate(int baudrate)
{   
	if (baudrate <= 1200)   return V10_B1200;
	if (baudrate >= 115200) return V10_B115200;
	if (baudrate == 1800)   return V10_B1800;    
	if (baudrate == 2400)   return V10_B2400;
	if (baudrate == 4800)   return V10_B4800;
	if (baudrate == 9600)   return V10_B9600;
	if (baudrate == 19200)  return V10_B19200;
	if (baudrate == 38400)  return V10_B38400;
	if (baudrate == 57600)  return V10_B57600;

	return V10_B9600;
}

int RS232_SetParameters(int port, int Baudrate, int Datasize, int Parity, int FlowControl)
{
	struct termios options;

	if ((Baudrate < 0) || (Baudrate>=V10_NUM_BAUDRATES)) return 0;    
	if ((Datasize<0) || (Datasize>=V10_NUM_DATASIZES))	return 0;

	//Get the current options for the port...
	tcgetattr(port, &options);

	/* General Setup: the library is design for `raw mode'. Only 1 stop can be
	 * configured. There's no special `break handling'. The input stream
	 * shouldn't be modified. Therefore parity error aren't marked and the
	 * input mapping is disabled.
	 */
	cfmakeraw(&options);

	/* handle the handshaking according to the open flags */
	if (FlowControl)
	{
		options.c_cflag |= CRTSCTS;
	}
	else
	{
		options.c_cflag &= ~CRTSCTS;
	}

	options.c_cflag &= ~HUPCL;

	options.c_iflag &= ~(IXON|IXOFF|IXANY);

	/* Decide wether to block while waiting for character or return immediatly.
	 */
	options.c_cc[VMIN]  = 0;
	options.c_cc[VTIME] = 50;

	/* Mask the character size bits and set data bits according to the
	 * parameter.
	 */
	options.c_cflag &= ~CSIZE;
	options.c_cflag |= MapDatasize[Datasize]; 

	/* Set the handling of the parity bit.
	 */
	switch (Parity)
	{
		case V10_NONE:			     /* disable parity bit */
			options.c_cflag &= ~PARENB;
			options.c_iflag &= ~INPCK;
			break;
		case V10_EVEN:			     /* even parity */
			options.c_cflag |= PARENB;
			options.c_cflag &= ~PARODD;
			options.c_iflag &= ~IGNPAR;
			options.c_iflag |= INPCK;	     /* removed "|ISTRIP" */
			break;
		case V10_ODD:			     /* odd parity */
			options.c_cflag |= PARENB;
			options.c_cflag |= PARODD;
			options.c_iflag &= ~IGNPAR;
			options.c_iflag |= INPCK;	     /* removed "|ISTRIP" */
			break;
		case V10_IGNORE:		     /* use parity but dont test */
			options.c_cflag |= PARENB;
			options.c_iflag |= IGNPAR;
			break;
		default:
			return 0;
	}

	/* We have to enable the receiver and set the port to local mode.
	 */
	options.c_cflag |= (CLOCAL|CREAD);

	/* Set the baud rates according to the parameter.*/
	cfsetispeed(&options, MapBaudrate[Baudrate]);
	cfsetospeed(&options, MapBaudrate[Baudrate]);

	/* At last we must set the new options for the port.
	 */
	tcsetattr(port, TCSANOW, &options);

	return 1;
}

int RS232_SetStopbits (int port, int Stops)
{
	struct termios options;

	/* Get the current options for the port... */
	if (Stops == 1)
	{
		tcgetattr(port, &options);
		options.c_cflag &= ~CSTOPB;
		tcsetattr(port, TCSANOW, &options);
	}
	else if (Stops == 2)
	{
		tcgetattr(port, &options);
		options.c_cflag |= CSTOPB;
		tcsetattr(port, TCSANOW, &options);
	}
	else
	{
		return 0;
	}
		
	return 1;
}

int RS232_SetTimeouts (int port, int TenthOfSeconds)
{
	struct termios options;

	if ( TenthOfSeconds <= 0 ) 
	{
		return 0;
	}

	tcgetattr(port, &options);
	options.c_cc[VMIN] = 0;		     /* we want `interchar timeouts' */
	options.c_cc[VTIME] = TenthOfSeconds;
	tcsetattr(port, TCSANOW, &options);

	return 1;
}

// Initialise serial port at the request baudrate. 
static int arca_serial_init(int BaudRate, int DataSize, int Parity, int FlowControl, char *dev)
{   
	if (fdRS232 > 0) 
	{
		close(fdRS232);
	}

	if (dev) 
	{
		fdRS232 = open(dev, O_RDWR | O_NOCTTY);
	} 
	else 
	{
		fdRS232 = open("/dev/ttyS0", O_RDWR | O_NOCTTY);
	}
	
	if ( fdRS232 < 0) 
	{
		mvwprintw(win1, 1, 1, "open fdRS232 failed, error:%d", fdRS232);
		return -1;
	}

	mvwprintw(win1, 1, 1, "comm parameters:%d,%d,%d,%d", BaudRate, DataSize, Parity, FlowControl);
	RS232_SetParameters(fdRS232, ConvertBaudrate(BaudRate), DataSize, Parity, FlowControl);
	RS232_SetStopbits(fdRS232, 1);

	return 0;
}

/* Flush serial input queue. 
 * Returns 0 on success or negative error number otherwise
 */
static int arca_serial_flush_input(void)
{    
	tcflush(fdRS232, TCIFLUSH);
	return 0;
}

/* Flush output queue. 
 * Returns 0 on success or negative error number otherwise
 */
static int arca_serial_flush_output(void)
{
	tcflush(fdRS232, TCOFLUSH);
	return 0;
}

/* Check if there is a character available to read. 
 * Returns 1 if there is a character available, 0 if not, 
 */
static int arca_serial_poll(void)
{
	int CharsWaiting = 0;                                                                                                            
	ioctl(fdRS232, FIONREAD, &CharsWaiting);

	if (CharsWaiting < 0) 
	{
		fprintf(Fp, "CharsWaiting=%d,",CharsWaiting);
		CharsWaiting = 0;
	}


	return CharsWaiting;   
}

/* read one character from the serial port. return character (between
 * 0 and 255) on success, or negative error number on failure. this
 * function is not blocking */
static int arca_serial_read(void)
{
	unsigned char TheData;

	if (read(fdRS232, &TheData, 1) != 1)
	{
		return -1;
	}
	return (int)TheData;
}

/* write character to serial port. return 0 on success, or negative
 * error number on failure. this function is blocking
 */
static int arca_serial_write(int c)
{
	unsigned char TheData;

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

	TheData = c&0xff;

	if (write(fdRS232, &TheData, 1) != 1)
	{
		return -1;
	}
	else
	{
		fsync(fdRS232);
	}
	
	//waits until all output written has been transmitted
	tcdrain(fdRS232);

	return 0;
}

static int arca_serial_tcdrain(void)
{
	return tcdrain(fdRS232);
}

static void arca_serial_free(void)
{
	if (fdRS232 > 0) 
	{
		close(fdRS232);
	}
}

static int arca_serial_get_lsr(void)
{
#if 0
	int i=0,result=0;
	if(fdRS232 > 0)
	{
		i=ioctl(fdRS232,TIOCSERGETLSR,&result);
		return result;
	}
	return TIOCSER_TEMT;
#endif
	return 0;
}

/* write buffer to serial port. return Size on success, or negative
 * error number on failure. this function is blocking
 */
static int arca_serial_write_buf(unsigned char* Data,int Size)
{
	if (write(fdRS232, Data, Size) != Size)
	{
		return -1;
	}
	else
	{
		fsync(fdRS232);
	}

	return Size;
}

/* read characters from the serial port. return characters length on success(>0), 
 * or negative error number on failure. this
 * function is not blocking */
static int arca_serial_read_buf(unsigned char* Data,int Size)
{          
	return read(fdRS232, Data, Size);
}

/* export serial driver */
serial_driver_t ff232 = {
	arca_serial_init,
	arca_serial_read,
	arca_serial_write,
	arca_serial_poll,
	arca_serial_flush_input,
	arca_serial_flush_output,
	arca_serial_tcdrain,
	arca_serial_free,
	arca_serial_get_lsr,
	arca_serial_write_buf,
	arca_serial_read_buf
};
