/*!
 * \file      uart-board.c
 *
 * \brief     Target board UART driver implementation
 *
 * \copyright Revised BSD License, see section \ref LICENSE.
 *
 * \code
 *                ______                              _
 *               / _____)             _              | |
 *              ( (____  _____ ____ _| |_ _____  ____| |__
 *               \____ \| ___ |    (_   _) ___ |/ ___)  _ \
 *               _____) ) ____| | | || |_| ____( (___| | | |
 *              (______/|_____)_|_|_| \__)_____)\____)_| |_|
 *              (C)2013-2017 Semtech
 *
 * \endcode
 *
 * \author    Miguel Luis ( Semtech )
 *
 * \author    Gregory Cristian ( Semtech )
 */
#include "stm32l1xx.h"
#include "utilities.h"
#include "board.h"
#include "sysIrqHandlers.h"
#include "uart-board.h"

/*!
 * Number of times the UartPutBuffer will try to send the buffer before
 * returning ERROR
 */
#define TX_BUFFER_RETRY_COUNT                       10

typedef struct
{
    UART_HandleTypeDef UartHandle;
    uint8_t RxData;
    uint8_t TxData;
}UartContext_t;

UartContext_t UartContext[2];

extern Uart_t Uart1;
extern Uart_t Uart2;

void UartMcuInit( Uart_t *obj, UartId_t uartId, PinNames tx, PinNames rx )
{
    if( uartId == UART_USB_CDC )
    {
#if defined( USE_USB_CDC )
        UartUsbInit( obj, uartId, NC, NC );
#endif
    }
    else
    {
        obj->UartId = uartId;

        if( obj->UartId == UART_1 )
        {
            __HAL_RCC_USART1_FORCE_RESET( );
            __HAL_RCC_USART1_RELEASE_RESET( );
            __HAL_RCC_USART1_CLK_ENABLE( );

            GpioInit( &obj->Tx, tx, PIN_ALTERNATE_FCT, PIN_PUSH_PULL, PIN_PULL_UP, GPIO_AF7_USART1 );
            GpioInit( &obj->Rx, rx, PIN_ALTERNATE_FCT, PIN_PUSH_PULL, PIN_PULL_UP, GPIO_AF7_USART1 );
        }
        else if( obj->UartId == UART_2 )
        {
            __HAL_RCC_USART2_FORCE_RESET( );
            __HAL_RCC_USART2_RELEASE_RESET( );
            __HAL_RCC_USART2_CLK_ENABLE( );

            GpioInit( &obj->Tx, tx, PIN_ALTERNATE_FCT, PIN_PUSH_PULL, PIN_PULL_UP, GPIO_AF7_USART2 );
            GpioInit( &obj->Rx, rx, PIN_ALTERNATE_FCT, PIN_PUSH_PULL, PIN_PULL_UP, GPIO_AF7_USART2 );
        }
    }
}

void UartMcuConfig( Uart_t *obj, UartMode_t mode, uint32_t baudrate, WordLength_t wordLength, StopBits_t stopBits, Parity_t parity, FlowCtrl_t flowCtrl )
{
    if( obj->UartId == UART_USB_CDC )
    {
#if defined( USE_USB_CDC )
        UartUsbConfig( obj, mode, baudrate, wordLength, stopBits, parity, flowCtrl );
#endif
    }
    else
    {
        if( obj->UartId == UART_1 )
        {
            UartContext[obj->UartId].UartHandle.Instance = USART1;
        }
        else if( obj->UartId == UART_2 )
        {
            UartContext[obj->UartId].UartHandle.Instance = USART2;
        }
        UartContext[obj->UartId].UartHandle.Init.BaudRate = baudrate;

        if( mode == TX_ONLY )
        {
            if( obj->FifoTx.Data == NULL )
            {
                assert_param( LMN_STATUS_ERROR );
            }
            UartContext[obj->UartId].UartHandle.Init.Mode = UART_MODE_TX;
        }
        else if( mode == RX_ONLY )
        {
            if( obj->FifoRx.Data == NULL )
            {
                assert_param( LMN_STATUS_ERROR );
            }
            UartContext[obj->UartId].UartHandle.Init.Mode = UART_MODE_RX;
        }
        else if( mode == RX_TX )
        {
            if( ( obj->FifoTx.Data == NULL ) || ( obj->FifoRx.Data == NULL ) )
            {
                assert_param( LMN_STATUS_ERROR );
            }
            UartContext[obj->UartId].UartHandle.Init.Mode = UART_MODE_TX_RX;
        }
        else
        {
            assert_param( LMN_STATUS_ERROR );
        }

        if( wordLength == UART_8_BIT )
        {
            UartContext[obj->UartId].UartHandle.Init.WordLength = UART_WORDLENGTH_8B;
        }
        else if( wordLength == UART_9_BIT )
        {
            UartContext[obj->UartId].UartHandle.Init.WordLength = UART_WORDLENGTH_9B;
        }

        switch( stopBits )
        {
        case UART_2_STOP_BIT:
            UartContext[obj->UartId].UartHandle.Init.StopBits = UART_STOPBITS_2;
            break;
        case UART_1_STOP_BIT:
        default:
            UartContext[obj->UartId].UartHandle.Init.StopBits = UART_STOPBITS_1;
            break;
        }

        if( parity == NO_PARITY )
        {
            UartContext[obj->UartId].UartHandle.Init.Parity = UART_PARITY_NONE;
        }
        else if( parity == EVEN_PARITY )
        {
            UartContext[obj->UartId].UartHandle.Init.Parity = UART_PARITY_EVEN;
        }
        else
        {
            UartContext[obj->UartId].UartHandle.Init.Parity = UART_PARITY_ODD;
        }

        if( flowCtrl == NO_FLOW_CTRL )
        {
            UartContext[obj->UartId].UartHandle.Init.HwFlowCtl = UART_HWCONTROL_NONE;
        }
        else if( flowCtrl == RTS_FLOW_CTRL )
        {
            UartContext[obj->UartId].UartHandle.Init.HwFlowCtl = UART_HWCONTROL_RTS;
        }
        else if( flowCtrl == CTS_FLOW_CTRL )
        {
            UartContext[obj->UartId].UartHandle.Init.HwFlowCtl = UART_HWCONTROL_CTS;
        }
        else if( flowCtrl == RTS_CTS_FLOW_CTRL )
        {
            UartContext[obj->UartId].UartHandle.Init.HwFlowCtl = UART_HWCONTROL_RTS_CTS;
        }

        UartContext[obj->UartId].UartHandle.Init.OverSampling = UART_OVERSAMPLING_16;

        if( HAL_UART_Init( &UartContext[obj->UartId].UartHandle ) != HAL_OK )
        {
            assert_param( LMN_STATUS_ERROR );
        }

        if( obj->UartId == UART_1 )
        {
            HAL_NVIC_SetPriority( USART1_IRQn, 1, 0 );
            HAL_NVIC_EnableIRQ( USART1_IRQn );
        }
        else if( obj->UartId == UART_2 )
        {
            HAL_NVIC_SetPriority( USART2_IRQn, 1, 0 );
            HAL_NVIC_EnableIRQ( USART2_IRQn );
        }

        /* Enable the UART Data Register not empty Interrupt */
        HAL_UART_Receive_IT( &UartContext[obj->UartId].UartHandle, &UartContext[obj->UartId].RxData, 1 );
    }
}

void UartMcuDeInit( Uart_t *obj )
{
    if( obj->UartId == UART_USB_CDC )
    {
#if defined( USE_USB_CDC )
        UartUsbDeInit( obj );
#endif
    }
    else
    {
        if( obj->UartId == UART_1 )
        {
            __HAL_RCC_USART1_FORCE_RESET( );
            __HAL_RCC_USART1_RELEASE_RESET( );
            __HAL_RCC_USART1_CLK_DISABLE( );
        }
        else if( obj->UartId == UART_2 )
        {
            __HAL_RCC_USART2_FORCE_RESET( );
            __HAL_RCC_USART2_RELEASE_RESET( );
            __HAL_RCC_USART2_CLK_DISABLE( );
        }

        GpioInit( &obj->Tx, obj->Tx.pin, PIN_ANALOGIC, PIN_PUSH_PULL, PIN_NO_PULL, 0 );
        GpioInit( &obj->Rx, obj->Rx.pin, PIN_ANALOGIC, PIN_PUSH_PULL, PIN_NO_PULL, 0 );
    }
}

uint8_t UartMcuPutChar( Uart_t *obj, uint8_t data )
{
    if( obj->UartId == UART_USB_CDC )
    {
#if defined( USE_USB_CDC )
        return UartUsbPutChar( obj, data );
#else
        return 255; // Not supported
#endif
    }
    else
    {
        CRITICAL_SECTION_BEGIN( );
        UartContext[obj->UartId].TxData = data;

        if( IsFifoFull( &obj->FifoTx ) == false )
        {
            FifoPush( &obj->FifoTx, UartContext[obj->UartId].TxData );

            // Trig UART Tx interrupt to start sending the FIFO contents.
            __HAL_UART_ENABLE_IT( &UartContext[obj->UartId].UartHandle, UART_IT_TC );

            CRITICAL_SECTION_END( );
            return 0; // OK
        }
        CRITICAL_SECTION_END( );
        return 1; // Busy
    }
}

uint8_t UartMcuGetChar( Uart_t *obj, uint8_t *data )
{
    if( obj->UartId == UART_USB_CDC )
    {
#if defined( USE_USB_CDC )
        return UartUsbGetChar( obj, data );
#else
        return 255; // Not supported
#endif
    }
    else
    {
        CRITICAL_SECTION_BEGIN( );

        if( IsFifoEmpty( &obj->FifoRx ) == false )
        {
            *data = FifoPop( &obj->FifoRx );
            CRITICAL_SECTION_END( );
            return 0;
        }
        CRITICAL_SECTION_END( );
        return 1;
    }
}

uint8_t UartMcuPutBuffer( Uart_t *obj, uint8_t *buffer, uint16_t size )
{
    if( obj->UartId == UART_USB_CDC )
    {
#if defined( USE_USB_CDC )
        return UartUsbPutBuffer( obj, buffer, size );
#else
        return 255; // Not supported
#endif
    }
    else
    {
        uint8_t retryCount;
        uint16_t i;

        for( i = 0; i < size; i++ )
        {
            retryCount = 0;
            while( UartPutChar( obj, buffer[i] ) != 0 )
            {
                retryCount++;

                // Exit if something goes terribly wrong
                if( retryCount > TX_BUFFER_RETRY_COUNT )
                {
                    return 1; // Error
                }
            }
        }
        return 0; // OK
    }
}

uint8_t UartMcuGetBuffer( Uart_t *obj, uint8_t *buffer, uint16_t size, uint16_t *nbReadBytes )
{
    uint16_t localSize = 0;

    while( localSize < size )
    {
        if( UartGetChar( obj, buffer + localSize ) == 0 )
        {
            localSize++;
        }
        else
        {
            break;
        }
    }

    *nbReadBytes = localSize;

    if( localSize == 0 )
    {
        return 1; // Empty
    }
    return 0; // OK
}

void HAL_UART_TxCpltCallback( UART_HandleTypeDef *handle )
{
    Uart_t *uart = &Uart1;
    UartId_t uartId = UART_1;

    if( handle == &UartContext[UART_1].UartHandle )
    {
        uart = &Uart1;
        uartId = UART_1;
    }
    else if( handle == &UartContext[UART_2].UartHandle )
    {
        uart = &Uart2;
        uartId = UART_2;
    }
    else
    {
        // Unknown UART peripheral skip processing
        return;
    }
    if( IsFifoEmpty( &uart->FifoTx ) == false )
    {
        UartContext[uartId].TxData = FifoPop( &uart->FifoTx );
        //  Write one byte to the transmit data register
        HAL_UART_Transmit_IT( &UartContext[uartId].UartHandle, &UartContext[uartId].TxData, 1 );
    }

    if( uart->IrqNotify != NULL )
    {
        uart->IrqNotify( UART_NOTIFY_TX );
    }
}

void HAL_UART_RxCpltCallback( UART_HandleTypeDef *handle )
{
    Uart_t *uart = &Uart1;
    UartId_t uartId = UART_1;

    if( handle == &UartContext[UART_1].UartHandle )
    {
        uart = &Uart1;
        uartId = UART_1;
    }
    else if( handle == &UartContext[UART_2].UartHandle )
    {
        uart = &Uart2;
        uartId = UART_2;
    }
    else
    {
        // Unknown UART peripheral skip processing
        return;
    }
    if( IsFifoFull( &uart->FifoRx ) == false )
    {
        // Read one byte from the receive data register
        FifoPush( &uart->FifoRx, UartContext[uartId].RxData );
    }

    if( uart->IrqNotify != NULL )
    {
        uart->IrqNotify( UART_NOTIFY_RX );
    }

    HAL_UART_Receive_IT( &UartContext[uartId].UartHandle, &UartContext[uartId].RxData, 1 );
}

void HAL_UART_ErrorCallback( UART_HandleTypeDef *handle )
{
    UartId_t uartId = UART_1;

    if( handle == &UartContext[UART_1].UartHandle )
    {
        uartId = UART_1;
    }
    else if( handle == &UartContext[UART_2].UartHandle )
    {
        uartId = UART_2;
    }
    else
    {
        // Unknown UART peripheral skip processing
        return;
    }
    HAL_UART_Receive_IT( &UartContext[uartId].UartHandle, &UartContext[uartId].RxData, 1 );
}

void USART1_IRQHandler( void )
{
    // [BEGIN] Workaround to solve an issue with the HAL drivers not managing the uart state correctly.
    uint32_t tmpFlag = 0, tmpItSource = 0;

    tmpFlag = __HAL_UART_GET_FLAG( &UartContext[UART_1].UartHandle, UART_FLAG_TC );
    tmpItSource = __HAL_UART_GET_IT_SOURCE( &UartContext[UART_1].UartHandle, UART_IT_TC );
    // UART in mode Transmitter end
    if( ( tmpFlag != RESET ) && ( tmpItSource != RESET ) )
    {
        if( ( UartContext[UART_1].UartHandle.State == HAL_UART_STATE_BUSY_RX ) || UartContext[UART_1].UartHandle.State == HAL_UART_STATE_BUSY_TX_RX )
        {
            UartContext[UART_1].UartHandle.State = HAL_UART_STATE_BUSY_TX_RX;
        }
    }
    // [END] Workaround to solve an issue with the HAL drivers not managing the uart state correctly.

    HAL_UART_IRQHandler( &UartContext[UART_1].UartHandle );
}

void USART2_IRQHandler( void )
{
    // [BEGIN] Workaround to solve an issue with the HAL drivers not managing the uart state correctly.
    uint32_t tmpFlag = 0, tmpItSource = 0;

    tmpFlag = __HAL_UART_GET_FLAG( &UartContext[UART_2].UartHandle, UART_FLAG_TC );
    tmpItSource = __HAL_UART_GET_IT_SOURCE( &UartContext[UART_2].UartHandle, UART_IT_TC );
    // UART in mode Transmitter end
    if( ( tmpFlag != RESET ) && ( tmpItSource != RESET ) )
    {
        if( ( UartContext[UART_2].UartHandle.State == HAL_UART_STATE_BUSY_RX ) || UartContext[UART_2].UartHandle.State == HAL_UART_STATE_BUSY_TX_RX )
        {
            UartContext[UART_2].UartHandle.State = HAL_UART_STATE_BUSY_TX_RX;
        }
    }
    // [END] Workaround to solve an issue with the HAL drivers not managing the uart state correctly.

    HAL_UART_IRQHandler( &UartContext[UART_2].UartHandle );
}