/*******************************************************************************
* Project:      Amped'RF Bluetooth
* File name:    GKI_dma.c
* Description:  DMA abstraction layer
*               Note -- This source is for reference only
*
* Copyright (c) 2013 Amped'RF Inc., AR Technology  All rights reserved.
*******************************************************************************/
#include "SDK_Common.h"
#include "SDK_it.h"
#include "GKI_lib.h"

#define LE_SDK

// Globals
#ifdef SEGMENT_TX_DMA
tHciDmaRec HciDmaRec;
#endif

//#define UartDev     UART[u]             // Uart Base Address
#define MainUartDev UART[GKI_MainUart]  // Uart Base Address
#define pUart       (&TM_UartDev[u])    // Uart Structure
#define RX          (pUart->Rx)
#define TX          (pUart->Tx)
#define UartPin     (pUart->Pin)
//#define RX_LOW_WM  64


// IRQn_Type for MAIN
// Tx interrupt vector -  DMA2_Stream7_IRQn - DMA TC
// Rx interrupt vector -  USART1_IRQn       - IDLE
//
// IRQn_Type for HCI
// Tx interrupt vector - DMA1_Stream6_IRQn - DMA TC
// Rx interrupt vector - USART2_IRQn       - IDLE
//


#define PRINT_DEBUG
#ifdef  PRINT_DEBUG
static char pdbgmsg[128];
char *p_pdbgmsg=pdbgmsg;
#define PRINT(a)			sprintf(pdbgmsg,a);              GKI_putstring(pdbgmsg)
#define PRINT1(a,p1)		sprintf(pdbgmsg,a,p1);           GKI_putstring(pdbgmsg)
#define PRINT2(a,p1,p2)		sprintf(pdbgmsg,a,p1,p2);        GKI_putstring(pdbgmsg)
#define PRINT3(a,p1,p2,p3)	sprintf(pdbgmsg,a,p1,p2,p3);     GKI_putstring(pdbgmsg)
#else
#define PRINT(a)
#define PRINT1(a,p1)
#define PRINT2(a,p1,p2)
#define PRINT3(a,p1,p3)
#endif




// UART Registers
extern USART_TypeDef * UART[];
//
#if defined(STM32F2xx) || defined(STM32F4xx)
#define GKI_MAIN_TX_DMA_PENDING (DMA_IT_FEIF7 | DMA_IT_DMEIF7 | DMA_IT_TEIF7 | DMA_IT_HTIF7 | DMA_IT_TCIF7)
#define GKI_HCI_TX_DMA_PENDING  (DMA_IT_FEIF6 | DMA_IT_DMEIF6 | DMA_IT_TEIF6 | DMA_IT_HTIF6 | DMA_IT_TCIF6)
static u32 TxITPending[2]={GKI_MAIN_TX_DMA_PENDING, GKI_HCI_TX_DMA_PENDING};

#define GKI_MAIN_RX_DMA_PENDING (DMA_FLAG_FEIF2 | DMA_FLAG_DMEIF2 | DMA_FLAG_TEIF2 | DMA_FLAG_HTIF2 | DMA_FLAG_TCIF2)
#define GKI_HCI_RX_DMA_PENDING  (DMA_FLAG_FEIF5 | DMA_FLAG_DMEIF5 | DMA_FLAG_TEIF5 | DMA_FLAG_HTIF5 | DMA_FLAG_TCIF5)
static u32 DmaRxPendingFlags[2]={GKI_MAIN_RX_DMA_PENDING, GKI_HCI_RX_DMA_PENDING};

static DMA_Stream_TypeDef * TxDmaStream[2]={DMA2_Stream7, DMA1_Stream6};
static DMA_Stream_TypeDef * RxDmaStream[2]={DMA2_Stream2, DMA1_Stream5};
static u32 TxChannel[2]={DMA_Channel_4, DMA_Channel_4};

static u8 RxIRQn[2]={USART1_IRQn, USART2_IRQn};
static u8 DmaRxIRQn[2]={DMA2_Stream2_IRQn, DMA1_Stream5_IRQn};
static u8 DmaTxIRQn[2]={DMA2_Stream7_IRQn, DMA1_Stream6_IRQn};
#endif


/*******************************************************************************
* Function Name  : GKI_dma_UartConfig
* Description    : Configure DMA for Uart
*******************************************************************************/
void GKI_dma_UartConfig(u16 u)
{
    static uint16 StatusRegister, DataRegister;
    uint32 DMA_RX_IT, DMA_TX_IT;
    NVIC_InitTypeDef  NVIC_InitStructure;
    DMA_InitTypeDef  DMA_InitStructure;
    uint16 UartPriority = u ? TM_MainUartPriority : TM_HciUartPriority;

    ENTER_CRITICAL();

    // Vector interrupts to use
    DMA_TX_IT = DMA_IT_TC;
    DMA_RX_IT = (u==eTM_MainUart) ?
        (DMA_IT_TC | DMA_IT_TE | DMA_IT_HT) : (DMA_IT_TC | DMA_IT_HT);

    // Setup Interrupt Vectors for USART Tx DMA
    DMA_ITConfig(TxDmaStream[u], DMA_TX_IT, DISABLE);
    DMA_ClearITPendingBit(TxDmaStream[u], TxITPending[u]);
    NVIC_InitStructure.NVIC_IRQChannel                      = DmaTxIRQn[u];
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority    = UartPriority;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority           = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd                   = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
    DMA_ITConfig(TxDmaStream[u], DMA_TX_IT, ENABLE);

    // If Rx DMA is available...
    if (DMA_RX_IT)
    {
        // DMA Rx interrupt vector
        NVIC_InitStructure.NVIC_IRQChannel                      = DmaRxIRQn[u];
        NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority    = UartPriority;
        NVIC_InitStructure.NVIC_IRQChannelSubPriority           = 0;
        NVIC_InitStructure.NVIC_IRQChannelCmd                   = ENABLE;
        NVIC_Init(&NVIC_InitStructure);

        // Setup Rx DMA
        DMA_Cmd(RxDmaStream[u], DISABLE);
        DMA_InitStructure.DMA_Channel                   = DMA_Channel_4;
        DMA_InitStructure.DMA_PeripheralBaseAddr        = (uint32)&UART[u]->DR;
        DMA_InitStructure.DMA_Memory0BaseAddr           = (uint32)(TM_UartDev[u].Rx.DmaBuffer); //       RX.DmaBuffer;
        DMA_InitStructure.DMA_DIR                       = DMA_DIR_PeripheralToMemory;
        DMA_InitStructure.DMA_BufferSize                = TM_UartDev[u].Rx.DmaSize;
        DMA_InitStructure.DMA_PeripheralInc             = DMA_PeripheralInc_Disable;
        DMA_InitStructure.DMA_MemoryInc                 = DMA_MemoryInc_Enable;
        DMA_InitStructure.DMA_PeripheralDataSize        = DMA_PeripheralDataSize_Byte;
        DMA_InitStructure.DMA_MemoryDataSize            = DMA_MemoryDataSize_Byte;
        DMA_InitStructure.DMA_Mode                      = DMA_Mode_Circular;
        DMA_InitStructure.DMA_Priority                  = DMA_Priority_VeryHigh;
        DMA_InitStructure.DMA_FIFOMode                  = DMA_FIFOMode_Disable;
        DMA_InitStructure.DMA_FIFOThreshold             = DMA_FIFOThreshold_Full;
        DMA_InitStructure.DMA_MemoryBurst               = DMA_MemoryBurst_Single;
        DMA_InitStructure.DMA_PeripheralBurst           = DMA_PeripheralBurst_Single;
        DMA_Init(RxDmaStream[u], &DMA_InitStructure);
        DMA_Cmd(RxDmaStream[u], ENABLE);

        // Turn on Vectors for RX DMA
        DMA_ITConfig(RxDmaStream[u], DMA_RX_IT, ENABLE);
    }
    RX.DmaPnt = 0;

    // Enable USART DMA for Tx & Rx
    USART_DMACmd(UART[u], USART_DMAReq_Tx, ENABLE);
    USART_DMACmd(UART[u], USART_DMAReq_Rx, ENABLE);

    // Enable IDLE interrupts
    NVIC_InitStructure.NVIC_IRQChannel                      = RxIRQn[u];
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority    = pUart->IRQPriority;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority           = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd                   = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
    USART_ITConfig(UART[u], USART_IT_IDLE, ENABLE);


    // Clear DMA Flags
    DMA_ClearFlag(TxDmaStream[u], DmaRxPendingFlags[u]);


    // Clear status flags
    StatusRegister = UART[u]->SR;
    DataRegister   = UART[u]->DR;

    EXIT_CRITICAL();
}



#ifdef ENABLE_TRACE_TX_DATA

#define TRACE_TX_DATA_SIZE   2048
u8  TraceTxData[TRACE_TX_DATA_SIZE];
u16 TraceTxDataPnt=0;

#define TRACE_TX_FRAME_MAX   14
typedef struct {
    u32 Timestamp;
    u16 StartPnt;
    u16 Length;
} tTraceTxFrame;
tTraceTxFrame TraceTxFrame[TRACE_TX_FRAME_MAX];
u16 TraceTxFramePnt=0;
bool AddTxHalt=false;

void AddTxFrame(uint16 u, void *pData, uint16 Length)
{
    if (u!=eTM_HciUart || AddTxHalt) {
        return;
    }
    else if (Length==0) {
        AddTxHalt = true;
    }

    TraceTxFrame[TraceTxFramePnt].Timestamp = TM_GetTicks();
    TraceTxFrame[TraceTxFramePnt].StartPnt = TraceTxDataPnt;
    TraceTxFrame[TraceTxFramePnt].Length = Length;
    if (++TraceTxFramePnt >= TRACE_TX_FRAME_MAX)
        TraceTxFramePnt = 0;

    while (Length) {
        u16 len = MIN(Length, TRACE_TX_DATA_SIZE - TraceTxDataPnt);
        memcpy(&TraceTxData[TraceTxDataPnt], pData, len);
        pData = (uint8 *)pData + len;
        TraceTxDataPnt += len;
        Length -= len;
        if (TraceTxDataPnt >= TRACE_TX_DATA_SIZE)
            TraceTxDataPnt = 0;
    }
}

void ShowTraceTx(void)
{
    u16 i, j, k, m;
    u32 ms;
    u32 t = TraceTxFrame[TraceTxFramePnt].Timestamp;
    char msg[64];

    GKI_putstring("\r\n   MSecs Len   Data\r\n");
    for (i=0; i<TRACE_TX_FRAME_MAX; i++) {
        j = (TraceTxFramePnt + i) % TRACE_TX_FRAME_MAX;
        ms = (TraceTxFrame[j].Timestamp-t)*100/TM_TICKS_PER_MILLISECOND;
        sprintf(msg, "%5d.%02d %3d: ", ms/100, ms%100, TraceTxFrame[j].Length);
        GKI_putstring(msg);
        for (k=0, m=TraceTxFrame[j].StartPnt; k<TraceTxFrame[j].Length; k++) {
            sprintf(msg," %02x", TraceTxData[m]);
            GKI_putstring(msg);
            if (++m >= TRACE_TX_DATA_SIZE)
                m = 0;
        }
        GKI_putstring("\r\n");

        t = TraceTxFrame[j].Timestamp;
    }
    AddTxHalt = false;
}


#else
#define AddTxFrame(u,x,y)
void ShowTraceTx(void) {}
#endif // ENABLE_TRACE_TX_DATA

#ifdef LE_STK
extern bool BLE_TEST_MODE;
#endif
extern volatile uint8 SMP_DEBUG;
/*******************************************************************************
* Function Name  : GKI_dma_StartTxDma
* Description    : Set DMA registers to start transmitting
*******************************************************************************/
void GKI_dma_StartTxDma(uint16 u, void *pData, uint16 Length)
{
    DMA_InitTypeDef DMA_InitStruct;

    // Initiate DMA transfer
#if defined(STM32F10x)
    DMA_DeInit(TxDmaIrq[u]);
    DMA_InitStruct.DMA_PeripheralBaseAddr  = (uint32)&UART[u]->DR;
    DMA_InitStruct.DMA_MemoryBaseAddr      = (uint32)pData;
    DMA_InitStruct.DMA_DIR                 = DMA_DIR_PeripheralDST;
    DMA_InitStruct.DMA_BufferSize          = Length;
    DMA_InitStruct.DMA_PeripheralInc       = DMA_PeripheralInc_Disable;
    DMA_InitStruct.DMA_MemoryInc           = DMA_MemoryInc_Enable;
    DMA_InitStruct.DMA_PeripheralDataSize  = DMA_PeripheralDataSize_Byte;
    DMA_InitStruct.DMA_MemoryDataSize      = DMA_MemoryDataSize_Byte;
    DMA_InitStruct.DMA_Mode                = DMA_Mode_Normal;
    DMA_InitStruct.DMA_Priority            = DMA_Priority_VeryHigh;
    DMA_InitStruct.DMA_M2M                 = DMA_M2M_Disable;
    DMA_Init(TxDMA, &DMA_InitStruct);
    DMA_ITConfig(TxDMA, DMA_IT_TC, ENABLE);
    DMA_Cmd(TxDmaIrq[u], ENABLE);
#elif defined(STM32F2xx) || defined(STM32F4xx)
    /*DMA_DeInit(TxDmaStream[u]);
    DMA_InitStruct.DMA_Channel              = TxChannel[u];
    DMA_InitStruct.DMA_PeripheralBaseAddr   = (u32)&UART[u]->DR;
    DMA_InitStruct.DMA_Memory0BaseAddr      = (u32)pData;
    DMA_InitStruct.DMA_DIR                  = DMA_DIR_MemoryToPeripheral;
    DMA_InitStruct.DMA_BufferSize           = Length;
    DMA_InitStruct.DMA_PeripheralInc        = DMA_PeripheralInc_Disable;
    DMA_InitStruct.DMA_MemoryInc            = DMA_MemoryInc_Enable;
    DMA_InitStruct.DMA_PeripheralDataSize   = DMA_PeripheralDataSize_Byte;
    DMA_InitStruct.DMA_MemoryDataSize       = DMA_MemoryDataSize_Byte;
    DMA_InitStruct.DMA_Mode                 = DMA_Mode_Normal;
    DMA_InitStruct.DMA_Priority             = DMA_Priority_VeryHigh;
    DMA_InitStruct.DMA_FIFOMode             = DMA_FIFOMode_Disable;
    DMA_InitStruct.DMA_FIFOThreshold        = DMA_FIFOThreshold_Full;
    DMA_InitStruct.DMA_MemoryBurst          = DMA_MemoryBurst_Single;
    DMA_InitStruct.DMA_PeripheralBurst      = DMA_PeripheralBurst_Single;
    DMA_Init(TxDmaStream[u], &DMA_InitStruct);
    DMA_ITConfig(TxDmaStream[u], DMA_IT_TC, ENABLE);
    DMA_Cmd(TxDmaStream[u], ENABLE);*/

    DMA_DeInit(TxDmaStream[u]);
    DMA_InitStruct.DMA_Channel              = TxChannel[u];
    DMA_InitStruct.DMA_PeripheralBaseAddr   = (u32)&UART[u]->DR;
    DMA_InitStruct.DMA_Memory0BaseAddr      = (u32)pData;
    DMA_InitStruct.DMA_DIR                  = DMA_DIR_MemoryToPeripheral;
    DMA_InitStruct.DMA_BufferSize           = Length;
    DMA_InitStruct.DMA_PeripheralInc        = DMA_PeripheralInc_Disable;
    DMA_InitStruct.DMA_MemoryInc            = DMA_MemoryInc_Enable;
    DMA_InitStruct.DMA_PeripheralDataSize   = DMA_PeripheralDataSize_Byte;
    DMA_InitStruct.DMA_MemoryDataSize       = DMA_MemoryDataSize_Byte;
    DMA_InitStruct.DMA_Mode                 = DMA_Mode_Normal;
    DMA_InitStruct.DMA_Priority             = DMA_Priority_VeryHigh;
    DMA_InitStruct.DMA_FIFOMode             = DMA_FIFOMode_Disable;
    DMA_InitStruct.DMA_FIFOThreshold        = DMA_FIFOThreshold_Full;
    DMA_InitStruct.DMA_MemoryBurst          = DMA_MemoryBurst_Single;
    DMA_InitStruct.DMA_PeripheralBurst      = DMA_FIFOThreshold_Full;
    DMA_Init(TxDmaStream[u], &DMA_InitStruct);
#ifdef LE_STK
    if(!BLE_TEST_MODE && !SMP_DEBUG)
   	DMA_ITConfig(TxDmaStream[u], DMA_IT_TC, ENABLE);
#else
    DMA_ITConfig(TxDmaStream[u], DMA_IT_TC, ENABLE);
#endif
    DMA_Cmd(TxDmaStream[u], ENABLE);
#endif
}



/*******************************************************************************
* Function Name  : GKI_StartTxDma
* Description    : Set DMA registers to start transmitting
*******************************************************************************/
void GKI_StartTxDma(uint16 u, void *pData, uint16 Length)
{
    AddTxFrame(u, pData, Length);

#ifdef GKI_TRACE_HCI
    if (u==eTM_HciUart) {
        GKI_HCI_Tx(pData, Length);
    }
#endif

#ifdef TESTING
    tHciUartPacket *pPkt = (tHciUartPacket *)pData;
    if (pPkt->PktTypeCode==HCI_UART_CMD) {
        L2CAP_Trace(TRACE_L2CAP_CMD, pPkt->PktType.Cmd.Opcode);
    }
#endif
#ifdef ENABLE_FOOTPRINT
    if (u==eTM_HciUart) {
        AddFootprint(FOOTPRINT_TX, pData);
    }
#endif
#ifdef ENABLE_SIGNAL_CHECK
    if (u==eTM_MainUart) {
        TS_LogRxTx(LOG_DIR_TX, Length);
    }
#endif


#ifdef USE_HCI_LOGICAL_CHANNEL
    if (u==eTM_HciUart) {
        *(uint8 *)pData = HCI_LOGICAL_CHANNEL | (*(uint8 *)pData & HCI_LOGICAL_CHANNEL_MASK);
    }
#endif

    GKI_dma_StartTxDma(u, pData, Length);

    // Wait until every Tx bit is rolled out
	if(u == eTM_HciUart)
    {
        // delay to avoid "hardware error 0x20" issue
        while (USART_GetFlagStatus(USART2, USART_FLAG_TC) == RESET)
            ;
    }
}






/*******************************************************************************
* Function Name  : GKI_show_clocks
* Description    : Show clocks
*******************************************************************************/
void GKI_show_clocks(void)
{
    extern void InitUart(void);
    InitUart();
    GKI_show_clocks();
    RCC_ClocksTypeDef RCC_Clocks;
    RCC_GetClocksFreq(&RCC_Clocks);
}


//--------------------------------------------------------------
//