/*
 * r_sci_uart_buf.c
 *
 *  Created on: 2/02/2021 г.
 *      Author: alexraynepe196@gmail.com
 * ----------------------------------------------------------------------
 * драйвер буфера УАРТа, на базе интерфейса r_sci_uart
 *
 */

// FSP generated sources
#include "r_uart_api.h"
#include "r_sci_uart.h"
#include <lib/ringbuf16index.h>
#include <mcu_usart.h>
#include <stdbool.h>

#include "r_sci_uart_buf.h"
#include "r_sci_uartx.h"

#include <project-conf.h>



#ifndef UARTBUF_RXDMA_LOOP
#define UARTBUF_RXDMA_LOOP  1
#endif

///< останов на переполнении приемного буффера. требует запуск приема по освобождению.
#define UARTBUF_RXOVER_STOP         0
///< при переполнении буффераб дожно дропать содержание головы, хвост всегда актуален
#define UARTBUF_RXOVER_OVERRIDE     1

#if UARTBUF_RXDMA_LOOP
#include "r_dtc.h"
// force override mode when DMA looped mode used for rx
#undef UARTBUF_RXOVER_STYLE
#define UARTBUF_RXOVER_STYLE        UARTBUF_RXOVER_OVERRIDE
#else

#ifndef UARTBUF_RXOVER_STYLE
#define UARTBUF_RXOVER_STYLE        UARTBUF_RXOVER_OVERRIDE
#endif

#endif


/**
 * TODO: имеет смысл сделать версию r_sci_uartbuf_onevent для DTC/без DTC; c буффером/насквозь
 * TODO: uartbuf_api_t - позволит полиморфный апи для разной настройки - с DTC/без DTC; c буффером/насквозь
 * */

/////////////////////////////////////////////////////////////////////////////////////////////////
#if 1
#include <assert.h>
#define ASSERT(...) assert(__VA_ARGS__);
#else
#define ASSERT(...)
#endif

/////////////////////////////////////////////////////////////////////////////////////////////////
#include <trace_probes.h>

#ifndef trace_uisr
trace_need(uisr);

trace_need(uisr_rx);
trace_need(uisr_rxc);
trace_need(uisr_txc);

trace_need(urx_up);
trace_need(urx_rc);
#endif
#ifndef trace_hdlc
trace_need(hdlc);
trace_need(uisr_hdlc);
#endif

#ifdef UART_PROBE_TARGET
#include <hal_data.h>
static inline
void trace_uart_target(uartbuf_instance_ctrl_t* p_ctrl){
    if ((void*)p_ctrl == (void*)&UART_PROBE_TARGET )
        trace_hdlc_on();
    else
        trace_hdlc_off();
}
#else
#define trace_uart_target(x)
#endif


/////////////////////////////////////////////////////////////////////////////////////////////////

static
unsigned safe_ringbuf16index_getn_avail(const struct ringbuf16index *r){
    FSP_CRITICAL_SECTION_DEFINE;
    FSP_CRITICAL_SECTION_ENTER;
    unsigned ret = ringbuf16index_getn_avail(r);
    FSP_CRITICAL_SECTION_EXIT;
    return ret;
}

static
unsigned safe_ringbuf16index_put_free(const struct ringbuf16index *r){
    FSP_CRITICAL_SECTION_DEFINE;
    FSP_CRITICAL_SECTION_ENTER;
    unsigned ret = ringbuf16index_put_free(r);
    FSP_CRITICAL_SECTION_EXIT;
    return ret;
}



/// @brief update txbuf progress from DTC
/// @return ???  sent data?
static
int dtctx_update(uartbuf_ctrl_t * const p_ctrl){
    if (p_ctrl->txstore == NULL)
        return 0;
    if (p_ctrl->uart_ctrl == NULL)
        return 0;

    if ( p_ctrl->tx_dma != NULL ){
        transfer_info_t * p_info = p_ctrl->tx_dma;

        // FIX: do not monitor dma, when p_info->length == 0, since sending may
        //      not run DMA, for short transfers
        if ( (p_ctrl->posted == 0) || (p_info->length == 0) ) {
            // dma not works, so nothing update.
            return 0;
        }

        struct ringbuf16index* buf = (struct ringbuf16index*)&p_ctrl->txbuf;
        int sent = 0;

        FSP_CRITICAL_SECTION_DEFINE;
        FSP_CRITICAL_SECTION_ENTER;
        // for case, when finished during gie off
        if ( (p_ctrl->posted | p_info->length) > 0)
        {

        const uint8_t* dmasrc = (const uint8_t*)p_info->p_src;
        const uint8_t* txsrc  = p_ctrl->txstore + buf->get_ptr;
        sent = dmasrc - txsrc;
        if (sent > 0){
            int avail = (int)ringbuf16index_getn_avail(buf);
            ASSERT( sent <= avail ) ;
            ringbuf16index_getn(buf, (uint16_t)sent);
            ASSERT( (unsigned)sent <= p_ctrl->posted );
            p_ctrl->posted -= sent;
        }
        else if (sent < 0){
            if ( p_info->length == 0 ){ //!R_SCI_UART_tx_busy(p_ctrl->uart_ctrl)
                // может случиться после Abort(TX), ISR дропнет posted байт из отправки,на остановившемся ДМА
                p_ctrl->posted = 0;
            }
            else
            ASSERT(sent > 0);
        }

        }//if ( (p_ctrl->posted | p_info->length) > 0)
        FSP_CRITICAL_SECTION_EXIT;
        return sent;
    }
    else {
        return 0;
    }
}

static
int dtctx_send(uartbuf_ctrl_t * const p_ctrl){
    if (p_ctrl->txstore == NULL)
        return 0;
    if (p_ctrl->posted > 0)
        return 0;

    sci_uart_instance_ctrl_t* uart = (sci_uart_instance_ctrl_t*)p_ctrl->uart_ctrl;
    if (uart == NULL)
        return 0;

    volatile struct ringbuf16index* vbuf = &p_ctrl->txbuf;
    struct ringbuf16index* buf = (struct ringbuf16index*)vbuf;


    if ( p_ctrl->tx_dma != NULL ){
        transfer_info_t * p_info = p_ctrl->tx_dma;
        if (p_info->length > 0)
            return 0;

        unsigned avail = safe_ringbuf16index_getn_avail(buf);
        if (avail <= 0)
            return 0;

        // DMA offline, restart it
        const uint8_t* txsrc  = p_ctrl->txstore + buf->get_ptr;
        p_ctrl->posted = avail;
        int ok = R_SCI_UART_Write(uart, txsrc, avail);
        if (ok != FSP_SUCCESS){
            p_ctrl->posted = 0;
            return -ok;
        }
        return avail;
    }
    else {
        // no DMA, just fill output FIFO

        R_SCI0_Type* sci = uart->p_reg;

        unsigned sent = 0;
        for(; ( ( UART_GetFlagStatus( sci ) & UART_FLAG_TXE) != 0 ) ; ++sent){
            sci->TDR = p_ctrl->txstore[buf->get_ptr];
            if (ringbuf16index_get(buf) < 0)
                break;
        }
        p_ctrl->posted = sent;
        return sent;
    }
}

static
int safe_dtctx_send(uartbuf_ctrl_t * const p_ctrl){
    if (p_ctrl->posted > 0)
        return 0;
    //FSP_CRITICAL_SECTION_DEFINE;
    //FSP_CRITICAL_SECTION_ENTER;
    int ret = dtctx_send(p_ctrl);
    //FSP_CRITICAL_SECTION_EXIT;
    return ret;
}

/// @brief - continue DTC receive
/// @return > 0 - DTC active
///         < 0 - failed DTC operation
///         == 0 - no DTC operation
static
int dtcrx_recv(uartbuf_ctrl_t * const p_ctrl){
    if (p_ctrl->rxstore == NULL)
        return 0;

    sci_uart_instance_ctrl_t* uart = (sci_uart_instance_ctrl_t*)p_ctrl->uart_ctrl;
    if (uart == NULL)
        return 0;

    if ( p_ctrl->rx_dma != NULL ){

        transfer_info_t * p_info =  p_ctrl->rx_dma;
        if (p_info->length > 0)
            return 1;

        struct ringbuf16index* buf = (struct ringbuf16index*)&p_ctrl->rxbuf;

#if UARTBUF_RXOVER_STYLE == UARTBUF_RXOVER_STOP
        trace_urx_rc_twist();
        unsigned avail =safe_ringbuf16index_put_free(buf);
        if (avail <= 0){
            return UARTBUF_EVENT_FULL;
        }

        // DMA offline - restart it
        uint8_t* dst  = p_ctrl->rxstore + buf->put_ptr;
        int ok = R_SCI_UART_Read(uart, dst, avail);
        if (ok != FSP_SUCCESS){
            return -ok;
        }
        return avail;
#else
        buf->put_ptr = 0;
        int ok = R_SCI_UART_Read(uart, p_ctrl->rxstore, ringbuf16index_size(buf) );
        if (ok != FSP_SUCCESS){
            return -ok;
        }
#if UARTBUF_RXDMA_LOOP
        // here change DMA to repeat mode, and since this moment, it should never
        //      generate DMA IRQ
        p_info->repeat_area   = TRANSFER_REPEAT_AREA_DESTINATION;
        p_info->mode          = TRANSFER_MODE_REPEAT;
        p_info->length        = ringbuf16index_size(buf);
        p_info->p_dest        = p_ctrl->rxstore;
        ok = R_DTC_Reconfigure (uart->p_cfg->p_transfer_rx->p_ctrl, p_info);
#endif
        return ok;
#endif
    }
    else {
        return 0;
    }
}


/// @brief update txbuf progress from DTC
/// @return ???  sent data?
static
int dtcrx_update(uartbuf_ctrl_t * const p_ctrl){
    if (p_ctrl->rxstore == NULL)
        return 0;
    if (p_ctrl->uart_ctrl == NULL)
        return 0;

    if ( p_ctrl->rx_dma != NULL ){
        transfer_info_t * p_info = p_ctrl->rx_dma;
        struct ringbuf16index* buf = (struct ringbuf16index*)&p_ctrl->rxbuf;

        FSP_CRITICAL_SECTION_DEFINE;
        FSP_CRITICAL_SECTION_ENTER;
        trace_urx_up_twist();
        const uint8_t* dmasrc = (const uint8_t*)p_info->p_dest;

#if UARTBUF_RXOVER_STYLE == UARTBUF_RXOVER_STOP
        const uint8_t* dst  = p_ctrl->rxstore + buf->put_ptr;
        int put = dmasrc - dst;

        if (get > 0){
            ASSERT( (unsigned)put <= safe_ringbuf16index_put_free(buf) ) ;
            ringbuf16index_putn(buf, put);
            if (p_info->length <= 0)
                //this should fix DMA dest after overloop, so next time dtcrx_update will be correct
                p_info->p_dest = p_ctrl->rxstore + buf->put_ptr;
        }
        else if (get < 0){
            ASSERT(put > 0);
        }
#else
        uint16_t put = (uint16_t)(dmasrc - p_ctrl->rxstore) &  buf->mask;
        // got new buffer put position, now move get from overriden area
        if (put >= buf->put_ptr){
            if (buf->get_ptr <= buf->put_ptr )
                {;}
            else {//eat head of looped buf
                if (put >= buf->get_ptr)
                    buf->get_ptr = (put+1);
            }
        }
        else { // buffer have overlooped
            if (buf->get_ptr > buf->put_ptr ) // buffer full
                buf->get_ptr = (put+1);
            else //eat head
                if (put >= buf->get_ptr)
                    buf->get_ptr = (put+1);
        }
        buf->put_ptr = put;
#endif
        FSP_CRITICAL_SECTION_EXIT;
        return put;
    }
    else {
        return 0;
    }
}

//-------------------------------------------------------------------------------------
/// @brief view received data in buf
/// @return >= 0 - avail data amount
/// @arg  p_head - pointer to receiver buffer head
///             == NULL - just return amount of data string in buffer, like R_SCI_UARTBUF_avail
int R_SCI_UARTBUF_Head(uartbuf_ctrl_t * const p_ctrl, void** p_head){
#if (SCI_UART_CFG_PARAM_CHECKING_ENABLE)
    FSP_ASSERT(p_ctrl->rxstore !=NULL);
    FSP_ASSERT(p_ctrl->rxbuf.mask > 0);
#endif

    dtcrx_update(p_ctrl);

    struct ringbuf16index* buf = (struct ringbuf16index*)&p_ctrl->rxbuf;
    if (p_head){
        *p_head = p_ctrl->rxstore + buf->get_ptr;
    }
    return ringbuf16index_getn_avail(buf);
}
//-------------------------------------------------------------------------------------

int R_SCI_UARTBUF_Read(uartbuf_ctrl_t * const p_ctrl, void* p_dest, unsigned bytes){
#if (SCI_UART_CFG_PARAM_CHECKING_ENABLE)
    FSP_ASSERT(p_ctrl->rxstore !=NULL);
    FSP_ASSERT(p_ctrl->rxbuf.mask > 0);
#endif

    if (p_ctrl->rxstore == NULL)
        return 0;

#ifdef UART_PROBE_TARGET
    if ((void*)p_ctrl == (void*)&UART_PROBE_TARGET )
        trace_hdlc_on();
    else
        trace_hdlc_off();
#endif

    dtcrx_update(p_ctrl);

    struct ringbuf16index* buf = (struct ringbuf16index*)&p_ctrl->rxbuf;
    unsigned have = R_SCI_UARTBUF_avail(p_ctrl);
    if (have > bytes)
        have = bytes;

    uint8_t* data = (uint8_t*) p_dest;
    if ( have > 0 ){

        if (LIKELY(p_dest != NULL)){

            unsigned blocksize = ringbuf16index_getn_avail(buf);

            if (blocksize < have){
                memcpy(data, p_ctrl->rxstore + buf->get_ptr , blocksize);
                data += blocksize;
                have -= blocksize;
                ringbuf16index_getn(buf, blocksize);
                blocksize = ringbuf16index_getn_avail(buf);
                if (blocksize > have) {
                    blocksize = have;
                }
            }
            else
                blocksize = have;

            memcpy(data, p_ctrl->rxstore + buf->get_ptr , blocksize);
            data += blocksize;
            ringbuf16index_getn(buf, blocksize);
        }
        else {
            // just drop recv data
            ringbuf16index_getn(buf, (uint16_t)have);
        }

        if (ringbuf16index_empty(buf))
            p_ctrl->rx_err_cnt  = 0;
    }
    else{
        p_ctrl->rx_err_cnt  = 0;
    }

#if UARTBUF_RXOVER_STYLE == UARTBUF_RXOVER_STOP
    dtcrx_recv(p_ctrl);
#endif
    return (data - (uint8_t*)p_dest);
}

int R_SCI_UARTBUF_Write(uartbuf_ctrl_t * const p_ctrl, void const* p_src, unsigned bytes){
#if (SCI_UART_CFG_PARAM_CHECKING_ENABLE)
    FSP_ASSERT(p_ctrl->txstore !=NULL);
    FSP_ASSERT(p_ctrl->txbuf.mask > 0);
#endif
    FSP_ASSERT(p_src != NULL);
    trace_uart_target(p_ctrl);

    if (p_ctrl->txstore == NULL)
        return bytes;

    const uint8_t* data = (const uint8_t*) p_src;
    volatile struct ringbuf16index* vbuf = &p_ctrl->txbuf;
    struct ringbuf16index* buf = (struct ringbuf16index*)vbuf;

    unsigned have = ringbuf16index_put_free(buf);
    if (have > bytes)
        have = bytes;
    else if (bytes <= buf->mask)
        if (dtctx_update(p_ctrl) >0) {
            have = safe_ringbuf16index_put_free(buf);
            if (have > bytes)
                have = bytes;
        }

    if (have > 0){
        memcpy(p_ctrl->txstore + buf->put_ptr , data, have);
        ringbuf16index_putn(buf, have);
        data += have;
        bytes -= have;
        // start transfer here
        safe_dtctx_send(p_ctrl);
    }
    else
        return 0;

    if (bytes <= 0)
        return (data - (uint8_t*)p_src);

    have = safe_ringbuf16index_put_free(buf);

    if (have > bytes)
        have = bytes;
    if (have > 0){
        memcpy(p_ctrl->txstore + buf->put_ptr , data, have);

        ringbuf16index_putn(buf, have);
        data += have;

        // start transfer here
        safe_dtctx_send(p_ctrl);
    }
    return (data - (uint8_t*)p_src);
}

int R_SCI_UARTBUF_Putc(uartbuf_ctrl_t * const p_ctrl, uint8_t x){
#if (SCI_UART_CFG_PARAM_CHECKING_ENABLE)
    FSP_ASSERT(p_ctrl->txstore !=NULL);
    FSP_ASSERT(p_ctrl->txbuf.mask > 0);
#endif

    if (p_ctrl->txstore == NULL)
        return 1;

    trace_uart_target(p_ctrl);

    struct ringbuf16index* buf = (struct ringbuf16index*)&p_ctrl->txbuf;
    if (!ringbuf16index_full(buf)){
        p_ctrl->txstore[buf->put_ptr] = x;
        ringbuf16index_put(buf);
        // start transfer here
        safe_dtctx_send(p_ctrl);
        return 1;
    }
    else
        return 0;
}

int R_SCI_UARTBUF_Getc(uartbuf_ctrl_t * const p_ctrl){
#if (SCI_UART_CFG_PARAM_CHECKING_ENABLE)
    FSP_ASSERT(p_ctrl->rxstore !=NULL);
    FSP_ASSERT(p_ctrl->rxbuf.mask > 0);
#endif

    if (p_ctrl->rxstore == NULL)
        return UARTBUF_EVENT_EMPTY;

    trace_uart_target(p_ctrl);

    dtcrx_update(p_ctrl);

    struct ringbuf16index* buf = (struct ringbuf16index*)&p_ctrl->rxbuf;
    if (!ringbuf16index_empty(buf)){

        int x = p_ctrl->rxstore[buf->get_ptr];
        ringbuf16index_get(buf);
        dtcrx_recv(p_ctrl);

        return x;
    }
    else
        return UARTBUF_EVENT_EMPTY;
}


void r_sci_uartbuf_onevent(uart_callback_args_t * args){
    trace_uisr_on();
    uartbuf_ctrl_t * p_ctrl =  (uartbuf_ctrl_t *)args->p_context;
    ASSERT(p_ctrl != NULL);

    trace_uart_target(p_ctrl);

    switch(args->event){
        case UART_EVENT_TX_DATA_EMPTY:
            // TODO: r_sci_uart disables TXI there, so last byte TXE will be at UART_EVENT_TX_COMPLETE
            //       if write to TXD before that, last byte will loose!
            if (0)  // do not expose it to app
            if (p_ctrl->on_sent)
                p_ctrl->on_sent(p_ctrl->p_context, p_ctrl, UARTBUF_EVENT_EMPTY) ;// invoke event
            break;

        case UART_EVENT_TX_COMPLETE:
            trace_uisr_txc_on();
            if (p_ctrl->txstore != NULL) {
                struct ringbuf16index* buf = (struct ringbuf16index*)&p_ctrl->txbuf;
                if (p_ctrl->posted > 0){
                    // NOTE: после Abort тут возникает рассогласование DMA<->txbuf.get
                    //      это может дропнуть из буфера неотосланый остаток посылки
                    ringbuf16index_getn(buf, p_ctrl->posted);
                    p_ctrl->posted = 0;
                }
                //dtctx_update(p_ctrl);
                if ( !ringbuf16index_empty(buf) ){
                    dtctx_send(p_ctrl);
                    break;
                }
            }
            if (p_ctrl->on_sent)
                p_ctrl->on_sent(p_ctrl->p_context, p_ctrl, UARTBUF_EVENT_TX_COMPLETE) ;// invoke event
            break;

        case UART_EVENT_RX_COMPLETE:
            trace_uisr_rxc_on();
            if (p_ctrl->rxstore != NULL) {
                struct ringbuf16index* buf = (struct ringbuf16index*)&p_ctrl->rxbuf;
#if UARTBUF_RXOVER_STYLE == UARTBUF_RXOVER_STOP
                dtcrx_update(p_ctrl);
                if ( !ringbuf16index_full(buf) ){
                    if (dtcrx_recv(p_ctrl) > 0)
                        break;
                }
                else if (p_ctrl->on_recv)
                    p_ctrl->on_recv(p_ctrl->p_context, p_ctrl, UARTBUF_EVENT_FULL) ;// invoke event
#else
                if ( ringbuf16index_full(buf) )
                if (p_ctrl->on_recv)
                    p_ctrl->on_recv(p_ctrl->p_context, p_ctrl, UARTBUF_EVENT_FULL) ;// invoke event
                dtcrx_recv(p_ctrl);
#endif
            }
            if (p_ctrl->on_recv)
                p_ctrl->on_recv(p_ctrl->p_context, p_ctrl, UARTBUF_EVENT_RX_COMPLETE) ;// invoke event
            break;

        case UART_EVENT_RX_CHAR:
            trace_uisr_rx_on();
            if (p_ctrl->rxstore != NULL)
            {
                struct ringbuf16index* buf = (struct ringbuf16index*)&p_ctrl->rxbuf;
                {
                    // this no DMA operation, of FULL buf
                    int i = ringbuf16index_peek_put(buf);
                    if (i >= 0){
                        p_ctrl->rxstore[i] = args->data;
                        ringbuf16index_put(buf);
                        if ( p_ctrl->rx_dma != NULL ){
                            transfer_info_t * p_info = p_ctrl->rx_dma;
                            //this should fix DMA dest after overloop, so next time dtcrx_update will be correct
                            p_info->p_dest = p_ctrl->rxstore + buf->put_ptr;
                        }
                    }
#if UARTBUF_RXOVER_STYLE == UARTBUF_RXOVER_STOP
                    if ( !ringbuf16index_full(buf) ){
                        if (dtcrx_recv(p_ctrl) > 0)
                            break;
                    }
                    else if (p_ctrl->on_recv)
                        p_ctrl->on_recv(p_ctrl->p_context, p_ctrl, UARTBUF_EVENT_FULL) ;// invoke event
#else
                    if (i < 0)
                    if (p_ctrl->on_recv)
                        p_ctrl->on_recv(p_ctrl->p_context, p_ctrl, UARTBUF_EVENT_FULL) ;// invoke event
                    //restart DMA operation
                    if (dtcrx_recv(p_ctrl) > 0)
                        break;
#endif
                }
            }
            if (p_ctrl->on_recv)
                p_ctrl->on_recv(p_ctrl->p_context, p_ctrl, args->data) ;// invoke event
            break;

        case UART_EVENT_BREAK_DETECT:
        case UART_EVENT_ERR_PARITY:
        case UART_EVENT_ERR_FRAMING:
        case UART_EVENT_ERR_OVERFLOW:
        default:
            dtcrx_update(p_ctrl);
            ++p_ctrl->rx_err_cnt;
            if (p_ctrl->on_recv)
                p_ctrl->on_recv(p_ctrl->p_context, p_ctrl, (UARTBUF_EVENT_ERR | args->event) ) ;
            break;
    };

    trace_uisr_txc_off();
    trace_uisr_rxc_off();
    trace_uisr_rx_off();
    trace_uisr_off();
}

bool R_SCI_UARTBUF_Flush(uartbuf_ctrl_t * const p_ctrl){
    sci_uart_instance_ctrl_t* uart = (sci_uart_instance_ctrl_t*)p_ctrl->uart_ctrl;
    if (uart == NULL)
        return true;

    if (ringbuf16index_empty((struct ringbuf16index*)&p_ctrl->txbuf)){
        if (uart == NULL)
            return true;

        return ( ( UART_GetFlagStatus( uart->p_reg ) & UART_FLAG_TXC) != 0 );
    }
    return false;
}


static
void R_SCI_UARTBUF_DTCAbort (uartbuf_ctrl_t * p_ctrl, uart_dir_t communication_to_abort){
    if ( (communication_to_abort & UART_DIR_TX) != 0) {

        if ( p_ctrl->tx_dma != NULL ){
            transfer_info_t * p_info = p_ctrl->tx_dma;
            p_info->length = 0;
        }
        dtctx_update(p_ctrl);
        p_ctrl->posted = 0;
        // if during dtctx_update, have sent by ISR
        if ( p_ctrl->tx_dma != NULL ){
            transfer_info_t * p_info = p_ctrl->tx_dma;
            p_info->length = 0;
        }

    } //if ( (communication_to_abort & UART_DIR_TX)

    if ( (communication_to_abort & UART_DIR_RX) != 0) {
        dtcrx_update(p_ctrl);
        if ( p_ctrl->rx_dma != NULL ){
            transfer_info_t * p_info = p_ctrl->rx_dma;
            p_info->length = 0;
        }
    }
}

static
fsp_err_t R_SCI_UARTBUF_Abort (uartbuf_ctrl_t * p_ctrl, uart_dir_t communication_to_abort){
    if (p_ctrl->uart_ctrl != NULL) {
        dtctx_update(p_ctrl);
        int ok = R_SCI_UART_Abort(p_ctrl->uart_ctrl, UART_DIR_TX);
        p_ctrl->posted = 0;
        if (ok != FSP_SUCCESS)
            return ok;
    }
    else
        return FSP_SUCCESS;

    R_SCI_UARTBUF_DTCAbort(p_ctrl, communication_to_abort);
    return FSP_SUCCESS;
}

fsp_err_t R_SCI_UARTBUF_Drop(uartbuf_ctrl_t * const p_ctrl, uart_dir_t communication_to_abort){
    if (communication_to_abort & UART_DIR_RX){
        dtcrx_update(p_ctrl);
        volatile
        struct ringbuf16index* buf = &p_ctrl->rxbuf;
        buf->get_ptr = buf->put_ptr;
        p_ctrl->rx_err_cnt  = 0;
        dtcrx_recv(p_ctrl);
    }
    if (communication_to_abort & UART_DIR_TX){
        R_SCI_UARTBUF_Abort(p_ctrl, UART_DIR_TX);
        volatile
        struct ringbuf16index* buf = &p_ctrl->txbuf;
        buf->get_ptr = buf->put_ptr;
    }
    return FSP_SUCCESS;
}

fsp_err_t R_SCI_UARTBUF_CallbackSet(uartbuf_ctrl_t * const p_ctrl, void* const p_context
                                , uartbuf_on_recv_t on_recv, uartbuf_on_sent_t on_sent
                                )
{
    p_ctrl->on_recv   = on_recv;
    p_ctrl->on_sent   = on_sent;
    p_ctrl->p_context = p_context;
    return FSP_SUCCESS;
}


static
void R_SCI_UARTBUF_init_dtc(uartbuf_ctrl_t * p_ctrl){
    transfer_instance_t const * dtdma = p_ctrl->uart_cfg->p_transfer_tx;
    if ( dtdma != NULL ){
        p_ctrl->tx_dma = dtdma->p_cfg->p_info;
    }
    else
        p_ctrl->tx_dma = NULL;

    transfer_instance_t const * drdma = p_ctrl->uart_cfg->p_transfer_rx;
    if ( drdma != NULL ){
        p_ctrl->rx_dma = drdma->p_cfg->p_info;
    }
    else
        p_ctrl->rx_dma = NULL;
}

fsp_err_t R_SCI_UARTBUF_Open(uartbuf_ctrl_t * const p_ctrl, uartbuf_cfg_t const * const p_cfg){
    int ok = FSP_SUCCESS;
    if ((void*)p_ctrl != (void*)p_cfg)
        memcpy(p_ctrl, p_cfg, sizeof(*p_cfg));

    struct ringbuf16index* rxbuf = (struct ringbuf16index*)&p_ctrl->rxbuf;
    ringbuf16index_init(rxbuf, ringbuf16index_size(rxbuf) );
    struct ringbuf16index* txbuf = (struct ringbuf16index*)&p_ctrl->txbuf;
    ringbuf16index_init(txbuf, ringbuf16index_size(txbuf) );
    p_ctrl->posted = 0;
    p_ctrl->rx_err_cnt = 0;


    ASSERT(p_ctrl->uart_ctrl != NULL);
    ok = R_SCI_UART_Open(p_ctrl->uart_ctrl, p_ctrl->uart_cfg);
    if (ok != FSP_SUCCESS)
        return ok;

    R_SCI_UARTBUF_init_dtc(p_ctrl);

    ok = R_SCI_UART_CallbackSet( p_ctrl->uart_ctrl, (r_sci_uartbuf_onevent), p_ctrl, NULL);
    if (ok != FSP_SUCCESS)
        return ok;

    //start DMA receive
    dtcrx_recv(p_ctrl);

    return ok;
}

fsp_err_t R_SCI_UARTBUF_Close(uartbuf_ctrl_t * const p_ctrl){
    return R_SCI_UART_Close(p_ctrl->uart_ctrl);
}


fsp_err_t R_SCI_UARTBUF_CallbackMode(uartbuf_ctrl_t * const p_ctrl, UARTBufEventMode mode){
    (void)mode;
    sci_uart_instance_ctrl_t* uart = (sci_uart_instance_ctrl_t*)p_ctrl->uart_ctrl;
    ASSERT(uart != NULL);
#if 0
    transfer_instance_t const * dtcrx = p_ctrl->uart_cfg->p_transfer_rx;
    if (dtcrx != NULL){
        transfer_info_t * p_info = dtcrx->p_cfg->p_info;
        ASSERT(p_info != NULL);
        p_info->irq = (mode & UARTBUF_EVMODE_RX_CHAR);
    }
#endif
    return FSP_SUCCESS;
}


void r_sci_uart_config_set (sci_uart_instance_ctrl_t * const p_ctrl, uart_cfg_t const * const p_cfg);

fsp_err_t R_SCI_UARTBUF_Over(uartbuf_ctrl_t * p_ctrl, uart_ctrl_t * p_uart_over){
    int ok = FSP_SUCCESS;

    if (p_ctrl->uart_ctrl)
        ok = R_SCI_UARTBUF_Abort(p_ctrl, UART_DIR_RX_TX);
    else
        R_SCI_UARTBUF_DTCAbort(p_ctrl, UART_DIR_RX_TX);
    p_ctrl->uart_ctrl = NULL;

    if (p_uart_over){
        R_SCI_UARTBUF_init_dtc(p_ctrl);

        // restore mode and baud
        r_sci_uart_config_set( p_uart_over, p_ctrl->uart_cfg );

        ok = R_SCI_UART_CallbackSet( p_uart_over, (r_sci_uartbuf_onevent), p_ctrl, NULL);
    }
    p_ctrl->uart_ctrl = p_uart_over;

    return ok;
}

/// @return >= 0 - writen data amount
void R_SCI_UARTBUF_Go(uartbuf_ctrl_t * const p_ctrl){

    //start DMA receive
    dtcrx_recv(p_ctrl);

    struct ringbuf16index* buf = (struct ringbuf16index*)&p_ctrl->txbuf;
    if (!ringbuf16index_empty(buf)){

        if (p_ctrl->posted > 0) {
            // to force rending, cleanup last sent
            dtctx_update(p_ctrl);
            p_ctrl->posted = 0;
        }

        // start transfer here
        safe_dtctx_send(p_ctrl);
    }
}



/***********************************************************************************************************************
 * Typedef definitions
 **********************************************************************************************************************/
typedef struct st_baud_setting_const_t
{
    uint8_t bgdm  : 1;                 /**< BGDM value to get divisor */
    uint8_t abcs  : 1;                 /**< ABCS value to get divisor */
    uint8_t abcse : 1;                 /**< ABCSE value to get divisor */
    uint8_t cks   : 2;                 /**< CKS  value to get divisor (CKS = N) */
} baud_setting_const_t;


/*******************************************************************************************************************//**
 * Changes baud rate based on predetermined register settings.
 *
 * @param[in]  p_sci_reg       Base pointer for SCI registers
 * @param[in]  p_baud_setting  Pointer to other divisor related settings
 *
 * @note       The transmitter and receiver (TE and RE bits in SCR) must be disabled prior to calling this function.
 **********************************************************************************************************************/
/* SCI SMR register bit masks */
#define SCI_SMR_CKS_VALUE_MASK                  (0x03U) ///< CKS: 2 bits

/* SCI SEMR register bit offsets */
#define SCI_UART_SEMR_BRME_OFFSET               (2U)
#define SCI_UART_SEMR_ABCSE_OFFSET              (3U)
#define SCI_UART_SEMR_ABCS_OFFSET               (4U)
#define SCI_UART_SEMR_BGDM_OFFSET               (6U)
#define SCI_UART_SEMR_BAUD_SETTING_MASK         ((1U << SCI_UART_SEMR_BRME_OFFSET) |  \
                                                 (1U << SCI_UART_SEMR_ABCSE_OFFSET) | \
                                                 (1U << SCI_UART_SEMR_ABCS_OFFSET) | (1U << SCI_UART_SEMR_BGDM_OFFSET))

void r_sci_uart_baud_set (R_SCI0_Type * p_sci_reg, baud_setting_t const * const p_baud_setting)
{
    /* Set BRR register value. */
    p_sci_reg->BRR = p_baud_setting->brr;

    /* Set clock source for the on-chip baud rate generator. */
    p_sci_reg->SMR_b.CKS = (uint8_t) (SCI_SMR_CKS_VALUE_MASK & p_baud_setting->cks);

    /* Set MDDR register value. */
    p_sci_reg->MDDR = p_baud_setting->mddr;

    /* Set clock divisor settings. */
    p_sci_reg->SEMR = (uint8_t) ((p_sci_reg->SEMR & ~(SCI_UART_SEMR_BAUD_SETTING_MASK)) |
                                 (p_baud_setting->semr_baudrate_bits & SCI_UART_SEMR_BAUD_SETTING_MASK));
}



/*******************************************************************************************************************//**
 * Configures UART related registers based on user configurations.
 *
 * @param[in]     p_ctrl  Pointer to UART control structure
 * @param[in]     p_cfg   Pointer to UART specific configuration structure
 **********************************************************************************************************************/

#define SCI_UART_SCMR_DEFAULT_VALUE             (0xF2U)
#define SCI_UART_BRR_DEFAULT_VALUE              (0xFFU)
#define SCI_UART_MDDR_DEFAULT_VALUE             (0xFFU)
#define SCI_UART_FCR_DEFAULT_VALUE              (0xF800)
#define SCI_UART_DCCR_DEFAULT_VALUE             (0x40U)

/* Noise filter setting definition */
typedef enum e_noise_cancel_lvl
{
    NOISE_CANCEL_LVL1,                 /**< Noise filter level 1(weak) */
    NOISE_CANCEL_LVL2,                 /**< Noise filter level 2 */
    NOISE_CANCEL_LVL3,                 /**< Noise filter level 3 */
    NOISE_CANCEL_LVL4                  /**< Noise filter level 4(strong) */
} noise_cancel_lvl_t;



void r_sci_uart_config_set (sci_uart_instance_ctrl_t * const p_ctrl, uart_cfg_t const * const p_cfg)
{
#if SCI_UART_CFG_FIFO_SUPPORT

    /* Configure FIFO related registers. */
    r_sci_uart_fifo_cfg(p_ctrl);
#else

    USART_TypeDef*  p_reg = p_ctrl->p_reg;

    uint8_t save_cr = p_reg->SCR;
    /* Disables transmitter and receiver. This terminates any in-progress transmission. */
    p_reg->SCR = save_cr &  (uint8_t)~( USART_TE | USART_RE);

    /* If fifo support is disabled and the current channel supports fifo make sure it's disabled. */
    if (BSP_FEATURE_SCI_UART_FIFO_CHANNELS & (1U << p_cfg->channel))
    {
        p_reg->FCR = SCI_UART_FCR_DEFAULT_VALUE;
    }
#endif

    /* Configure parity and stop bits. */
    uint32_t smr  = (((uint32_t) p_cfg->parity << 4U) | ((uint32_t) p_cfg->stop_bits << 3U));
    uint32_t scmr = SCI_UART_SCMR_DEFAULT_VALUE;

    /* Configure data size. */
    if (UART_DATA_BITS_7 == p_cfg->data_bits)
    {
        /* Set the SMR.CHR bit & SCMR.CHR1 bit as selected (Character Length)
         *  Character Length
         *  (CHR1,CHR)
         *  (1, 1) Transmit/receive in 7-bit data length*3
         */
        smr |= (1U << 6);
    }
    else if (UART_DATA_BITS_9 == p_cfg->data_bits)
    {
        /* Set the SMR.CHR bit & SCMR.CHR1 bit as selected (Character Length)
         *  Character Length
         *  (CHR1,CHR)
         *  (0, 0) Transmit/receive in 9-bit data length
         */
        scmr &= ~(1U << 4);
    }
    else
    {
        /* Do nothing.  Default is 8-bit mode. */
    }

    /* Write to the SMR register. */
    p_reg->SMR = (uint8_t) smr;

    /* Write to the SCMR register. */
    p_reg->SCMR = (uint8_t) scmr;

    sci_uart_extended_cfg_t * p_extend = (sci_uart_extended_cfg_t *) p_cfg->p_extend;

    /* Configure CTS flow control if CTS/RTS flow control is enabled. */
    p_reg->SPMR = ((uint8_t) (p_extend->flow_control << R_SCI0_SPMR_CTSE_Pos) & R_SCI0_SPMR_CTSE_Msk);

    uint32_t semr = 0;

    /* Starts reception on falling edge of RXD if enabled in extension (otherwise reception starts at low level
     * of RXD). */
    semr |= (p_extend->rx_edge_start & 1U) << 7;

    /* Enables the noise cancellation, fixed to the minimum level, if enabled in the extension. */
    semr |= (p_extend->noise_cancel & 1U) << 5;

    p_reg->SNFR = NOISE_CANCEL_LVL1;

    if ((SCI_UART_CLOCK_EXT8X == p_extend->clock) || (SCI_UART_CLOCK_EXT16X == p_extend->clock))
    {
        /* Use external clock for baud rate */
        p_reg->BRR = SCI_UART_BRR_DEFAULT_VALUE;

        if (SCI_UART_CLOCK_EXT8X == p_extend->clock)
        {
            /* Set baud rate as (external clock / 8) */
            semr |= 1U << SCI_UART_SEMR_ABCS_OFFSET;
        }

        p_reg->SEMR = (uint8_t) semr;
    }
    else
    {
        p_reg->SEMR = (uint8_t) semr;

        /* Set the baud rate settings for the internal baud rate generator. */
        r_sci_uart_baud_set(p_reg, p_extend->p_baud_setting);
    }

    // enable UART activity back
    //p_reg->SCR |= save_cr & ( USART_TE | USART_RE);
    p_reg->SCR = save_cr;
}

#if SCI_UART_CFG_FIFO_SUPPORT

/*******************************************************************************************************************//**
 * Resets FIFO related registers.
 *
 * @param[in] p_ctrl  Pointer to UART instance control
 * @param[in] p_cfg   Pointer to UART configuration structure
 **********************************************************************************************************************/
static void r_sci_uart_fifo_cfg (sci_uart_instance_ctrl_t * const p_ctrl)
{
    if (0U != p_ctrl->fifo_depth)
    {
        /* Enable the fifo and set the tx and rx reset bits */
        uint32_t fcr = 1U;

 #if (SCI_UART_CFG_RX_ENABLE)
  #if SCI_UART_CFG_DTC_SUPPORTED

        /* If DTC is used keep the receive trigger at the default level of 0. */
        if (NULL == p_ctrl->p_cfg->p_transfer_rx)
  #endif
        {
            /* Otherwise, set receive trigger number as configured by the user. */
            sci_uart_extended_cfg_t const * p_extend = p_ctrl->p_cfg->p_extend;

            /* RTRG(Receive FIFO Data Trigger Number) controls when the RXI interrupt will be generated. If data is
             * received but the trigger number is not met the RXI interrupt will be generated after 15 ETUs from
             * the last stop bit in asynchronous mode. For more information see the FIFO Selected section of "Serial
             * Data Reception in Asynchronous Mode" in the RA6M3 manual R01UH0886EJ0100 or the relevant section for
             * the MCU being used. */
            fcr |= (((p_ctrl->fifo_depth - 1U) & p_extend->rx_fifo_trigger) & SCI_UART_FCR_TRIGGER_MASK) <<
                   SCI_UART_FCR_RTRG_OFFSET;
        }

        /* RTS asserts when the amount of received data stored in the fifo is equal or less than this value. */
        fcr |= ((p_ctrl->fifo_depth - 1U) & SCI_UART_FCR_TRIGGER_MASK) << SCI_UART_FCR_RSTRG_OFFSET;
 #endif

        /* Set the FCR and reset the fifos. */
        p_ctrl->p_reg->FCR = (uint16_t) (fcr | SCI_UART_FCR_RESET_TX_RX);

        /* Wait for the fifo reset to complete after 1 PCLK according to section 34.2.26 "FIFO Control Register (FCR)
         * in the RA6M3 manual R01UH0886EJ0100 or the relevant section for the MCU being used.*/
        FSP_HARDWARE_REGISTER_WAIT(p_ctrl->p_reg->FCR, fcr);
    }
}

#endif
