//*****************************************************************************
//
// u2c_can.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 "inc/hw_nvic.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_can.h"
#include "driverlib/rom_map.h"

uint8_t g_pui8RxMsgData[8];

void InitCan(void)
{
    //
    // For this example CAN0 is used with RX and TX pins on port E4 and E5.
    // The actual port and pins used may be different on your part, consult
    // the data sheet for more information.
    // GPIO port E needs to be enabled so these pins can be used.
    // TODO: change this to whichever GPIO port you are using
    //
    MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOE);

    //
    // Configure the GPIO pin muxing to select CAN0 functions for these pins.
    // This step selects which alternate function is available for these pins.
    // This is necessary if your part supports GPIO pin function muxing.
    // Consult the data sheet to see which functions are allocated per pin.
    // TODO: change this to select the port/pin you are using
    //
    MAP_GPIOPinConfigure(GPIO_PE4_CAN0RX);
    MAP_GPIOPinConfigure(GPIO_PE5_CAN0TX);

    //
    // Enable the alternate function on the GPIO pins.  The above step selects
    // which alternate function is available.  This step actually enables the
    // alternate function instead of GPIO for these pins.
    // TODO: change this to match the port/pin you are using
    //
    MAP_GPIOPinTypeCAN(GPIO_PORTE_BASE, GPIO_PIN_4 | GPIO_PIN_5);

    //
    // The GPIO port and pins have been set up for CAN.  The CAN peripheral
    // must be enabled.
    //
    MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_CAN0);

    //
    // Initialize the CAN controller
    //
    CANInit(CAN0_BASE);

    //
    // Set up the bit rate for the CAN bus.  This function sets up the CAN
    // bus timing for a nominal configuration.  You can achieve more control
    // over the CAN bus timing by using the function CANBitTimingSet() instead
    // of this one, if needed.
    // In this example, the CAN bus speed is defined .  In the function below,
    // the call to SysCtlClockGet() or ui32SysClock is used to determine the
    // clock rate that is used for clocking the CAN peripheral.  This can be
    // replaced with a  fixed value if you know the value of the system clock,
    // saving the extra function call.  For some parts, the CAN peripheral is
    // clocked by a fixed 8 MHz regardless of the system clock in which case
    // the call to SysCtlClockGet() or ui32SysClock should be replaced with
    // 8000000.  Consult the data sheet for more information about CAN
    // peripheral clocking.
    //
    CANBitRateSet(CAN0_BASE, g_ui32SysClock, CANBAUD);

    //
    // Enable the CAN for operation.
    //
    CANEnable(CAN0_BASE);


}

void SendCANMessage(uint32_t msgId, uint32_t length, uint8_t *pui8Data)
{
    tCANMsgObject sCANMessage;

    sCANMessage.ui32MsgID = msgId;
    sCANMessage.ui32Flags = MSG_OBJ_EXTENDED_ID;
    sCANMessage.ui32MsgLen = length;
    sCANMessage.pui8MsgData = pui8Data;
    CANMessageSet(CAN0_BASE, 0, &sCANMessage, MSG_OBJ_TYPE_TX);

}

void SendCANPing(void)
{
    tCANMsgObject sCANMessage;
    uint32_t timeout;
    //
    // Wait until the previous packet has been sent, providing a time out so
    // that the boot loader does not hang here.
    //
    for(timeout = 1000;
        (timeout != 0) && (HWREG(CAN0_BASE + CAN_O_TXRQ1) != 0);
        timeout--)
    {
    }
    if(timeout !=0)
    {
        // Setup to receive ACK
        CANIntClear(CAN0_BASE, 1u);
        sCANMessage.ui32MsgID = LM_API_UPD_ACK;
        sCANMessage.ui32Flags = MSG_OBJ_RX_INT_ENABLE;
        CANMessageSet(CAN0_BASE, 1u, &sCANMessage, MSG_OBJ_TYPE_RX);
        SendCANMessage(LM_API_UPD_PING, 0, 0);
    }
}
void SendCANDownload(uint8_t *buffer)
{
    tCANMsgObject sCANMessage;
    uint32_t timeout;
    uint8_t data[8];
    //
    // Wait until the previous packet has been sent, providing a time out so
    // that the boot loader does not hang here.
    //
    for(timeout = 1000;
        (timeout != 0) && (HWREG(CAN0_BASE + CAN_O_TXRQ1) != 0);
        timeout--)
    {
    }
    if(timeout !=0)
    {
        // Setup to receive ACK
        CANIntClear(CAN0_BASE, 1u);
        sCANMessage.ui32MsgID = LM_API_UPD_ACK;
        sCANMessage.ui32Flags = MSG_OBJ_RX_INT_ENABLE;
        CANMessageSet(CAN0_BASE, 1u, &sCANMessage, MSG_OBJ_TYPE_RX);
        data[0] = buffer[4];
        data[1] = buffer[3];
        data[2] = buffer[2];
        data[3] = buffer[1];
        data[4] = buffer[8];
        data[5] = buffer[7];
        data[6] = buffer[6];
        data[7] = buffer[5];
        SendCANMessage(LM_API_UPD_DOWNLOAD, 8u, data);
    }
}

bool sendCANSend(int count, uint8_t *buffer)
{
    tCANMsgObject sCANMessage;
    uint32_t timeout;
    uint32_t frameSize;
    uint8_t *pui8data;

    pui8data = buffer + 1u;
    count--;
    while(count != 0)
    {
        if(count > 8u)
        {
            frameSize = 8u;
        }
        else
        {
            frameSize = count;
        }
        //
        // Wait until the previous packet has been sent, providing a time out so
        // that the boot loader does not hang here.
        //
        for(timeout = 1000u;
            (timeout != 0) && (HWREG(CAN0_BASE + CAN_O_TXRQ1) != 0);
            timeout--)
        {
        }
        if(timeout !=0)
        {
            // Setup to receive ACK
            CANIntClear(CAN0_BASE, 1u);
            sCANMessage.ui32MsgID = LM_API_UPD_ACK;
            sCANMessage.ui32Flags = MSG_OBJ_RX_INT_ENABLE;
            CANMessageSet(CAN0_BASE, 1u, &sCANMessage, MSG_OBJ_TYPE_RX);
            SendCANMessage(LM_API_UPD_SEND_DATA, frameSize, pui8data);
            if(GetCANAck() == false)
            {
                return false;
            }
        }

        count = count - frameSize;
        pui8data += frameSize;
    }
    return true;
}

void SendCANReset(void)
{
    uint32_t timeout;
    //
    // Wait until the previous packet has been sent, providing a time out so
    // that the boot loader does not hang here.
    //
    for(timeout = 1000;
        (timeout != 0) && (HWREG(CAN0_BASE + CAN_O_TXRQ1) != 0);
        timeout--)
    {
    }
    if(timeout !=0)
    {
        SendCANMessage(LM_API_UPD_RESET, 0, 0);
    }
}

bool GetCANAck(void)
{
    uint32_t timeout;
    uint8_t pui8data[8u];
    tCANMsgObject sCANMessage;
    bool ret = false;
    // Wait for ACK message, may take long time during erase
    for(timeout = 1000000u;
        (timeout != 0) && ((HWREG(CAN0_BASE + CAN_O_MSG1INT) & 1u) == 0);
        timeout--)
    {
    }
    if(timeout == 0)
    {
        ret = false;
    }
    else
    {
        sCANMessage.pui8MsgData = pui8data;
        CANMessageGet(CAN0_BASE, 1u, &sCANMessage, true);
        if(pui8data[0] == CAN_CMD_SUCCESS)
        {
            ret = true;
        }
        else
        {
            ret = false;
        }
    }
    return ret;
}

void SendCANUpdate(int count, uint8_t *buffer)
{
    uint32_t id;

    buffer++; //Skip past CAN Update command 0x26
    id = *buffer++ << 24u;
    id |= *buffer++ << 16u;
    id |= *buffer++ << 8u;
    id |= *buffer++;

    // If the ID is 0xFFFFFFFF and the data is one byte of 0x00
    //    then jump to serial bootloader to reprogram this part
    if((id == 0xFFFFFFFFu) && (count == 6) && (*buffer == 0x00u))
    {
        // Disable all processor interrupts.  Instead of disabling them
        // one at a time, a direct write to NVIC is done to disable all
        // peripheral interrupts.
        HWREG(NVIC_DIS0) = 0xffffffff;
        HWREG(NVIC_DIS1) = 0xffffffff;
        // Return control to the ROM serial boot loader.
        ROM_UpdateUART();
    }
    else
    {
        SendCANMessage(id, count - 5, buffer);
    }
}
