/* Copyright (C) 2018 RDA Technologies Limited and/or its affiliates("RDA").
 * All rights reserved.
 *
 * This software is supplied "AS IS" without any warranties.
 * RDA assumes no responsibility or liability for the use of the software,
 * conveys no license or title under any patent, copyright, or mask work
 * right to the product. RDA reserves the right to make changes in the
 * software without notification.  RDA also make no representation or
 * warranty that such application will be suitable for the specified use
 * without further testing or modification.
 */

#ifndef _DRV_UART_H_
#define _DRV_UART_H_

#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#include "drv_names.h"
#include "drv_config.h"

#ifdef __cplusplus
extern "C" {
#endif

struct drvUart;

/**
 * @brief UART struct
 *
 * Only a declare here, caller have no necessary to know
 * how it is implemented. And it is a anchor to identify
 * which uart device is to be handled.
 */
typedef struct drvUart drvUart_t;

/**
 * @brief UART data event
 */
enum
{
    DRV_UART_EVENT_RX_ARRIVED = (1 << 0),  ///< Received new data
    DRV_UART_EVENT_RX_OVERFLOW = (1 << 1), ///< Rx fifo overflowed
    DRV_UART_EVENT_TX_COMPLETE = (1 << 2)  ///< All data had been sent
};

/**
 * @brief UART data bits
 */
typedef enum
{
    DRV_UART_DATA_BITS_5 = 5,
    DRV_UART_DATA_BITS_6 = 6,
    DRV_UART_DATA_BITS_7 = 7,
    DRV_UART_DATA_BITS_8 = 8
} drvUartDataBits_t;

/**
 * @brief UART stop bits
 */
typedef enum
{
    DRV_UART_STOP_BITS_1 = 1,
    DRV_UART_STOP_BITS_2 = 2
} drvUartStopBits_t;

/**
 * @brief UART parity check mode
 */
typedef enum
{
    DRV_UART_NO_PARITY,    ///< No parity check
    DRV_UART_ODD_PARITY,   ///< Parity check is odd
    DRV_UART_EVEN_PARITY,  ///< Parity check is even
    DRV_UART_SPACE_PARITY, ///< Parity check is always 0 (space)
    DRV_UART_MARK_PARITY   ///< Parity check is always 1 (mark)
} drvUartParity_t;

/**
 * @brief function type to notify data event
 */
typedef void (*drvUartEventCB_t)(void *param, uint32_t evt);

/**
 * @brief UART config
 */
typedef struct drvUartCfg
{
    uint32_t baud;               ///< baudrate, 0 for auto baud
    drvUartDataBits_t data_bits; ///< data bits
    drvUartStopBits_t stop_bits; ///< stop bits
    drvUartParity_t parity;      ///< parity check mode
    bool auto_baud_lc;           ///< "at" ot "AT" for auto baud. 8910 can detect both "at" and "AT". So, it will be ignored in 8910.
    bool cts_enable;             ///< enable cts or not
    bool rts_enable;             ///< enable rts or not
    size_t rx_buf_size;          ///< rx buffer size
    size_t tx_buf_size;          ///< tx buffer size
    uint32_t event_mask;         ///< event mask, identify which event will be notified
    drvUartEventCB_t event_cb;   ///< event notify callback function
    void *event_cb_ctx;          ///< app private param (like a context)
} drvUartCfg_t;

/**
 * @brief UART attributes
 */
typedef struct
{
    uint32_t baud_max;
    bool data_bit5_support : 1;
    bool data_bit6_support : 1;
    bool data_bit7_support : 1;
    bool data_bit8_support : 1;
    bool stop_bit1_support : 1;
    bool stop_bit2_support : 1;
    bool parity_odd_support : 1;
    bool parity_even_support : 1;
    bool parity_mark_support : 1;
    bool parity_space_support : 1;
} drvUartAttr_t;

/**
 * @brief create an UART driver
 *
 * Create an UART driver, caller should provide an valid UART FOURCC device name
 * and an valid UART config.
 *
 * @param name  FOURCC UART name (DRV_NAME_UART{1, 2, 3})
 * @param cfg   UART config
 * @return
 *      - NULL      Fail to create an UART driver
 *      - non-null  Create an UART driver
 */
drvUart_t *drvUartCreate(uint32_t name, drvUartCfg_t *cfg);

/**
 * @brief open UART driver
 *
 * Open the UART driver, done it can send/receive data via the UART.
 *
 * When the UART is already opened, it will return true directly.
 *
 * @param uart  the UART driver
 * @return
 *      - true  success
 *      - false fail
 */
bool drvUartOpen(drvUart_t *uart);

/**
 * @brief create an UART driver for already opened device
 *
 * This is similar to \p drvUartCreate and \p drvUartOpen. And it will only
 * create and open already enabled device. Inside, UART configuration won't
 * be changed. So, UART configuration related fields will be ignored, and
 * will be filled with the actual value.
 *
 * This will be useful when the data already in hardware rx fifo shall be
 * kept.
 *
 * @param name  FOURCC UART name (DRV_NAME_UART{1, 2, 3})
 * @param cfg   UART config
 * @return
 *      - created UART driver
 *      - NULL on fail, invalid parameter or UART isn't enabled.
 */
drvUart_t *drvUartOpenKeepConfig(uint32_t name, drvUartCfg_t *cfg);

/**
 * @brief close UART driver
 *
 * @note All data in tx/rx buffer will be purged.
 *
 * Close the UART driver, stop all data transfer.
 * But do not release resource.
 *
 * @param uart  the UART driver
 */
void drvUartClose(drvUart_t *uart);

/**
 * @brief destroy the UART driver
 *
 * @param uart  the UART driver
 */
void drvUartDestroy(drvUart_t *uart);

/**
 * @brief send data via the UART
 *
 * @note data may not send to hardware right away
 *
 * Send data via UART, the data may not send to hardware directly
 * because the UART hw may be busy, and the data not sent to hw will
 * be cached to a software tx buffer(see, drvUartCfg_t->tx_buf_size).
 *
 * Therefore the return value include the data had been sent and the
 * data had been cached.
 *
 * @param uart  the UART driver
 * @param data  data buffer to be sent
 * @param size  data buffer size
 * @return
 *      - (-1)  Parameter error
 *      - OTHERS (>=0)  The number of bytes actually sent (or cached)
 */
int drvUartSend(drvUart_t *uart, const void *data, size_t size);

/**
 * @brief send data via the UART without cache in n milliseconds
 *
 * @note data will not cached to tx fifo
 *
 * @param uart          the UART driver
 * @param data          data buffer to be sent
 * @param size          data buffer size
 * @param timeout_ms    timeout milliseconds
 * @return
 *      - (-1) Parameter error
 *      - OTHERS (>=0)  The number of bytes actually sent
 */
int drvUartSendAll(drvUart_t *uart, const void *data, size_t size, uint32_t timeout_ms);

/**
 * @brief receive data from the UART
 *
 * Receive data from the UART driver. Actually caller got data
 * from a software rx fifo but not hw fifo directly.
 *
 * After drvUartOpen the driver will store data from UART hw rx fifo
 * automatically and trigger a DRV_UART_EVENT_RX_ARRIVED, app who use
 * this driver can call the api after receive the event.
 *
 * @param uart  the UART driver
 * @param buf   buffer to store data
 * @param size  buffer size
 * @return
 *      - (-1) Parameter error
 *      - OTHERS (>=0)  The number of bytes actually receive from UART
 */
int drvUartReceive(drvUart_t *uart, void *buf, size_t size);

/**
 * @brief inquire avalable bytes in rx buffer
 *
 * @param uart  the UART driver
 * @return
 *      - (-1) Parameter error
 *      - OTHERS (>=0) Available size in byte
 */
int drvUartReadAvail(drvUart_t *uart);

/**
 * @brief inquire avalable space in tx buffer
 *
 * @param uart  the UART driver
 * @return
 *      - (-1) Parameter error
 *      - OTHERS (>=0) Available size in byte
 */
int drvUartWriteAvail(drvUart_t *uart);

/**
 * @brief set whether UART will auto sleep
 *
 * When enabled, UART will sleep after all bytes are transfered , with
 * specified timeout.
 *
 * To disable auto sleep feature, set \p timeout to -1 (or other negative
 * values).
 *
 * It maybe different among chips, or even different UARTs of one chip
 * whether system will be waken up when there are UART input.
 *
 * @param uart      the UART driver
 * @param timeout   auto sleep wait time after transfer done. It can be
 *                  0 but not recommended. Negative value to disable
 *                  auto sleep feature.
 */
void drvUartSetAutoSleep(drvUart_t *uart, int timeout);

/**
 * @brief get uart device name
 *
 * @param uart  the UART driver
 * @return uart device name
 */
uint32_t drvUartGetName(drvUart_t *uart);

/**
 * @brief get uart configuration, will never return null
 *
 * @param uart  the UART driver
 * @return
 *      - NULL      fail, only when UART is NULL
 *      - otherwise the UART configuration struct point.
 */
const drvUartCfg_t *drvUartConfig(drvUart_t *uart);

/**
 * @brief get the UART attribute
 *
 * @param uart  the UART driver
 * @return
 *      - NULL  fail, only when \p uart is NULL
 *      - other the UART attribute struct address
 */
const drvUartAttr_t *drvUartAttribute(drvUart_t *uart);

/**
 * @brief get the UART attribute from the device name
 *
 * @param name  the UART device name
 * @return
 *      - NULL  invalid device name
 *      - other the UART attribute struct address
 */
const drvUartAttr_t *drvUartAttributeFromName(uint32_t name);

/**
 * @brief reconfig the uart
 *
 * @note caller should make sure the UART had already closed,
 *       or it will return false
 *       if `cfg` is NULL, it will do nothing and return true
 *
 * @param uart  the UART driver
 * @param cfg   the configuration
 * @return
 *      - true  sucess
 *      - false fail
 */
bool drvUartReconfig(drvUart_t *uart, drvUartCfg_t *cfg);

/**
 * @brief set event mask and callback
 *
 * @param uart  the UART driver
 * @param event_mask event mask, identify which event will be notified
 * @param event_cb event notify callback function
 * @param event_cb_ctx event callback context
 * @return
 *      - true on sucess
 *      - false on fail, invalid parameter
 */
bool drvUartSetCallback(drvUart_t *uart, uint32_t event_mask, drvUartEventCB_t event_cb, void *event_cb_ctx);

/**
 * @brief wait rx available, with optional timeout
 *
 * @param uart      the UART driver
 * @param timeout   wait timeout
 * @return
 *      - true on success
 *      - false on fail, invalid prameter or timeout
 */
bool drvUartWaitRxAvail(drvUart_t *uart, unsigned timeout);

/**
 * @brief wait tx available, with optional timeout
 *
 * @param uart      the UART driver
 * @param timeout   wait timeout
 * @return
 *      - true on success
 *      - false on fail, invalid prameter or timeout
 */
bool drvUartWaitTxAvail(drvUart_t *uart, unsigned timeout);

/**
 * @brief wait tx data send finished
 *
 * @param uart      the UART driver
 * @param timeout   wait time in millisecond
 * @return
 *      - true      data all sent
 *      - false     data residual
 */
bool drvUartWaitTxFinish(drvUart_t *uart, uint32_t timeout);

/**
 * @brief bind UART to stdio
 *
 * @param uart      the UART driver
 * @return
 *      - true      on success
 *      - false     on fail
 */
bool drvUartBindStdio(drvUart_t *uart);

#ifdef __cplusplus
}
#endif
#endif
