/*****************************************************************************
* | File        :   dev_hardware_UART.c
* | Author      :   Waveshare team
* | Function    :   Read and write /dev/UART,  hardware UART
* | Info        :
*----------------
* |	This version:   V1.0
* | Date        :   2019-06-26
* | Info        :   Basic version
*
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documnetation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to  whom the Software is
# furished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
******************************************************************************/
#include "dev_hardware_UART.h"

#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <termios.h>
#include <unistd.h>
#include <string.h>

HARDWARE_UART hardware_UART;
struct termios UART_Set;

/******************************************************************************
function:   Serial port initialization
parameter:
    UART_device : Device name
Info:
    /dev/ttyTHS*
    The default baud rate is 9600, 8-bit data, 1 stop bit, no parity
******************************************************************************/
void DEV_HARDWARE_UART_begin(char *UART_device)
{
    // device
    if ((hardware_UART.fd = open(UART_device, O_RDWR | O_NOCTTY)) < 0)
    { // 打开UART
        perror("Failed to open UART device.\n");
        exit(1);
    }
    else
    {
        DEV_HARDWARE_UART_Debug("open : %s\r\n", UART_device);
    }
    DEV_HARDWARE_UART_setBaudrate(B9600);
    DEV_HARDWARE_UART_Set(8, 1, 'N');
}

/******************************************************************************
function:   Serial device End
parameter:
Info:
******************************************************************************/
void DEV_HARDWARE_UART_end(void)
{
    if (close(hardware_UART.fd) != 0)
    {
        DEV_HARDWARE_UART_Debug("Failed to close UART device\r\n");
        perror("Failed to close UART device.\n");
    }
}

/******************************************************************************
function:   Set the serial port baud rate
parameter:
    Baudrate : Serial port baud rate
               B50          50
               B75          75
               B110         110
               B134         134
               B150         150
               B200         200
               B300         300
               B600         600
               B1200        1200
               B1800        1800
               B2400        2400
               B4800        4800
               B9600        9600
               B19200       19200
               B38400       38400
               B57600       57600
               B115200      115200
               B230400      230400
               B460800      460800
               B921600      921600
Info:
******************************************************************************/
void DEV_HARDWARE_UART_setBaudrate(uint32_t Baudrate)
{
    uint16_t err;
    uint32_t baud;
    tcflush(hardware_UART.fd, TCIOFLUSH); // 清空数据缓冲区

    switch (Baudrate) // Set the number of data bits
    {

    case 921600:
        baud = B921600;
        break;
        
    case 460800:
        baud = B460800;
        break;

    case 230400:
        baud = B230400;
        break;

    case 115200:
        baud = B115200;
        break;

    case 57600:
        baud = B57600;
        break;

    case 38400:
        baud = B38400;
        break;

    case 19200:
        baud = B19200;
        break;

    case 9600:
        baud = B9600;
        break;

    case 4800:
        baud = B4800;
        break;

    case 2400:
        baud = B2400;
        break;

    case 1800:
        baud = B1800;
        break;

    case 1200:
        baud = B1200;
        break;

    case 600:
        baud = B600;
        break;

    case 300:
        baud = B300;
        break;

    case 200:
        baud = B200;
        break;

    case 150:
        baud = B150;
        break;

    case 134:
        baud = B134;
        break;

    case 110:
        baud = B110;
        break;

    case 75:
        baud = B75;
        break;

    case 50:
        baud = B50;
        break;
    default:
        baud = B9600;
        break;
    }
    DEV_HARDWARE_UART_Debug("Baud rate setting\r\n");
    if (cfsetispeed(&UART_Set, baud) != 0)
    {
        DEV_HARDWARE_UART_Debug("Baud rate setting failed 1\r\n");
    }
    if (cfsetospeed(&UART_Set, baud) != 0)
    {
        DEV_HARDWARE_UART_Debug("Baud rate setting failed 2\r\n");
    }
    err = tcsetattr(hardware_UART.fd, TCSANOW, &UART_Set);
    if (err != 0)
    {
        perror("tcsetattr fd");
        DEV_HARDWARE_UART_Debug("Setting the terminal baud rate failed\r\n");
    }
}

/******************************************************************************
function: Serial port sends one byte of data
parameter:
    buf :   Sent data
Info:
******************************************************************************/
uint8_t DEV_HARDWARE_UART_writeByte(uint8_t buf)
{
    uint8_t sbuf[1] = {0};
    sbuf[0] = buf;
    write(hardware_UART.fd, sbuf, 1);
    return 0;
}

/******************************************************************************
function: Serial port sends arbitrary length data
parameter:
    buf :   Sent Data buffer
    len :   Number of bytes sent
Info:
******************************************************************************/
uint8_t DEV_HARDWARE_UART_write(uint8_t *buf, uint32_t len)
{
    write(hardware_UART.fd, buf, len);
    return 0;
}

/******************************************************************************
function: The serial port reads a byte
parameter:
Info: Return read data
******************************************************************************/
uint8_t DEV_HARDWARE_UART_readByte(void)
{
    uint8_t buf[1] = {0};
    read(hardware_UART.fd, buf, 1);
    return buf[0];
}

/******************************************************************************
function: Serial port reads arbitrary byte length data
parameter:
    buf :   Read Data buffer
    len :   Number of bytes Read
Info:
******************************************************************************/
uint8_t DEV_HARDWARE_UART_read(char *buf, uint32_t len)
{
    read(hardware_UART.fd, buf, len);
    return 0;
}

/******************************************************************************
function: Serial port reads arbitrary byte length data
parameter:
    buf :   Read Data buffer
    len :   Number of bytes Read
Info:
******************************************************************************/
void DEV_HARDWARE_UART_flushInput()
{
    tcflush(hardware_UART.fd, TCIFLUSH);
}

/******************************************************************************
function: Set serial port parameters
parameter:
    databits :   Data bit
    stopbits :   Stop bit
    parity   :   Parity bit
Info:
******************************************************************************/
int DEV_HARDWARE_UART_Set(int databits, int stopbits, int parity)
{
    if (tcgetattr(hardware_UART.fd, &UART_Set) != 0)
    {
        perror("tcgetattr fd");
        DEV_HARDWARE_UART_Debug("Failed to get terminal parameters\r\n");
        return 0;
    }
    UART_Set.c_cflag |= (CLOCAL | CREAD); // Generally set flag

    switch (databits) // Set the number of data bits
    {
    case 5:
        UART_Set.c_cflag &= ~CSIZE;
        UART_Set.c_cflag |= CS5;
        break;
    case 6:
        UART_Set.c_cflag &= ~CSIZE;
        UART_Set.c_cflag |= CS6;
        break;
    case 7:
        UART_Set.c_cflag &= ~CSIZE;
        UART_Set.c_cflag |= CS7;
        break;
    case 8:
        UART_Set.c_cflag &= ~CSIZE;
        UART_Set.c_cflag |= CS8;
        break;
    default:
        fprintf(stderr, "Unsupported data size.\n");
        return 0;
    }

    switch (parity) // Set check digit
    {
    case 'n':
    case 'N':
        UART_Set.c_cflag &= ~PARENB; // Clear check digit
        UART_Set.c_iflag &= ~INPCK;  // enable parity checking
        break;
    case 'o':
    case 'O':
        UART_Set.c_cflag |= PARENB; // enable parity
        UART_Set.c_cflag |= PARODD; // Odd parity
        UART_Set.c_iflag |= INPCK;  // disable parity checking
        break;
    case 'e':
    case 'E':
        UART_Set.c_cflag |= PARENB;  // enable parity
        UART_Set.c_cflag &= ~PARODD; // Even parity
        UART_Set.c_iflag |= INPCK;   // disable pairty checking
        break;
    case 's':
    case 'S':
        UART_Set.c_cflag &= ~PARENB; // Clear check digit
        UART_Set.c_cflag &= ~CSTOPB;
        UART_Set.c_iflag |= INPCK; // disable pairty checking
        break;
    default:
        fprintf(stderr, "Unsupported parity.\n");
        return 0;
    }
    switch (stopbits) // Set stop bit  1   2
    {
    case 1:
        UART_Set.c_cflag &= ~CSTOPB;
        break;
    case 2:
        UART_Set.c_cflag |= CSTOPB;
        break;
    default:
        fprintf(stderr, "Unsupported stopbits.\n");
        return 0;
    }
    UART_Set.c_cflag |= (CLOCAL | CREAD);
    UART_Set.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
    UART_Set.c_oflag &= ~OPOST;
    UART_Set.c_oflag &= ~(ONLCR | OCRNL);
    UART_Set.c_iflag &= ~(ICRNL | INLCR);
    UART_Set.c_iflag &= ~(IXON | IXOFF | IXANY);
    tcflush(hardware_UART.fd, TCIFLUSH);
    UART_Set.c_cc[VTIME] = 150;                               // Set timeout
    UART_Set.c_cc[VMIN] = 1;                                  //
    if (tcsetattr(hardware_UART.fd, TCSANOW, &UART_Set) != 0) // Update the UART_Set and do it now
    {
        perror("tcsetattr fd");
        DEV_HARDWARE_UART_Debug("Setting terminal parameters failed\r\n");
        return 0;
    }
    DEV_HARDWARE_UART_Debug("Set terminal parameters successfully\r\n");
    return 1;
}
