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

#include "Serial.h"

#define DBG_SERIAL 0

extern int verbosity;

extern volatile sig_atomic_t bRunning;

int serial_fd = 0;

//static struct termios options;       //place for settings for serial port
char buf[255];                       //buffer for where data is put


char serialDev[64];
/**************serial open *******************/

/***********************************************************
* 名    称：int set_Parity(int fd,int databits,int stopbits,int parity)
* 功    能：串口属性
* 入口参数：无 
* 出口参数：无
* 说    明： 					 
**********************************************************/ 
/**
*@brief   设置串口数据位，停止位和效验位
*@param  fd     类型  int  打开的串口文件句柄*
*@param  databits 类型  int 数据位   取值 为 7 或者8*
*@param  stopbits 类型  int 停止位   取值为 1 或者2*
*@param  parity  类型  int  效验类型 取值为N,E,O,,S
*/
int set_Parity(int fd,int databits,int stopbits,int parity)
{
	struct termios options;
	
 	if  ( tcgetattr( fd,&options)  !=  0)
  	{
	  	perror("SetupSerial 1");
	  	return(0);
  	}

 	
  	options.c_cflag &= ~CSIZE;
	
  	switch (databits) /*设置数据位数*/
  	{
	  	case 7:
	  		options.c_cflag |= CS7;
	  		break;
	  	case 8:
			options.c_cflag |= CS8;
			break;
		default:
			fprintf(stderr,"Unsupported data size\n");
			return (0);
	}
	
  	switch (parity)
  	{
	  	case 'n':
		case 'N':
			options.c_cflag &= ~PARENB;   /* Clear parity enable */
			options.c_iflag &= ~INPCK;     /* Enable parity checking */
			break;
		case 'o':
		case 'O':
			options.c_cflag |= (PARODD | PARENB);  /* 设置为奇效验*/ 
			options.c_iflag |= INPCK;             /* Disnable parity checking */
			break;
		case 'e':
		case 'E':
			options.c_cflag |= PARENB;     /* Enable parity */
			options.c_cflag &= ~PARODD;   /* 转换为偶效验*/  
			options.c_iflag |= INPCK;       /* Disnable parity checking */
			break;
		case 'S':
		case 's':  /*as no parity*/
			options.c_cflag &= ~PARENB;
			options.c_cflag &= ~CSTOPB;
			break;
		default:
			fprintf(stderr,"Unsupported parity\n");
			return (0);
	}
  /* 设置停止位*/   
  
  	switch (stopbits)
  	{
	  	case 1:
	  		options.c_cflag &= ~CSTOPB;
			break;
		case 2:
			options.c_cflag |= CSTOPB;
			break;
		default:
			fprintf(stderr,"Unsupported stop bits\n");
			return (0);
	}
  /* Set input parity option */

	if ((parity != 'n') && (parity != 'N'))
    	{
        	options.c_iflag |= INPCK;
    	}

	options.c_lflag  &= ~(ICANON | ECHO | ECHOE | ISIG);
    	options.c_oflag  &= ~OPOST;
    	options.c_cflag |= (CLOCAL | CREAD);

    	options.c_oflag &= ~(ONLCR | OCRNL);
    	options.c_iflag &= ~(ICRNL | INLCR);
    	options.c_iflag &= ~(IXON | IXOFF | IXANY);

	options.c_cc[VTIME] = 30; // 30 seconds
	options.c_cc[VMIN] =  0;//15
  

  	tcflush(fd,TCIFLUSH); /* Update the options and do it NOW */
  	if (tcsetattr(fd,TCSANOW,&options) != 0)
  	{
  		perror("SetupSerial 3");
		return (0);
	}
	
  	return (1);
 }

/***********************************************************
* 名    称：set_speed(int fd, int speed)
* 功    能：串口波特率设置
* 入口参数：
* 出口参数：
* 说    明： 					 
**********************************************************/  

int speed_arr[] = { B115200,B38400, B19200, B9600, B4800, B2400, B1200, B300,\
	    B38400, B19200, B9600, B4800, B2400, B1200, B300, };
int name_arr[] = {115200,38400, 19200,  9600,  4800,  2400,  1200,  300,\
	    38400,  19200,  9600, 4800, 2400, 1200,  300, };


teSerial_Status set_speed(int fd, int speed)
{
  printf("set_speed\n");
  int   i;
  int   status;
  struct termios   Opt;
  tcgetattr(fd, &Opt);
  for ( i= 0;  i < sizeof(speed_arr) / sizeof(int);  i++)
   {
        printf("input:%d,speed:%d,i = %d\n",speed,name_arr[i],i);
   	if  (speed == name_arr[i])
   	{

	    	SERIAL_LOG_ERR("speed:%d,i = %d\n",name_arr[i],i);
                printf("speed:%d,i = %d\n",name_arr[i],i);
   	    	tcflush(fd, TCIOFLUSH);
	    	cfsetispeed(&Opt, speed_arr[i]);
	    	cfsetospeed(&Opt, speed_arr[i]);
	    	status = tcsetattr(fd, TCSANOW, &Opt);
	    	if  (status != 0)
            {
                    perror("tcsetattr fd1");
                    return E_ERROR;
                }
                tcflush(fd,TCIOFLUSH);
                return E_SERIAL_OK;
            }
   	
   }

  return E_ERROR;
}

/***********************************************************
* 名    称：int devOpen(char *dev)
* 功    能：打开串口设备
* 入口参数： 
* 出口参数：
* 说    明： 					 
**********************************************************/
 
int devOpen(char *dev)
{
	 int fd;
   	 fd = open(dev, O_RDWR | O_NOCTTY | O_NDELAY);   
    
   	 if(fd == -1)
        {
	 	//SERIAL_LOG_ERR("devOpen %s\n ",dev);
         	//perror("devOpen");
	 	return -1;
    	}
   
    if (fd>0)
    {
        set_speed(fd,SERIAL_BAUD);
    }  
	else
	{
		    SERIAL_LOG_ERR("Can't Open Serial Port!\n");
		    return -1;
	}
	if (set_Parity(fd,8,1,'N')== 0)
	{
	        SERIAL_LOG_ERR("Set Parity Error\n");
		return -1;
	} 
    	return fd;
}




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

/***********************************************************
* 名    称：eSerial_start()
* 功    能：打开串口
* 入口参数： 
* 出口参数：
* 说    明： 					 
**********************************************************/  

void eSerial_start()
{
        #if 0
        int ret = eSerial_Init(SERIAL_NAME, SERIAL_BAUD, &serial_fd);

        if(E_SERIAL_OK != ret)
        {
                SERIAL_LOG_ERR("Open serial faild\n");
        }
        #endif

        serial_fd = devOpen(serialDev);
		printf("------------------------------------>serial_fd=%d\n",serial_fd);
}

/***********************************************************
* 名    称：eSerial_stop()
* 功    能：停止串口
* 入口参数： 
* 出口参数：
* 说    明： 					 
**********************************************************/  

void eSerial_stop()
{
        if(serial_fd>0)
        {
                close(serial_fd);
                serial_fd = 0;
                SERIAL_LOG_DBG("close serial_fd ok\n");
        }
}

/***********************************************************
* 名    称：eSerial_Read(unsigned char *data)
* 功    能：读串口（单个字节）
* 入口参数： 
* 出口参数：
* 说    明： 					 
**********************************************************/  

teSerial_Status eSerial_Read(unsigned char *data)
{
    signed char res;

    if(serial_fd <= 0)
    {
        return E_SERIAL_FD_ERROR;
    }

    res = read(serial_fd,data,1);
    if (res > 0)
    {
        //SERIAL_LOG_DBG("RX 0x%02x\n", *data);
        return E_SERIAL_OK;
    }
    else
    {
        printf("Serial read: %d,serial_fd=%d\n", res,serial_fd);

        if (res == 0)
        {
            
        }
        
        return E_SERIAL_NODATA;
    }
    
}

/***********************************************************
* 名    称：eSerial_Write(const unsigned char data)
* 功    能：读串口（单个字节）
* 入口参数：
* 出口参数：
* 说    明： 					 
**********************************************************/  
teSerial_Status eSerial_Write(const unsigned char data)
{
    int err, attempts = 0;

    if(serial_fd <= 0)
    {
        return E_SERIAL_FD_ERROR;
    }
    
     SERIAL_LOG_DBG("TX 0x%02x\n", data);
    
    err = write(serial_fd,&data,1);
    if (err < 0)
    {
        if (errno == EAGAIN)
        {
            for (attempts = 0; attempts <= 5; attempts++)
            {
                usleep(1000);
                err = write(serial_fd,&data,1);
                if (err < 0) 
                {
                    if ((errno == EAGAIN) && (attempts == 5))
                    {
                        SERIAL_LOG_ERR("Error writing to module after %d attempts(%s)", attempts, strerror(errno));
                        exit(-1);
                    }
                }
                else
                {
                    break;
                }
            }
        }
        else
        {
            SERIAL_LOG_ERR("Error writing to module(%s)", strerror(errno));
            exit(-1);
        }
    }
    return E_SERIAL_OK;
}

/***********************************************************
* 名    称：eSerial_ReadBuffer(unsigned char *data, int *count)
* 功    能：读串口（多个字节）
* 入口参数： 
* 出口参数：
* 说    明： 					 
**********************************************************/  
teSerial_Status eSerial_ReadBuffer(unsigned char *data, int *count)
{
    int res;

    if(serial_fd <= 0)
    {
        return E_SERIAL_FD_ERROR;
    }
    
    res = read(serial_fd, data, *count);
    SERIAL_LOG_DBG("uart recv res:%d\n",res);
    if (res > 0)
    {
        *count = res;
        SERIAL_LOG_DBG("uart recv:%d\n",res);
        return E_SERIAL_OK;
    }
    else
    {
#if DEBUG
        if (verbosity >= LOG_DEBUG) daemon_log(LOG_DEBUG, "Serial read: %d\n", res);
#endif /* DEBUG */

        if(res < 0)
        {
                SERIAL_LOG_ERR("readbuff error\n");
                return E_SERIAL_FD_ERROR;
        }

        if (res == 0)
        {
            //SERIAL_LOG_ERR("Serial connection to module interrupted");
            //bRunning = 0;
        }
        res = *count = 0;
        return E_SERIAL_NODATA;
    }
}

/***********************************************************
* 名    称：eSerial_WriteBuffer(unsigned char *data, int *count)
* 功    能：写串口（多个字节）
* 入口参数： 
* 出口参数：
* 说    明： 					 
**********************************************************/  
teSerial_Status eSerial_WriteBuffer(unsigned char *data, uint32_t count)
{
    int attempts = 0;
    //printf("send char %d\n", data);
    int total_sent_bytes = 0, sent_bytes = 0;

    if(serial_fd <= 0)
    {
        return E_SERIAL_FD_ERROR;
    }

	
    while (total_sent_bytes < count)
    {
        sent_bytes = write(serial_fd, &data[total_sent_bytes], count - total_sent_bytes);
        if (sent_bytes <= 0)
        {
            if (errno == EAGAIN)
            {
                if (++attempts >= 5)
                {
                    SERIAL_LOG_ERR("Error writing to module(%s)", strerror(errno));
                    return E_SERIAL_ERROR;
                }
                usleep(1000);
            }
            else
            {
                SERIAL_LOG_ERR("Error writing to module(%s)", strerror(errno));
                return -1;
            }
        }
        else
        {
            attempts = 0;
            total_sent_bytes += sent_bytes;
        }
    }
    return E_SERIAL_OK;
}


