//*****************************************************************************
//
// u2c_uart.c
//
// Copyright (c) 2020 Texas Instruments Incorporated.  All rights reserved.
// Software License Agreement
//
// Texas Instruments (TI) is supplying this software for use solely and
// exclusively on TI's microcontroller products. The software is owned by
// TI and/or its suppliers, and is protected under applicable copyright
// laws. You may not combine this software with "viral" open-source
// software in order to form a larger program.
//
// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS.
// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT
// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY
// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
// DAMAGES, FOR ANY REASON WHATSOEVER.
//
//*****************************************************************************

#include <stdbool.h>
#include <stdint.h>
#include "inc/hw_can.h"
#include "inc/hw_ints.h"
#include "inc/hw_memmap.h"
#include "inc/hw_types.h"
#include "driverlib/can.h"
#include "driverlib/gpio.h"
#include "driverlib/interrupt.h"
#include "driverlib/pin_map.h"
#include "driverlib/sysctl.h"
#include "driverlib/uart.h"
#include "driverlib/rom.h"
#include "driverlib/rom_map.h"
#include "bl_can.h"
#include "bl_commands.h"
#include "u2c_uart.h"

uint32_t CheckSum(const uint8_t *pui8Data, uint32_t ui32Size);

void UART2IntHandler(void)
{
    static int count, receiveCount;
    int data;

    if(eUartState == waiting)
    {
        receiveCount = MAP_UARTCharGetNonBlocking(UART2_BASE);
        count = 0;
        if(receiveCount > 2)
        {
            g_receiveCount = receiveCount - 2;
            eUartState = receiving;
        }
    }
    while((data = MAP_UARTCharGetNonBlocking(UART2_BASE)) != -1)
    {
        g_ui8UartBuffer[count++] = (uint8_t)data;
        if(count == receiveCount - 1)
        {
            // Check checksum, checksum is first byte
            if(g_ui8UartBuffer[0] != CheckSum(g_ui8UartBuffer + 1, count - 1 ))
            {
                NakPacket();
                eUartState = waiting;
            }
            else
            {
                eUartState = finished;
                MAP_IntDisable(INT_UART2);
                AckPacket();
            }
            break;
        }
    }
    MAP_UARTIntClear(UART2_BASE,  UART_INT_RX | UART_INT_RT);
}

//*****************************************************************************
//
// This function sets up UART2 to be used for a console to display information
// and communicate with LMFlashProgrammer.
//
//*****************************************************************************
void InitConsole(void)
{
    //
    // Enable the GPIO Peripheral used by the UART.
    //
    MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOD);

    //
    // Enable UART2
    //
    MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_UART2);

    //
    // Configure GPIO Pins for UART mode.
    //
    MAP_GPIOPinConfigure(GPIO_PD4_U2RX);
    MAP_GPIOPinConfigure(GPIO_PD5_U2TX);
    MAP_GPIOPinTypeUART(GPIO_PORTD_BASE, GPIO_PIN_4 | GPIO_PIN_5);

    //
    // Enable UART2 so that we can configure the clock.
    //
    MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_UART2);

    //
    // Use the internal 16MHz oscillator as the UART clock source.
    //
    MAP_UARTClockSourceSet(UART2_BASE, UART_CLOCK_PIOSC);

    //
    // Select the alternate (UART) function for these pins.
    // TODO: change this to select the port/pin you are using.
    //
    MAP_GPIOPinTypeUART(GPIO_PORTD_BASE, GPIO_PIN_4 | GPIO_PIN_5);

    //
    // Initialize the UART
    //
    MAP_UARTConfigSetExpClk(UART2_BASE, 16000000u, 115200u,
                        UART_CONFIG_WLEN_8 | UART_CONFIG_STOP_ONE | UART_CONFIG_PAR_NONE);
    MAP_UARTFIFOEnable(UART2_BASE);
    MAP_UARTFIFOLevelSet(UART2_BASE, UART_FIFO_TX7_8, UART_FIFO_RX4_8);
    MAP_UARTIntEnable(UART2_BASE, UART_INT_RX | UART_INT_RT);
    UARTIntRegister(UART2_BASE, UART2IntHandler);
    MAP_IntEnable(INT_UART2);
}


void UARTgets(uint8_t *pui8Data, uint32_t ui32Size)
{
    while(ui32Size--)
    {
        *pui8Data++ = MAP_UARTCharGet(UART2_BASE);
    }
}

//*****************************************************************************
//
//! Calculates an 8-bit checksum
//!
//! \param pui8Data is a pointer to an array of 8-bit data of size ui32Size.
//! \param ui32Size is the size of the array that will run through the checksum
//! algorithm.
//!
//! This function simply calculates an 8-bit checksum on the data passed in.
//!
//! \return Returns the calculated checksum.
//
//*****************************************************************************
uint32_t CheckSum(const uint8_t *pui8Data, uint32_t ui32Size)
{
    uint32_t ui32CheckSum;

    //
    // Initialize the checksum to zero.
    //
    ui32CheckSum = 0;

    //
    // Add up all the bytes, do not do anything for an overflow.
    //
    while(ui32Size--)
    {
        ui32CheckSum += *pui8Data++;
    }

    //
    // Return the caculated check sum.
    //
    return(ui32CheckSum & 0xff);
}

//*****************************************************************************
//
//! Sends an Acknowledge packet.
//!
//! This function is called to acknowledge that a packet has been received by
//! the microcontroller.
//!
//! \return None.
//
//*****************************************************************************
void AckPacket(void)
{
    //
    // ACK/NAK packets are the only ones with no size.
    //
    MAP_UARTCharPut(UART2_BASE, COMMAND_ACK);
}

//*****************************************************************************
//
//! Sends a no-acknowledge packet.
//!
//! This function is called when an invalid packet has been received by the
//! microcontroller, indicating that it should be retransmitted.
//!
//! \return None.
//
//*****************************************************************************
void NakPacket(void)
{
    //
    // ACK/NAK packets are the only ones with no size.
    //
    MAP_UARTCharPut(UART2_BASE, COMMAND_NAK);
}

void sendStatus(uint8_t status)
{
    MAP_UARTCharPut(UART2_BASE, 3);
    MAP_UARTCharPut(UART2_BASE, status);
    MAP_UARTCharPut(UART2_BASE, status);
    MAP_UARTCharGet(UART2_BASE); // read and discard 0xCC response
}

int ReceiveUartPacket(uint8_t *pui8Data, uint32_t ui32MaxSize)
{
    uint32_t ui32Size, ui32CheckSum;
    ui32Size = 0;
    while(ui32Size == 0)
    {
        ui32Size = MAP_UARTCharGet(UART2_BASE);
    }

    //
    // Subtract off the size and checksum bytes.
    //
    ui32Size -= 2;

    //
    // Receive the checksum followed by the actual data.
    //
    ui32CheckSum = MAP_UARTCharGet(UART2_BASE);

    //
    // If there is room in the buffer then receive the requested data.
    //
    if(ui32MaxSize >= ui32Size)
    {
        //
        // Receive the actual data in the packet.
        //
        UARTgets(pui8Data, ui32Size);

        //
        // Send a no acknowledge if the checksum does not match, otherwise send
        // an acknowledge to the packet later.
        //
        if(CheckSum(pui8Data, ui32Size) != (ui32CheckSum & 0xff))
        {
            //
            // Packet was not received, there is no valid data in the buffer.
            //
            return(-1);
        }
    }
    else
    {
        //
        // If the caller allocated a buffer that was too small for the received
        // data packet, receive it but don't fill the buffer.
        // Then inform the caller that the packet was not received correctly.
        //
        while(ui32Size--)
        {
            MAP_UARTCharGet(UART2_BASE);
        }

        //
        // Packet was not received, there is no valid data in the buffer.
        //
        return(-1);
    }

    //
    // Packet was received successfully.
    // Make sure to return the number of bytes received.
    //
    return(ui32Size);
}

