/*
 * Copyright (c) 2013 - 2014, Freescale Semiconductor, Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * o Redistributions of source code must retain the above copyright notice, this list
 *   of conditions and the following disclaimer.
 *
 * o Redistributions in binary form must reproduce the above copyright notice, this
 *   list of conditions and the following disclaimer in the documentation and/or
 *   other materials provided with the distribution.
 *
 * o Neither the name of Freescale Semiconductor, Inc. nor the names of its
 *   contributors may be used to endorse or promote products derived from this
 *   software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */


#include <stdint.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include "device/fsl_device_registers.h"
#include "fsl_uart_driver.h"
#include "fsl_clock_manager.h"
#include "fsl_flexcan_driver.h"
#include "fsl_pit_driver.h"
#include "fsl_interrupt_manager.h"
#include "board.h"

////////////////////////////////////////////////////////////////////////////////
// Prototypes
////////////////////////////////////////////////////////////////////////////////
int printf(const char *format,...);
int scanf(const char *format,...);
static void pit0_isr(void);

static uart_state_t s_uart0State;
static uint32_t s_uart0Instance;
static uart_user_config_t s_uart0Config =
{
    115200,
    kUartParityDisabled,
    kUartOneStopBit,
    kUart8BitsPerChar
};

static uint32_t s_pitInstance;
volatile uint8_t timeout_flag = false;  //disable timeout event
// Structure of initialize PIT, this will enable PIT module as default
static pit_user_config_t s_pitConfig = {
    .isInterruptEnabled = true,
    .isTimerChained = false,
    //for 115200bps 8N1, 1 charactor is around 87us, here set 4ms timeout
    .periodUs = 4000
};

static uint32_t TX_identifier;
static uint32_t RX_identifier;
//static uint32_t TX_remote_identifier;
//static uint32_t RX_remote_identifier;
static uint32_t TX_mailbox_num;
static uint32_t RX_mailbox_num;
//static uint32_t TX_remote_mailbox_num;
//static uint32_t RX_remote_mailbox_num;
static flexcan_user_config_t flexcan1_data;
static flexcan_id_table_t id_table;
static flexcan_mb_t rx_fifo;
static flexcan_mb_t rx_mb;
static uint8_t s_flexcanInstance;
static flexcan_data_info_t tx_info;
static flexcan_data_info_t rx_info;
static uint32_t number_errors;
extern bool int_mb,int_fifo;
extern sync_object_t irqSync;

void PIT_IRQ_Handler(void);
////////////////////////////////////////////////////////////////////////////////
// Code
////////////////////////////////////////////////////////////////////////////////
//! @brief Init hardware for TWR-K70F120M
static void init_hardware(void)
{
    hardware_init();

    // Initialize PIT, enable module clock.
    s_pitInstance = 0; //pit0 is used

    // clock enable in the pit driver, and disable timer run in debug mode
    pit_init_module(false);

    // Init pit timer.
    pit_init_channel(s_pitInstance, &s_pitConfig);

    // Register a custom pit0 ISR
    interrupt_register_handler(PIT0_IRQn, pit0_isr);

    // Enable PIT0 interrupt.
    interrupt_enable(PIT0_IRQn);

    //start pit timer 0
    pit_timer_start(s_pitInstance);

    s_uart0Instance = BOARD_DEBUG_UART_INSTANCE;
    uart_init(s_uart0Instance, &s_uart0State, &s_uart0Config);

    s_flexcanInstance = 0; //FlexCAN0 is selected in this demo

    // Enable clock gate to FlexCAN 0 module
    clock_manager_set_gate(kClockModuleFLEXCAN,s_flexcanInstance,true);
}

//! @brief main function.
void main(void)
{
    uint8_t ch0[8], ch1[8], ch2[8], *ch; //ch0 and ch1 for uart receiving buffer, ch2 for uart transfer buffer
    uint8_t node_type; //var used to store the node type input from the console
    uint32_t i, j;
    uart_status_t status;
    uint32_t result;
    uint32_t rx_fifo_id[8]; //though rx fifo is not used in this demo, keep it for future useage
    uint32_t temp;

    init_hardware();

    number_errors = 0;

    flexcan1_data.num_mb = 16;
    flexcan1_data.max_num_mb = 16;
    flexcan1_data.num_id_filters = kFlexCanRxFifoIDFilters_8;
    flexcan1_data.is_rx_fifo_needed = false; //disable fifo here
    flexcan1_data.is_rx_mb_needed = true; //enable message buffer to receive

    id_table.is_extended_mb = false;
    id_table.is_remote_mb = false;
    rx_fifo_id[0] = 0x666;
    rx_fifo_id[1] = 0x667;
    rx_fifo_id[2] = 0x676;
    rx_fifo_id[3] = 0x66E;
    rx_fifo_id[4] = 0x66F;
    for (i = 5; i < 8; i++)
        rx_fifo_id[i] = 0x6E6;
    id_table.id_filter = rx_fifo_id;

    /* Select mailbox number */
    RX_mailbox_num = 8;
    TX_mailbox_num = 9;
 //   RX_remote_mailbox_num = 10;
 //   TX_remote_mailbox_num = 11;

    printf("\r\n*********FlexCAN : SCI2CAN demo *********");
    printf("\r\n   Message format: Standard (11 bit id)");
    printf("\r\n   Message buffer 8 used for Rx.");
    printf("\r\n   Message buffer 9 used for Tx.");
    printf("\r\n   OSJTAG Port used for Serial Console.");
    printf("\r\n   Interrupt Mode: Enabled");
    printf("\r\n   Operation Mode: TX and RX --> Normal");
    printf("\r\n*****************************************\r\n");

    do{
       printf("Please select local node as A or B:\r\n");
       printf("Node:");
       scanf("%c",&node_type);
       printf("%c\r\n",node_type);
    }while(node_type!='A' && node_type!='B' && node_type!='a' && node_type!='b');
    if(node_type=='A' || node_type=='a')
    {
       RX_identifier = 0x123;
       TX_identifier = 0x321;
 //      RX_remote_identifier = 0x0F0;
 //      TX_remote_identifier = 0x00F;
       rx_fifo_id[0] = 0x123; //set rx fifo id
    }
    else
    {
       RX_identifier = 0x321;
       TX_identifier = 0x123;
 //      RX_remote_identifier = 0x00F;
 //     TX_remote_identifier = 0x0F0;
       rx_fifo_id[0] = 0x321; //set rx fifo id
    }

    result = flexcan_init(s_flexcanInstance, &flexcan1_data, true);
    if (result)
    {
        number_errors++;
        printf("\r\nFLEXCAN initilization failed. result: 0x%lx", result);
        return;
    }

    result = flexcan_set_bitrate(s_flexcanInstance, kFlexCanBitrate_1M);
    if (result)
    {
        number_errors++;
        printf("\r\nFLEXCAN set bitrate failed. result: 0x%lx", result);
        return;
    }

    flexcan_set_mask_type(s_flexcanInstance, kFlexCanRxMask_Global);

    if (flexcan1_data.is_rx_fifo_needed)
    {
        result = flexcan_set_rx_fifo_global_mask(s_flexcanInstance, kFlexCanMbId_Std, 0x7FF);
        if (result)
        {
            number_errors++;
            printf("\r\nFLEXCAN set rx fifo global mask failed. result: 0x%lx", result);
            return;
        }
    }

    if (flexcan1_data.is_rx_mb_needed)
    {
        result = flexcan_set_rx_mb_global_mask(s_flexcanInstance, kFlexCanMbId_Std, 0x123);
        if (result)
        {
            number_errors++;
            printf("\r\nFLEXCAN set rx MB global mask failed. result: 0x%lx", result);
            return;
        }
    }
    //FlexCAN reveive config
    rx_info.msg_id_type = kFlexCanMbId_Std;
    rx_info.data_length = 1;

    if (flexcan1_data.is_rx_fifo_needed)
    {
        // Configure RX FIFO fields
        result = flexcan_rx_fifo_config(s_flexcanInstance, &flexcan1_data, kFlexCanRxFifoIdElementFormat_A,
                                        &id_table);
        if (result)
        {
            number_errors++;
            printf("\r\nFlexCAN RX FIFO configuration failed. result: 0x%lx", result);
            return;
        }
    }

    if (flexcan1_data.is_rx_mb_needed)
    {
        // Configure RX MB fields
        result = flexcan_rx_mb_config(s_flexcanInstance, &flexcan1_data, RX_mailbox_num, &rx_info,
                                      RX_identifier);
        if (result)
        {
            number_errors++;
            printf("\r\nFlexCAN RX MB configuration failed. result: 0x%lx", result);
            return;
        }
    }
    //FlexCAN transfer config
    tx_info.msg_id_type = kFlexCanMbId_Std;
    tx_info.data_length = 8;

    result = flexcan_tx_mb_config(s_flexcanInstance, &flexcan1_data, TX_mailbox_num, &tx_info, TX_identifier);
    if (result)
    {
        printf("\nTransmit MB config error. Error Code: 0x%lx", result);
    }

    int_mb = int_fifo = false;

    ch = ch0;
    uart_receive_data_async(&s_uart0State,ch,8); //start to receive charactors from UART0
    while(1)
    {
      if(uart_hal_is_receive_active_edge_detected(s_uart0Instance)) //check if start bit detected
      {
          uart_hal_clear_status_flag(s_uart0Instance,kUartReceiveActiveEdgeDetect); //Yes to clear flag
          pit_timer_stop(s_pitInstance); //reset pit timer 0
          pit_timer_start(s_pitInstance); //reset pit timer 0

          if(!(timeout_flag)) //if timeout event is disabled
          {
            timeout_flag = 0xff; //enable timeout event
          }
      }

      status = uart_get_receive_status(&s_uart0State,&i); //get receive status
      if((status==kStatus_UART_Success) || (status==kStatus_UART_RxBusy && timeout_flag==true)) //if receiving complete or still in receiving but timeout event happens
      {
        if(i) //if has received something
        {
          if(timeout_flag==true)
          {
            uart_abort_receiving_data(&s_uart0State); //cancle the receiving
            timeout_flag = 0xff; //re-enable timeout event
          }
          ch = (ch==ch0)? ch1:ch0; //switch receiving buffer
          uart_receive_data_async(&s_uart0State,ch,8); //restart the receiving process
          tx_info.data_length = i; //number of bytes to be sent
          result = flexcan_send(s_flexcanInstance, &flexcan1_data, TX_mailbox_num, &tx_info, TX_identifier,
                                i, (ch==ch1)?ch0:ch1);
          if (result)
          {
              number_errors++;
              printf("\r\nTransmit send configuration failed. result: 0x%lx", result);
              return;
          }
        }
      }

      if(int_mb) //if message buffer interrupt happens
      {
            sync_create(&irqSync, 0); //reset irpSync as the flexcan isr signals it.
        // Lock RX MB
            result = flexcan_hal_lock_rx_mb(s_flexcanInstance, &flexcan1_data, RX_mailbox_num);
            if(result)
            {
                printf("\r\nFlexCAN RX MB Lock failed. result: 0x%lx", result);
                return;
            }

            // Get RX MB field values
            result = flexcan_hal_get_mb(s_flexcanInstance, &flexcan1_data, RX_mailbox_num, &rx_mb);
            if(result)
            {
                printf("\r\nFlexCAN Get RX MB field failed. result: 0x%lx", result);
                return;
            }
            temp = ((rx_mb.cs) >> 16) & 0xF;
            for(j=0;j<temp;j++)
            {
              ch2[j] = rx_mb.data[j];
            }
            uart_send_data_async(&s_uart0State,ch2,temp);
            // Unlock RX message buffer and RX FIFO
            flexcan_hal_unlock_rx_mb(s_flexcanInstance);
            int_mb = false;
      }

      if(int_fifo) //if rx fifo interrupt happens
      {
            sync_create(&irqSync, 0); //reset irpSync as the flexcan isr signals it.
            // Lock RX FIFO
            result = flexcan_hal_lock_rx_mb(s_flexcanInstance, &flexcan1_data, 0);
            if(result)
            {
                printf("\r\nFlexCAN RX FIFO Lock failed. result: 0x%lx", result);
                return;
            }

            // Get RX FIFO field values
            result = flexcan_hal_read_fifo(s_flexcanInstance, &rx_fifo);
            if(result)
            {
                printf("\r\nFlexCAN Get RX FIFO field failed. result: 0x%lx", result);
                return;
            }

            temp = ((rx_fifo.cs) >> 16) & 0xF;
            for(j=0;j<temp;j++)
            {
              ch2[j] = rx_fifo.data[j];
            }
            uart_send_data_async(&s_uart0State,ch2,temp);
            // Unlock RX message buffer and RX FIFO
            flexcan_hal_unlock_rx_mb(s_flexcanInstance);
            int_fifo = false;
      }

      if(number_errors)
      {
        printf("\r\nFlexCAN demo failed!!");
        return;
      }
    }

}

//! @brief User define function
int printf(const char *format,...)
{
    int chars;
    va_list ap;
    char printbuffer[128];
    va_start(ap, format);
    chars = vsprintf(printbuffer, format, ap);
    va_end(ap);
    uart_send_data(&s_uart0State, (uint8_t*)printbuffer, chars, 1);
    return chars ;
}
int scanf(const char *format,...)
{
    int i = 0;
    unsigned char c;
    va_list ap;
    char scanbuffer[128];

    while(1)
    {
        uart_receive_data(&s_uart0State,&c,1,1);
        if((c == 0x0d) || (c == 0x0a))
        {
            scanbuffer[i] = '\0';
            /* In case we receiced sequence: 0x0D->0x0A, polling a byte to bypass this char */
            uart_receive_data_async(&s_uart0State,&c,1);
            break;
        }
        else
        {
            scanbuffer[i++] = c;
        }
    }

    va_start(ap,format);
    i = vsscanf((char *)scanbuffer,format,ap);
    va_end(ap);

    return i;
}
//! @brief K70 PIT IRQ handler with the same name in startup code
//!
//! Note: each timer has seperate ISR handler.
static void pit0_isr(void)
{
    // Set timeout_flag
    if(timeout_flag==0xff)
      timeout_flag = true;

    // Clear interrupt flag.
    pit_hal_clear_interrupt_flag(0);
}
////////////////////////////////////////////////////////////////////////////////
// EOF
////////////////////////////////////////////////////////////////////////////////
