/*
 * ll_dmadrivenusartcore.cpp
 *
 *  Created on: Nov 4, 2019
 *      Author: abody
 */

#include <f4ll/usart_core.h>

namespace f4ll {

usart_core::usart_core(USART_TypeDef *usart, DMA_TypeDef *dma, uint32_t streamRx, uint32_t streamTx)
    : m_usart(usart),
      m_rx_dma(dma, streamRx),
      m_tx_dma(dma, streamTx)
{
    uint32_t status = usart->SR;
    volatile uint32_t tmpreg = usart->DR; // clearing some of the error/status bits in the USART
    (void)tmpreg;
    (void)status;

    *m_tx_dma.get_ifc_reg() =
        m_tx_dma.get_tc_mask() | m_rx_dma.get_ht_mask() | m_tx_dma.get_te_mask() | m_rx_dma.get_fe_mask() | m_rx_dma.get_dme_mask();
    *m_rx_dma.get_ifc_reg() =
        m_rx_dma.get_tc_mask() | m_rx_dma.get_ht_mask() | m_rx_dma.get_te_mask() | m_rx_dma.get_fe_mask() | m_rx_dma.get_dme_mask();

    LL_DMA_EnableIT_TC(dma, streamRx);
    LL_DMA_EnableIT_TE(dma, streamRx);
    LL_DMA_EnableIT_TC(dma, streamTx);
    LL_DMA_EnableIT_TE(dma, streamTx);
}

void usart_core::usart_isr()
{
    uint32_t status = m_usart->SR;
    volatile uint32_t tmpreg = m_usart->DR; // clearing some of the error/status bits in the HW
    (void)tmpreg;

    if (LL_USART_IsEnabledIT_TC(m_usart) && LL_USART_IsActiveFlag_TC(m_usart)) { // transmission complete
        LL_USART_DisableIT_TC(m_usart);
        transmission_complete();
    }
    if (LL_USART_IsEnabledIT_IDLE(m_usart) && (status & USART_SR_IDLE)) {
        receiver_idle();
    }
    if (LL_USART_IsEnabledIT_ERROR(m_usart)) {
        if (status & USART_SR_FE) {
            framing_error();
        }
        if (status & USART_SR_ORE) {
            overrun();
        }
    }
}

void usart_core::rx_dma_isr()
{
    if (*m_rx_dma.get_is_reg() & m_rx_dma.get_tc_mask()) {
        *m_rx_dma.get_ifc_reg() = m_rx_dma.get_tc_mask();
        if (m_rx_dma.is_enabled_it_tc()) {
            rx_dma_transfer_complete();
        }
    }
    if (*m_rx_dma.get_is_reg() & m_rx_dma.get_ht_mask()) {
        *m_rx_dma.get_ifc_reg() = m_rx_dma.get_ht_mask();
        if (m_rx_dma.is_enabled_it_ht()) {
            rx_dma_half_transfer();
        }
    }
    if (*m_rx_dma.get_is_reg() & m_rx_dma.get_te_mask()) {
        *m_rx_dma.get_ifc_reg() = m_rx_dma.get_te_mask();
        if (m_rx_dma.is_enabled_it_te()) {
            rx_dma_error(dma_helper::dma_error_type::transfer);
        }
    }
    if (*m_rx_dma.get_is_reg() & m_rx_dma.get_fe_mask()) {
        *m_rx_dma.get_ifc_reg() = m_rx_dma.get_fe_mask();
        if (m_rx_dma.is_enabled_it_fe()) {
            rx_dma_error(dma_helper::dma_error_type::fifo);
        }
    }
    if (*m_rx_dma.get_is_reg() & m_rx_dma.get_dme_mask()) {
        *m_rx_dma.get_ifc_reg() = m_rx_dma.get_dme_mask();
        if (m_rx_dma.is_enabled_it_dme()) {
            rx_dma_error(dma_helper::dma_error_type::direct_mode);
        }
    }
}

void usart_core::tx_dma_isr()
{
    if (*m_tx_dma.get_is_reg() & m_tx_dma.get_tc_mask()) { // DMA transfer complete
        *m_tx_dma.get_ifc_reg() = m_tx_dma.get_tc_mask();
        if (m_tx_dma.is_enabled_it_tc()) {
            tx_dma_transfer_complete();
        }
    }
    if (*m_tx_dma.get_is_reg() & m_tx_dma.get_ht_mask()) {
        *m_tx_dma.get_ifc_reg() = m_tx_dma.get_ht_mask();
        if (m_tx_dma.is_enabled_it_ht()) {
            tx_dma_half_transfer();
        }
    }
    if (*m_tx_dma.get_is_reg() & m_tx_dma.get_te_mask()) {
        *m_tx_dma.get_ifc_reg() = m_tx_dma.get_te_mask();
        if (m_tx_dma.is_enabled_it_te()) {
            tx_dma_error(dma_helper::dma_error_type::transfer);
        }
    }
    if (*m_tx_dma.get_is_reg() & m_tx_dma.get_fe_mask()) {
        *m_tx_dma.get_ifc_reg() = m_tx_dma.get_fe_mask();
        if (m_tx_dma.is_enabled_it_fe()) {
            tx_dma_error(dma_helper::dma_error_type::fifo);
        }
    }
    if (*m_tx_dma.get_is_reg() & m_tx_dma.get_dme_mask()) {
        *m_tx_dma.get_ifc_reg() = m_tx_dma.get_dme_mask();
        if (m_tx_dma.is_enabled_it_dme()) {
            tx_dma_error(dma_helper::dma_error_type::direct_mode);
        }
    }
}

void usart_core::setup_transmit(void const *buffer, uint16_t length)
{
    LL_DMA_ConfigAddresses(
        m_tx_dma.get_dma(), m_tx_dma.get_stream(), reinterpret_cast<uint32_t>(buffer), LL_USART_DMA_GetRegAddr(m_usart),
        LL_DMA_DIRECTION_MEMORY_TO_PERIPH);
    LL_DMA_SetDataLength(m_tx_dma.get_dma(), m_tx_dma.get_stream(), length);
    LL_USART_EnableDMAReq_TX(m_usart);
    LL_DMA_EnableStream(m_tx_dma.get_dma(), m_tx_dma.get_stream());
}

void usart_core::setup_receive(void *buffer, uint16_t length)
{
    LL_DMA_ConfigAddresses(
        m_rx_dma.get_dma(), m_rx_dma.get_stream(), LL_USART_DMA_GetRegAddr(m_usart), reinterpret_cast<uint32_t>(buffer),
        LL_DMA_DIRECTION_PERIPH_TO_MEMORY);
    LL_DMA_SetDataLength(m_rx_dma.get_dma(), m_rx_dma.get_stream(), length);
    LL_USART_EnableDMAReq_RX(m_usart);
    LL_USART_ClearFlag_ORE(m_usart);
    LL_DMA_EnableStream(m_rx_dma.get_dma(), m_rx_dma.get_stream());
}

} /* namespace f4ll */
