
#include "uart.h"

#include <termios.h>    /*PPSIX�ն˿��ƶ���*/
#include <linux/serial.h>

/***@brief  ���ô���ͨ������
*@param  fd     ���� int  �򿪴��ڵ��ļ����
*@param  speed  ���� int  �����ٶ�
*@return  void*/

int speed_arr[] = {B500000, B460800, B230400, B115200, B57600, B38400, B19200, B9600, B4800, B2400, B1200, B300,
                   B57600, B38400, B19200, B9600, B4800, B2400, B1200, B300,
                  };
int name_arr[] = {500000, 460800, 230400, 115200, 57600, 38400,  19200,  9600,  4800,  2400,  1200,  300,
                  57600, 38400,  19200,  9600, 4800, 2400, 1200,  300,
                 };

void uart_set_speed(int fd, int speed)
{
    int   i;
    int   status;
    struct termios   Opt;
    tcgetattr(fd, &Opt);
    for (i = 0;  i < sizeof(speed_arr) / sizeof(int);  i++)
    {
        if (speed == name_arr[i])
        {
            tcflush(fd, TCIOFLUSH);
            cfsetispeed(&Opt, speed_arr[i]);
            status = cfsetospeed(&Opt, speed_arr[i]);
            printf("set speed %d, status %d\n", speed, status);
            status = tcsetattr(fd, TCSANOW, &Opt);
            if (status != 0)
            {
                printf("tcsetattr fd1, errno %d\n", errno);
            }
            return;
        }
        tcflush(fd, TCIOFLUSH);
    }
}

static speed_t get_baudrate(int baudrate)
{
    switch (baudrate)
    {
        case 0:
            return B0;
        case 50:
            return B50;
        case 75:
            return B75;
        case 110:
            return B110;
        case 134:
            return B134;
        case 150:
            return B150;
        case 200:
            return B200;
        case 300:
            return B300;
        case 600:
            return B600;
        case 1200:
            return B1200;
        case 1800:
            return B1800;
        case 2400:
            return B2400;
        case 4800:
            return B4800;
        case 9600:
            return B9600;
        case 19200:
            return B19200;
        case 38400:
            return B38400;
        case 57600:
            return B57600;
        case 115200:
            return B115200;
        case 230400:
            return B230400;
        case 460800:
            return B460800;
        case 500000:
            return B500000;
        case 576000:
            return B576000;
        case 921600:
            return B921600;
        case 1000000:
            return B1000000;
        case 1152000:
            return B1152000;
        case 1500000:
            return B1500000;
        case 2000000:
            return B2000000;
        case 2500000:
            return B2500000;
        case 3000000:
            return B3000000;
        case 3500000:
            return B3500000;
        case 4000000:
            return B4000000;
        default:
            return -1;
    }
}

int uart_config(int fd, int databits, int stopbits, int parity, int baudrate)
{
    speed_t speed;
    struct termios Opt;

    if (tcgetattr(fd, &Opt) != 0)
    {
        perror("uart_set tcgetattr init fail.");
        return -1;
    }

    Opt.c_cflag |= (CLOCAL | CREAD);

    switch (databits)
    {
        case 7:
            Opt.c_cflag &= ~CSIZE;
            Opt.c_cflag |= CS7;
            break;
        case 8:
            Opt.c_cflag &= ~CSIZE;
            Opt.c_cflag |= CS8;
            break;
        default:
            printf("uart_set Unsupported databits.");
            return -1;
    }

    switch (parity)
    {
        case 'n':
        case 'N':
            Opt.c_cflag &= ~PARENB;
            Opt.c_iflag &= ~INPCK;
            break;
        case 'o':
        case 'O':
            Opt.c_cflag |= PARENB;
            Opt.c_cflag |= PARODD;
            Opt.c_iflag |= INPCK;
            break;
        case 'e':
        case 'E':
            Opt.c_cflag |= PARENB;
            Opt.c_cflag &= ~PARODD;
            Opt.c_iflag |= INPCK;
            break;
        case 's':
        case 'S':
            Opt.c_cflag &= ~PARENB;
            Opt.c_cflag &= ~CSTOPB;
            Opt.c_iflag |= INPCK;
            break;
        default:
            printf("uart_set Unsupported parity.");
            return -1;
    }

    switch (stopbits)
    {
        case 1:
            Opt.c_cflag &= ~CSTOPB;
            break;
        case 2:
            Opt.c_cflag |= CSTOPB;
            break;
        default:
            printf("uart_set Unsupported stopbits.");
            return -1;
    }

    speed = get_baudrate(baudrate);
    if ((int) speed == -1)
    {
        /* TODO: throw an exception */
        printf("uart_set Invalid baudrate.");
        return -1;
    }
    tcflush(fd, TCIOFLUSH);
    cfsetispeed(&Opt, speed);
    cfsetospeed(&Opt, speed);

    Opt.c_cflag |= (CLOCAL | CREAD);

    Opt.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);

    Opt.c_oflag &= ~OPOST;
    Opt.c_oflag &= ~(ONLCR | OCRNL);

    Opt.c_iflag &= ~(ICRNL | INLCR);
    Opt.c_iflag &= ~(IXON | IXOFF | IXANY);

    tcflush(fd, TCIFLUSH);

    Opt.c_cc[VTIME] = 0;
    Opt.c_cc[VMIN] = 0;

    if (tcsetattr(fd, TCSANOW, &Opt) != 0)
    {
        printf("uart_set tcgetattr TCSANOW fail.");
        return -1;
    }

    return 0;

}
/**
*@brief   ���ô�������λ��ֹͣλ��Ч��λ
*@param  fd     ����  int  �򿪵Ĵ����ļ����*
*@param  databits ����  int ����λ   ȡֵ Ϊ 7 ����8*
*@param  stopbits ����  int ֹͣλ   ȡֵΪ 1 ����2*
*@param  parity  ����  int  Ч������ ȡֵΪN,E,O,,S
*/
int uart_set_param(int fd, int databits, int stopbits, int parity, int flow)
{
    struct termios options;

    if (tcgetattr(fd, &options)  !=  0)
    {
        printf("SetupSerial 1, errno %d\n", errno);
        return -1;
    }

    options.c_cflag &= ~CSIZE;
    switch (databits) /*��������λ��*/
    {
        case 5:
            options.c_cflag |= CS5;
            break;
        case 6:
            options.c_cflag |= CS6;
            break;
        case 7:
            options.c_cflag |= CS7;
            break;
        case 8:
            options.c_cflag |= CS8;
            break;
        default:
            printf("Unsupported data size, errno %d\n", errno);
            return -1;
    }

    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:
            printf("Unsupported parity, errno %d\n", errno);
            return -1;
    }

    /* ����ֹͣλ*/
    switch (stopbits)
    {
        case 1:
            options.c_cflag &= ~CSTOPB;
            break;
        case 2:
            options.c_cflag |= CSTOPB;
            break;
        default:
            printf("Unsupported stop bits, errno %d\n", errno);
            return -1;
    }

    /* Set input parity option */
    if (parity != 'n')
    {
        options.c_iflag |= INPCK;
    }

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

    if (flow == 0)
    {
        options.c_iflag &= ~(IXON | IXOFF);
    }

    if (tcsetattr(fd, TCSANOW, &options) != 0)
    {
        printf("SetupSerial 3, errno %d\n", errno);
        return -1;
    }

    tcflush(fd, TCIOFLUSH); /* Update the options and do it NOW */

    return 0;
}
/**
*@breif �򿪴���
*/
int uart_open(char *Dev, int open_flags)
{
    struct termios opt;

    int fd = open(Dev, open_flags);         //
    if (-1 == fd)
    {
        printf("Can't Open Serial Port, errno %d\n", errno);
        return -1;
    }

    tcgetattr(fd, &opt);
    //  opt.c_lflag &= ~(ICANON | ECHO | ISIG);
    opt.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);

    /*
     * No SIGINT on BREAK, CR-to-NL off, input parity
     * check off, don't strip 8th bit on input, output
     * flow control off.
     */
    //    opt.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
    opt.c_iflag &= ~(BRKINT | ICRNL);
    opt.c_oflag &= ~(OPOST);


    tcsetattr(fd, TCSANOW, &opt);

    tcflush(fd, TCIOFLUSH);

    return fd;
}

int uart_set_raw(int fd)
{
    struct termios opt;

    tcgetattr(fd, &opt);

    opt.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP
                     | INLCR | IGNCR | ICRNL | IXON | INPCK | IUCLC);
    opt.c_oflag &= ~OPOST;
    opt.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
    opt.c_cflag &= ~(CSIZE | PARENB);
    opt.c_cflag |= CS8;

    tcsetattr(fd, TCSANOW, &opt);

    tcflush(fd, TCIOFLUSH);

    return 0;
}

/*
speed:
    500000, 460800, 230400, 115200, 57600, 38400,  19200,  9600,  4800,  2400,  1200,  300,
        57600, 38400,  19200,  9600, 4800, 2400, 1200,  300
nonblock:
    �Ƿ������
*/

int uart_init(char *dev, int speed, int stop, int parity,int bits, int nonblock)
{
    int fd;
    char cparity = 'N';

    int open_flags = O_RDWR | O_NOCTTY;
    if (nonblock)
    {
        open_flags |= O_NONBLOCK;
    }

    if (parity == 1)
    {
        cparity = 'O';
    }
    else if (parity == 2)
    {
        cparity = 'E';
    }
    fd = open(dev, O_RDWR | O_NOCTTY | O_NONBLOCK);
    if (fd < 0)
    {
        return -1;
    }

    if (uart_config(fd, bits, stop, cparity, speed))
    {
        printf("uart set param error, errno %d\n", errno);
        if (fd > 0)
        {
            close(fd);
        }
        return -1;
    }
    tcflush(fd, TCIOFLUSH);

    return fd;
}

void uart_uninit(int fd)
{
    close(fd);
}
