//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft end-user
// license agreement (EULA) under which you licensed this SOFTWARE PRODUCT.
// If you did not accept the terms of the EULA, you are not authorized to use
// this source code. For a copy of the EULA, please see the LICENSE.RTF on your
// install media.
//
//
/*++
  THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
  ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
  THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
  PARTICULAR PURPOSE.

  Module Name: common_pdd_ser.cpp

  Abstract:

  Serial PDD for SamSung  UART Common Code.

  Notes:
--*/

/*****************************************************************************
 * Include files
 *****************************************************************************/ 
#include <windows.h>
#include <types.h>
#include <ceddk.h>

#include <bsp.h>
#include <ddkreg.h>
#include <serhw.h>
//#include <Serdbg.h>
#include <common_pdd_ser.h>
#include <register_map.h>

#ifdef USE_DMA
#include <dma_reg.h>
#include "ser_dma.h"
#endif 


/*****************************************************************************
 * Definition
 *****************************************************************************/
#ifdef USE_DMA
#define DMA_MSG         DISABLE
#define TX_FIFO_LEN     16

#define RX_DMA_TRANS_SIZE  4032
#define DMA_TRANS_SIZE			28
#define DMA_RX_TRIGGER_LEVEL	32

#define DMA_BURST_4B FALSE
#define DMA_MSG_MDD         DISABLE//ENABLE
#define NORMAL_DMA_MODE	DISABLE
#endif 

#define    UART_SCLK    ENABLE

//#define    _LOOPBACK    TRUE

#ifdef UART_PDD_DRIVER_DEBUG
DBGPARAM dpCurSettings = {
        TEXT("SerialDriver"), {
                TEXT("Test")           //  0
               ,TEXT("Params")         //  1
               ,TEXT("Verbose")        //  2
               ,TEXT("Interrupt")      //  3
               ,TEXT("WODM")           //  4
               ,TEXT("WIDM")           //  5
               ,TEXT("PDD")            //  6
               ,TEXT("MDD")            //  7
               ,TEXT("Regs")           //  8
               ,TEXT("Misc")           //  9
               ,TEXT("Init")           // 10
               ,TEXT("IOcontrol")      // 11
               ,TEXT("Alloc")          // 12
               ,TEXT("Function")       // 13
               ,TEXT("Warning")        // 14
               ,TEXT("Error")          // 15
        }
        ,
            (UARTDRV_ZONES)
};
#endif



/*****************************************************************************
 * Functions
 *****************************************************************************/

    CRegPDDUart::CRegPDDUart(PULONG pRegAddr)
:   m_pReg(pRegAddr)
{
        m_fIsBackedUp    = FALSE;
        m_ULCONBackup    = NULL;
        m_UCONBackup     = NULL;
        m_UFCONBackup    = NULL;
        m_UMCOMBackup    = NULL;
        m_UBRDIVBackup   = NULL;
        m_UDIVSLOTBackup = NULL;
        m_UINTMBackup    = NULL;

        m_BaudRate = NULL;
        m_s5pv210_pclk = PCLKPSYS;
}



BOOL   CRegPDDUart::Init()
{
        BOOL bRet = TRUE;

        if (m_pReg)
        { // Set Value to default.
                Write_ULCON(0);
#ifdef _LOOPBACK 
                Write_UCON((CLKSEL_SCLK | LOOPBACK_ENABLE));
#else
                Write_UCON(0);
#endif 
                Write_UFCON(0);
                Write_UMCON(0);
                bRet = TRUE;
        }
        else
        {
                bRet = FALSE;
        }

        return bRet;
}

void CRegPDDUart::Backup()
{

#ifdef USE_DMA
        DmaPowerDown();
#endif                    
        m_ULCONBackup = Read_ULCON();
        m_UCONBackup = Read_UCON();
        m_UFCONBackup = Read_UFCON();
        m_UMCOMBackup = Read_UMCON();
        m_UBRDIVBackup = Read_UBRDIV();
        m_UDIVSLOTBackup = Read_UDIVSLOT();
        m_UINTMBackup = Read_UINTM();
        m_fIsBackedUp = TRUE;
}


void CRegPDDUart::Restore()
{
        if (m_fIsBackedUp)
        {
                Write_ULCON(m_ULCONBackup );
                //================================ [
                // RxD FIFO Reset
                DWORD dwBit = Read_UFCON();
                // Reset RxD Fifo.
                dwBit |= FCR_RX_FIFO_RESET;
                dwBit &= ~FCR_FIFO_ENABLE;
                Write_UFCON( dwBit);

                // Enable RxD FIFO.
                dwBit &= ~FCR_RX_FIFO_RESET;
                dwBit |= FCR_FIFO_ENABLE;
                Write_UFCON(dwBit); // RxD Fifo Reset Done..
                //================================ ]

                Write_UFCON( m_UFCONBackup );
                Write_UMCON( m_UMCOMBackup );
                Write_UBRDIV( m_UBRDIVBackup);
                Write_UDIVSLOT( m_UDIVSLOTBackup );
                Write_UCON( m_UCONBackup );
                m_fIsBackedUp = FALSE;
#ifdef USE_DMA
                DmaPowerUp();
#endif                  
        }
}


CRegPDDUart::Write_BaudRate(ULONG BaudRate)
{
        DOUBLE Div_val = 0.0;
        UINT UDIVSLOTn = 0;
        UINT UBRDIV = 0;
        BOOL bRet = TRUE;

        DEBUGCHK(BaudRate != NULL);

        DBGMSG(UART_FUNC, (TEXT("[UART] [Write_BaudRate] %d\r\n"), BaudRate));

        if ( (Read_UCON() & CLKSEL_MASK) == CLKSEL_PCLK )
        {
                Div_val = (m_s5pv210_pclk/16.0/BaudRate);
                UBRDIV = (int)Div_val - 1;
                Write_UBRDIV( UBRDIV );
                UDIVSLOTn = (int)( (Div_val - (int)Div_val) * 16);
                Write_UDIVSLOT( UDIVSLOT_TABLE[UDIVSLOTn] );
                DBGMSG( UART_INFO , (TEXT("[UART] [Write_BaudRate] CLK:%d, BaudRate:%d, UBRDIV:%d, UDIVSLOTn:%d\r\n"), m_s5pv210_pclk, BaudRate, UBRDIV, UDIVSLOTn));
                bRet = TRUE;
        }
        else if( (Read_UCON() & CLKSEL_MASK) == CLKSEL_SCLK )
        {
                Div_val = (m_hCPddUart->m_pBSPArgs->SystemClocks.HCLKPSYS_CLK/16.0/BaudRate);
                UBRDIV = (int)Div_val - 1;
                Write_UBRDIV( UBRDIV );
                UDIVSLOTn = (int)( (Div_val - (int)Div_val) * 16);
                Write_UDIVSLOT( UDIVSLOT_TABLE[UDIVSLOTn] );
                DBGMSG( UART_INFO , (TEXT("[UART] [Write_BaudRate] CLK:%d, BaudRate:%d, UBRDIV:%d, UDIVSLOTn:%d\r\n"), m_hCPddUart->m_pBSPArgs->SystemClocks.HCLKPSYS_CLK, BaudRate, UBRDIV, UDIVSLOTn));
                bRet = TRUE;
        }
        else
        {
                DBGMSG(UART_INFO, (TEXT("[UART][ERR] [Write_BaudRate] The serial driver doesn't support an external UART clock.\r\n")));
                ASSERT(FALSE);
                bRet = FALSE;
        }

        return bRet;
}
#ifdef DEBUG
void CRegPDDUart::DumpRegister()
{
        NKDbgPrintfW(TEXT("[UART] [DumpRegister] (ULCON = %x, UCON = %x, UFCON= %x, UMCOM = %x, UBDIV = %x)\r\n"),
                Read_ULCON(),Read_UCON(),Read_UFCON(),Read_UMCON(),Read_UBRDIV());
}
#endif

CPddUart::CPddUart (LPTSTR lpActivePath, PVOID pMdd, PHWOBJ pHwObj )
:   CSerialPDD(lpActivePath,pMdd, pHwObj)
,   m_ActiveReg(HKEY_LOCAL_MACHINE,lpActivePath)
,   CMiniThread (0, TRUE)
{
        m_pRegUart = NULL;
        m_dwSysIntr = MAXDWORD;
        m_hISTEvent = NULL;
        m_dwDevIndex = 0;
        m_pRegVirtualAddr = NULL;
        m_XmitFlushDone =  CreateEvent(0, FALSE, FALSE, NULL);
        m_XmitFifoEnable = FALSE;
        m_dwWaterMark = NULL ;
        m_ClockSelectValid = FALSE;
        m_AutoFlowEnabled = FALSE;
        m_UseAutoFlow = FALSE;
}
CPddUart::~CPddUart()
{
        InitModem(FALSE);
        if (m_hISTEvent)
        {
                m_bTerminated = TRUE;
                ThreadStart();
                SetEvent(m_hISTEvent);
                ThreadTerminated(1000);
                InterruptDisable( m_dwSysIntr );
                CloseHandle(m_hISTEvent);
        };
        if (m_pRegUart)
        {
                delete m_pRegUart;
        }
        if (m_XmitFlushDone)
        {
                CloseHandle(m_XmitFlushDone);
        }
        if (m_pRegVirtualAddr != NULL)
        {
                MmUnmapIoSpace((PVOID)m_pRegVirtualAddr,sizeof(UART_REG));
        }
        if (pVirtDmaRxBufferAddr != NULL)
        {
                HalFreeCommonBuffer(0, 0, PhysDmaRxBufferAddr, pVirtDmaRxBufferAddr, FALSE);
        }
        if (pVirtDmaSrcBufferAddr != NULL)
        {
                HalFreeCommonBuffer(0, 0, PhysDmaSrcBufferAddr, pVirtDmaSrcBufferAddr, FALSE);
        }

        if (pUartDma != NULL)
        {
                if (pUartDma->pDMACregs)
                {
                        MmUnmapIoSpace((PVOID)pUartDma->pDMACregs, sizeof(DMAC_REG));
                        pUartDma->pDMACregs = NULL;
                }

                if (pUartDma->pDMAC0regs)
                {
                        MmUnmapIoSpace((PVOID)pUartDma->pDMAC0regs, sizeof(DMAC_REG));
                        pUartDma->pDMAC0regs = NULL;
                }

                if (pUartDma->pDMAC1regs)
                {
                        MmUnmapIoSpace((PVOID)pUartDma->pDMAC1regs, sizeof(DMAC_REG));
                        pUartDma->pDMAC1regs = NULL;
                }

                if (pUartDma->pSYSCONregs)
                {
                        MmUnmapIoSpace((PVOID)pUartDma->pSYSCONregs, sizeof(CMU_CLK_REG));
                        pUartDma->pSYSCONregs = NULL;
                }

        }

}


BOOL CPddUart::Init()
{
        BOOL bRet = TRUE;

        if ( CSerialPDD::Init() && IsKeyOpened() && m_XmitFlushDone != NULL)
        {
                // IST Setup
                DDKISRINFO ddi;
                if (GetIsrInfo( &ddi) != ERROR_SUCCESS)
                {
                        bRet = FALSE;
                        goto CleanUp;
                }
                m_dwSysIntr = ddi.dwSysintr;
                if (m_dwSysIntr !=  MAXDWORD && m_dwSysIntr != 0 )
                {
                        m_hISTEvent = CreateEvent(0,FALSE,FALSE,NULL);
                }

                if (m_hISTEvent != NULL)
                {
                        if ( !InterruptInitialize(m_dwSysIntr,m_hISTEvent,0,0))
                        {
                                ERRMSG((TEXT("[UART][ERR] [Init] CPddV210Uart::Init InterruptInitialize() Failed \n\r")));
                                bRet = FALSE;
                                goto CleanUp;
                        }

                }
                else
                {
                        bRet = FALSE;
                        goto CleanUp;
                }

                // Get Device Index.
                if ( !GetRegValue(PC_REG_DEVINDEX_VAL_NAME, (PBYTE) &m_dwDevIndex, PC_REG_DEVINDEX_VAL_LEN))
                {
                        m_dwDevIndex = 0;
                }
                if ( !GetRegValue(PC_REG_SERIALWATERMARK_VAL_NAME,(PBYTE) &m_dwWaterMark,PC_REG_SERIALWATERMARKER_VAL_LEN))
                {
                        m_dwWaterMark = DEFAULT_VALUE_WATER_MARK;
                }

		DBGMSG(UART_DBG, (TEXT("m_dwWaterMark = %d"), m_dwWaterMark));                         				
                if ( !GetRegValue(PC_REG_UART_IST_TIMEOUTS_VAL_NAME,(PBYTE) &m_dwISTTimeout, PC_REG_UART_IST_TIMEOUTS_VAL_LEN))
                {
                        m_dwISTTimeout = INFINITE;
                }
                if ( !GetRegValue(PC_REG_UART_MEM_LENGTH_VAL_NAME, (PBYTE) &m_dwMemLen, PC_REG_UART_MEM_LENGTH_VAL_LEN))
                {
                        m_dwMemLen = DEFAULT_VALUE_MEM_LENGTH;
                }
                if ( !GetRegValue(PC_REG_UART_AFC_EN_NAME,(PBYTE) &m_dwAFCEnable, sizeof(DWORD)))
                {
                        m_dwAFCEnable = FALSE;
                }      


#ifdef USE_DMA
                if ( !GetRegValue(PC_REG_UART_TX_DMA_EN_NAME,(PBYTE) &m_dwTXDMAEnable, sizeof(DWORD)))
                {
                        m_dwTXDMAEnable = FALSE;
                }      
#endif         
                if ( !MapHardware()  || !CreateHardwareAccess())
                {
                        bRet = FALSE;
                        goto CleanUp;
                }
#ifdef USE_DMA
                if(m_dwTXDMAEnable)
                {
                        m_IsRXTimeout=FALSE;
                        m_IsRXDMA=FALSE;
                        ERRMSG((L"[UART] DMA init CH:%d \r\n", m_dwDevIndex));
                        InitializeDMA(m_dwDevIndex);
                }
#endif         
                bRet = TRUE;
                goto CleanUp;
        }
        bRet = FALSE;
CleanUp:
        return bRet;
}


BOOL CPddUart::MapHardware()
{
        if (m_pRegVirtualAddr != NULL)
        {
                return TRUE;
        }

        // Get IO Window From Registry
        DDKWINDOWINFO dwi;
        if ( GetWindowInfo( &dwi) != ERROR_SUCCESS ||
                dwi.dwNumMemWindows < 1 ||
                dwi.memWindows[0].dwBase == 0 ||
                dwi.memWindows[0].dwLen < m_dwMemLen)
        {
                return FALSE;
        }

        DWORD dwInterfaceType;
        if (m_ActiveReg.IsKeyOpened() &&
                m_ActiveReg.GetRegValue( DEVLOAD_INTERFACETYPE_VALNAME, (PBYTE) &dwInterfaceType,sizeof(DWORD)))
        {
                dwi.dwInterfaceType = dwInterfaceType;
        }

        // Translate to System Address.
        PHYSICAL_ADDRESS    ioPhysicalBase = { dwi.memWindows[0].dwBase, 0};
        ULONG               inIoSpace = 0;

        // Map it if it is Memory Mapped IO.
        m_pRegVirtualAddr = MmMapIoSpace(ioPhysicalBase, dwi.memWindows[0].dwLen,FALSE);
        if (m_pRegVirtualAddr == NULL)
        {
            ERRMSG((TEXT("[UART][ERR] [MapHardware] MmMapIoSpace Failed \n\r")));
        }

        ioPhysicalBase.LowPart = IMAGE_SHARE_ARGS_PA_START;
        m_pBSPArgs = (BSP_ARGS *)MmMapIoSpace(ioPhysicalBase, sizeof(BSP_ARGS), FALSE);
        if (m_pBSPArgs == NULL)
        {
                ERRMSG((L"[UART:ERR] MapRegisters() : m_pBSPArgs MmMapIoSpace() Failed\n\r"));
        }

        return (m_pRegVirtualAddr != NULL );
}

BOOL CPddUart::CreateHardwareAccess()
{
        BOOL bRet = TRUE;

        if (m_pRegUart)
        {
                bRet = TRUE;
                goto CleanUp;
        }
        if (m_pRegVirtualAddr != NULL)
        {
                m_pRegUart = new CRegPDDUart((PULONG)m_pRegVirtualAddr);
                if (m_pRegUart && !m_pRegUart->Init())
                {
                        delete m_pRegUart ;
                        m_pRegUart = NULL;
                }
                m_pRegUart->m_hCPddUart = this;
        }
        bRet = (m_pRegUart != NULL);

CleanUp:
        return bRet;
}

#define MAX_RETRY 0x1000

void CPddUart::PostInit()
{
        DWORD dwCount = 0;
        m_HardwareLock.Lock();
#ifdef _LOOPBACK 
        m_pRegUart->Write_UCON((CLKSEL_SCLK | LOOPBACK_ENABLE));
#else    
        m_pRegUart->Write_UCON(0); // Set to Default;
#endif     
        DisableInterrupt(INT_RXD | INT_TXD | INT_ERR | INT_MODEM);
        // Mask all interrupt.
        while ((GetInterruptStatus() & (INT_RXD | INT_TXD | INT_ERR | INT_MODEM)) != 0 && dwCount <MAX_RETRY)
        {
                InitReceive(TRUE);
                InitLine(TRUE);
                ClearInterrupt(INT_RXD | INT_TXD | INT_ERR | INT_MODEM);
                dwCount++;
        }
        ASSERT((GetInterruptStatus() & (INT_RXD | INT_TXD | INT_ERR | INT_MODEM)) == 0);
        m_HardwareLock.Unlock();
        CSerialPDD::PostInit();
        CeSetPriority(m_dwPriority256);
#ifdef DEBUG
        if ( ZONE_INIT )
        {
                m_pRegUart->DumpRegister();
        }
#endif
        ThreadStart();  // Start IST.
}


DWORD CPddUart::ThreadRun()
{
        DWORD dwData;
        DWORD interrupts = 0;

        while ( m_hISTEvent != NULL && !IsTerminated() )
        {
                if ( WaitForSingleObject( m_hISTEvent, m_dwISTTimeout) == WAIT_OBJECT_0)
                {
                        m_HardwareLock.Lock();

                        while ( !IsTerminated() )
                        {
                                dwData = ( GetInterruptStatus() & (INT_RXD | INT_TXD | INT_ERR | INT_MODEM) );
                                if (dwData)
                                {
                                        DBGMSG(UART_USR3, (TEXT("[UART] [ThreadRun] INT = %x device: %d\r\n"), dwData,m_dwDevIndex));

                                        interrupts = 0;

                                        if ( m_dwAFCEnable && (m_dwDevIndex == 0 || m_dwDevIndex == 1 || m_dwDevIndex == 2))
                                        {
                                            if (((dwData & INT_RXD) == INT_RXD) && m_RxInterrupt)
                                        {
                                                interrupts |= INTR_RX;
#ifdef USE_UART_RX_DMA
                                                        if((m_pRegUart->Read_UFSTAT() & FSR_RX_FIFO_COUNT_MASK)<DMA_RX_TRIGGER_LEVEL)
                                                        {
                                                            DBGMSG(UART_USR3,(TEXT("[UART] T0\r\n"))); 
                                                            SetEvent(pUartDma->hRxDmaTimeoutEvent);
                                                        }
#endif 
                                        }
                                        }
                                        else
                                        {
                                            if (((dwData & INT_RXD) == INT_RXD))
                                            {
                                                interrupts |= INTR_RX;
#ifdef USE_UART_RX_DMA
                                                        if((m_pRegUart->Read_UFSTAT() & FSR_RX_FIFO_COUNT_MASK)<DMA_RX_TRIGGER_LEVEL)
                                                        {
                                                            DBGMSG(UART_USR3,(TEXT("[UART] T1 (%d)\r\n"), (m_pRegUart->Read_UFSTAT() & FSR_RX_FIFO_COUNT_MASK))); 
                                                            SetEvent(pUartDma->hRxDmaTimeoutEvent);
                                                        }
#endif
                                            }
                                        }       
                                        if ((dwData & INT_TXD) == INT_TXD)
                                        {
                                                interrupts |= INTR_TX;
                                        }
                                        if ((dwData & INT_ERR) == INT_ERR)
                                        {
                                                interrupts |= INTR_LINE | INTR_RX;
#ifdef USE_UART_RX_DMA
                                                if((m_pRegUart->Read_UFSTAT() & FSR_RX_FIFO_COUNT_MASK)>0)
                                                {
                                                    DBGMSG(UART_USR3,(TEXT("[UART] T2\r\n"))); 
                                                    DBGMSG(UART_USR3,(TEXT("[UART] INT = %x,MASK = %d\r\n"),m_pRegUart->Read_UINTP(), m_pRegUart->Read_UINTM()) );
                                                    SetEvent(pUartDma->hRxDmaTimeoutEvent);
                                                }
#endif
                                        }
                                        if ((dwData & INT_MODEM) == INT_MODEM)
                                        {
                                                interrupts |= INTR_MODEM;
                                        }

                                        NotifyPDDInterrupt( (INTERRUPT_TYPE)interrupts );

                                        ClearInterrupt(dwData);
                                }
                                else
                                {
                                        break;
                                }
                        }

                        m_HardwareLock.Unlock();

                        InterruptDone(m_dwSysIntr);
                }
                else
                {
                        DBGMSG(UART_INFO,(TEXT("[UART] [ThreadRun] timeout INT = %x,MASK = %d\r\n"),m_pRegUart->Read_UINTP(), m_pRegUart->Read_UINTM()) );
                        ASSERT(FALSE);
                }
        }

        return 1;
}


BOOL CPddUart::InitialEnableInterrupt(BOOL bEnable )
{
        m_HardwareLock.Lock();
        if (bEnable)
        {
#ifdef UART_MODEM            
               EnableInterrupt(INT_RXD | INT_ERR | INT_MODEM);
#else
                EnableInterrupt(INT_RXD | INT_ERR);
#endif 
        }
        else
        {
                DisableInterrupt(INT_RXD | INT_ERR | INT_MODEM);
        }
        m_HardwareLock.Unlock();
        return TRUE;
}

BOOL  CPddUart::InitXmit(BOOL bInit)
{
        DWORD dwTicks = 0;
        DWORD dwUTRState = 0;

        if (bInit)
        {
                m_HardwareLock.Lock();
                DWORD dwBit = m_pRegUart->Read_UCON();

#if UART_SCLK
                // Set UART CLK.
                dwBit &= ~(CLKSEL_MASK);
                dwBit |= (CLKSEL_SCLK);
#endif
                if (m_ClockSelectValid) 
                {
                        dwBit &= ~(CLKSEL_MASK);
                        dwBit |= (m_ClockSelect);
                }

                dwBit &= ~( TX_INT_TYPE_MASK | TX_MODE_MASK );
                dwBit |= ( TX_INT_TYPE_LEVEL | TX_INT_POLL );
                m_pRegUart->Write_UCON(dwBit );

                // Reset Xmit Fifo.
                dwBit = m_pRegUart->Read_UFCON();
                dwBit |= (FCR_TX_FIFO_RESET);
                dwBit &= ~(FCR_FIFO_ENABLE );
                m_pRegUart->Write_UFCON( dwBit);

                // Set Trigger level to 16.
                dwBit &= ~(FCR_TX_FIFO_TRIG_MASK);
                dwBit |= (FCR_TX_FIFO_TRIG_16);
                m_pRegUart->Write_UFCON(dwBit);

                // Enable Xmit FIFO.
                dwBit &= ~(FCR_TX_FIFO_RESET);
                dwBit |= (FCR_FIFO_ENABLE);
                m_pRegUart->Write_UFCON(dwBit);

                if (m_dwAFCEnable && (m_dwDevIndex == 0 || m_dwDevIndex == 1 || m_dwDevIndex == 2))                
                {
                        // Enabling AFC
                        m_pRegUart->Write_UMCON( m_pRegUart->Read_UMCON() | MCR_AUTO_FLOW_ENABLE);
                }

                m_HardwareLock.Unlock();

#ifdef USE_DMA
                if(m_dwTXDMAEnable)
                {
                        InitXmitDMA();
                }
#endif  

        }
        else
        { 
                // We have to make sure the xmit is complete because MDD will shut down the device after this return
                while (dwTicks < TIMEOUT_TX_EMPTY 
                        && ((dwUTRState = m_pRegUart->Read_UTRSTAT()) & (TRANSMITTER_STATUS_MASK | TX_BUFFER_STATUS_MASK))
                        != (TRANSMITTER_EMPTY | TX_BUFFER_EMPTY))
                { 
                        // Transmitter empty is not true
                        DBGMSG(UART_USR1, (TEXT("CPddUart::InitXmit ! Wait for UTRSTAT = %x clear.\r\n"), dwUTRState));
                        Sleep(5);
                        dwTicks += 5;
                }
        }
        return TRUE;
}


DWORD   CPddUart::GetWriteableSize()
{
        DWORD dwWriteSize = 0;
        DWORD dwUfState = m_pRegUart->Read_UFSTAT() ;
        if ((dwUfState & FSR_TX_FIFO_MASK) == FSR_TX_FIFO_NOT_FULL)
        { // It is not full.
                dwUfState = ((dwUfState & FSR_TX_FIFO_COUNT_MASK) >> FSR_TX_FIFO_COUNT_SHIFT); // It is fifo count.
                if (dwUfState < (GetFifoDepth() - 1))
                {
                        dwWriteSize = (GetFifoDepth() - 1) - dwUfState;
                }
        }
        return dwWriteSize;
}


DWORD   CPddUart::GetFifoDepth()
{
        DWORD dwFifoDepth = 0;
                
        if (m_dwDevIndex == 0)
        {
                        dwFifoDepth = 256;
        }
                else if (m_dwDevIndex == 1)
                        {
                        dwFifoDepth = 64;
        }
                else if (m_dwDevIndex == 2)
        {
                        dwFifoDepth = 16;
        }
                else if (m_dwDevIndex == 3)
        {
                        dwFifoDepth = 16;
        }
                else
        {
            ERRMSG((TEXT(" CPddUart::GetFifoDepth Invalid Index\r\n"))); 
                        dwFifoDepth = 0;
        }

        return dwFifoDepth;
}
#ifdef USE_DMA
void    CPddUart::XmitInterruptHandler(PUCHAR pTxBuffer, ULONG *pBuffLen)
{
        BOOL bRet;
        PREFAST_DEBUGCHK(pBuffLen != NULL);
        m_HardwareLock.Lock();

        DBGMSG(UART_FUNC,(TEXT(" CPddUart::XmitInterruptHandler\r\n"))); 
        //wait until TX FIFO empty 
        // 6ms : 38400 baudrate/64Byte
        // 85us : 3M baudrate/64Byte    
        // It affects when BT power on and off at low baudrate  
        if((m_dwTXDMAEnable == 1) && (*pBuffLen < TX_FIFO_LEN))
        {
                while(((m_pRegUart->Read_UFSTAT() & FSR_TX_FIFO_COUNT_MASK) >> FSR_TX_FIFO_COUNT_SHIFT))
                {
                        DBGMSG(UART_USR3, (TEXT("[UART] Wait \r\n")));
                }
        } 

        if (*pBuffLen ==  0)
        {
                EnableXmitInterrupt(FALSE);
        }
        else
        {
                DEBUGCHK(pTxBuffer);
                PulseEvent(m_XmitFlushDone);
                DWORD dwDataAvaiable = *pBuffLen;
                *pBuffLen = 0;
                DMA_ERROR dma_error_value = DMA_SUCCESS;

                Rx_Pause(TRUE);
                if ((m_DCB.fOutxCtsFlow && IsCTSOff()) ||(m_DCB.fOutxDsrFlow && IsDSROff()))
                { 
                        // We are in flow off
                        DBGMSG(UART_USR3, (TEXT("[UART] [CPddUart::XmitInterruptHandler] Flow Off, Data Discard.\r\n")));
                        EnableXmitInterrupt(FALSE);
                }
                else
                {
                        if((m_dwTXDMAEnable == 1) && (dwDataAvaiable > TX_FIFO_LEN))                  
                        {
                                DWORD dwDmaLen = (dwDataAvaiable > Buffer_Mem_Size)? Buffer_Mem_Size:dwDataAvaiable;

                                DBGMSG(UART_USR3,(TEXT("[UART] Thread for TX : USE DMA (TxCount : %d) \r\n"),dwDmaLen));

                                if(dwDmaLen > 0)
                                {
                                        bRet = StartXmitDMA(pTxBuffer, dwDmaLen);
                                        if( !bRet)
                                        {
                                                goto LEAVEWRITE;
                                        }
                                        dwDataAvaiable -= dwDmaLen;
                                        pTxBuffer = (PUCHAR)(((PUINT) pTxBuffer) + dwDmaLen);
                                }

                                *pBuffLen = dwDmaLen;
                                EnableXmitInterrupt(TRUE); 
                        }
                        else
                        {
                                DWORD dwWriteSize = GetWriteableSize();

                                DBGMSG(UART_USR3,(TEXT("[UART] XmitInterruptHandler ! WriteableSize = %x to FIFO,dwDataAvaiable= %x\r\n"),
                                            dwWriteSize,dwDataAvaiable));

                                for (DWORD dwByteWrite = 0; dwByteWrite<dwWriteSize && dwDataAvaiable != 0;dwByteWrite++)
                                {
                                        m_pRegUart->Write_UTXH(*pTxBuffer);
                                        pTxBuffer++;
                                        dwDataAvaiable--;
                                }

                                DBGMSG(UART_USR3,(TEXT("[UART] XmitInterruptHandler ! Write %d byte to FIFO\r\n"),dwByteWrite));

                                *pBuffLen = dwByteWrite;
                                EnableXmitInterrupt(TRUE);        
                        }                

                }
LEAVEWRITE:
                ClearInterrupt(INT_TXD);

                if (m_pRegUart->Read_ULCON() & LCR_IR_MODE)
                {
                        while((m_pRegUart->Read_UFSTAT() & FSR_TX_FIFO_COUNT_MASK) >> FSR_TX_FIFO_COUNT_SHIFT)
                        {
                                ;
                        }
                }
                Rx_Pause(FALSE);
        }
        m_HardwareLock.Unlock();
}

#else//#ifdef USE_DMA

void    CPddUart::XmitInterruptHandler(PUCHAR pTxBuffer, ULONG *pBuffLen)
{
        PREFAST_DEBUGCHK(pBuffLen != NULL);
        m_HardwareLock.Lock();

        DBGMSG(UART_USR3,(TEXT(" +INT\r\n")));       
        if (*pBuffLen == 0)
        {
                EnableXmitInterrupt(FALSE);
        }
        else
        {
                DEBUGCHK(pTxBuffer);
                PulseEvent(m_XmitFlushDone);
                DWORD dwDataAvaiable = *pBuffLen;
                *pBuffLen = 0;

                Rx_Pause(TRUE);
                if ( !m_AutoFlowEnabled && ((m_DCB.fOutxCtsFlow && IsCTSOff()) ||(m_DCB.fOutxDsrFlow && IsDSROff())))
                { // We are in flow off
                        DBGMSG(UART_USR3, (TEXT("CPddUart::XmitInterruptHandler ! Flow Off, Data Discard.\r\n")));
                        EnableXmitInterrupt(FALSE);
                }
                else
                {
                        DWORD dwWriteSize = GetWriteableSize();
                        DBGMSG(UART_USR3,(TEXT("CPddUart::XmitInterruptHandler ! WriteableSize = %x to FIFO,dwDataAvaiable= %x\r\n"),
                                    dwWriteSize,dwDataAvaiable));
                        for (DWORD dwByteWrite = 0; dwByteWrite<dwWriteSize && dwDataAvaiable != 0;dwByteWrite++)
                        {
                                m_pRegUart->Write_UTXH(*pTxBuffer);
                                pTxBuffer ++;
                                dwDataAvaiable--;
                        }
                        DBGMSG(UART_USR3,(TEXT("CPddUart::XmitInterruptHandler ! Write %d byte to FIFO\r\n"),dwByteWrite));
                        *pBuffLen = dwByteWrite;
                        EnableXmitInterrupt(TRUE);
                }
                ClearInterrupt(INT_TXD);

                if (m_pRegUart->Read_ULCON() & LCR_IR_MODE)
                {
                        while((m_pRegUart->Read_UFSTAT() & FSR_TX_FIFO_COUNT_MASK) >> FSR_TX_FIFO_COUNT_SHIFT);
                }
                Rx_Pause(FALSE);
        }
        m_HardwareLock.Unlock();
}
#endif //#ifdef USE_DMA


void    CPddUart::XmitComChar(UCHAR ComChar)
{
        // This function has to poll until the Data can be sent out.
        BOOL bDone = FALSE;
        do
        {
                m_HardwareLock.Lock();
                if ( GetWriteableSize() != 0 )
                {  // If not full
                        m_pRegUart->Write_UTXH(ComChar);
                        bDone = TRUE;
                }
                else
                {
                        EnableXmitInterrupt(TRUE);
                }
                m_HardwareLock.Unlock();
                if ( !bDone)
                {
                        WaitForSingleObject(m_XmitFlushDone, (ULONG)1000);
                }
        }while ( !bDone);
}


BOOL    CPddUart::EnableXmitInterrupt(BOOL fEnable)
{
        m_HardwareLock.Lock();
        if (fEnable)
        {
                EnableInterrupt(INT_TXD);
        }
        else
        {
                DisableInterrupt(INT_TXD);
        }
        m_HardwareLock.Unlock();
        return TRUE;
}


BOOL  CPddUart::CancelXmit()
{
        return InitXmit(TRUE);
}

static PAIRS s_HighWaterPairs0[] =
{
#if (S5PV210_EVT==0)
        {0, 1 },
        {1, 4 },
        {2, 8 },
        {3, 16 },
        {4, 32 },
        {5, 64 },
        {6, 128 },
        {7, 256 }
#else
        {0, 32 },
        {1, 64 },
        {2, 96 },
        {3, 128 },
        {4, 160 },
        {5, 192 },
        {6, 224 },
        {7, 256 }
#endif
};

static PAIRS s_HighWaterPairs1[] =
{
#if (S5PV210_EVT==0)
        {0, 1 },
        {1, 4 },
        {2, 8 },
        {3, 16 },
        {4, 32 },
        {5, 64 },
        {6, 128 },
        {7, 256 }
#else
        {0, 8 },
        {1, 16 },
        {2, 24 },
        {3, 32 },
        {4, 40 },
        {5, 48 },
        {6, 56 },
        {7, 64 }
#endif
};

static PAIRS s_HighWaterPairs2[] =
{
#if (S5PV210_EVT==0)
        {0, 1 },
        {1, 4 },
        {2, 8 },
        {3, 16 },
        {4, 32 },
        {5, 64 },
        {6, 128 },
        {7, 256 }
#else
        {0, 2 },
        {1, 4 },
        {2, 6 },
        {3, 8 },
        {4, 10 },
        {5, 12 },
        {6, 14 },
        {7, 16 }
#endif
};

static PAIRS s_HighWaterPairs3[] =
{
#if (S5PV210_EVT==0)
        {0, 1 },
        {1, 4 },
        {2, 8 },
        {3, 16 },
        {4, 32 },
        {5, 64 },
        {6, 128 },
        {7, 256 }
#else
        {0, 2 },
        {1, 4 },
        {2, 6 },
        {3, 8 },
        {4, 10 },
        {5, 12 },
        {6, 14 },
        {7, 16 }
#endif
};

BYTE  CPddUart::GetWaterMarkBit()
{
        PAIRS * pHighWaterPairs = s_HighWaterPairs0;
		
        if (m_dwDevIndex == 0)
        {
                ERRMSG((TEXT(" m_dwDevIndex == 0\r\n")));         
                pHighWaterPairs = s_HighWaterPairs0;
        }
        else if (m_dwDevIndex == 1)
        {
                ERRMSG((TEXT(" m_dwDevIndex == 1\r\n")));                 
                pHighWaterPairs = s_HighWaterPairs1;
        }
        else if (m_dwDevIndex == 2)
        {
                ERRMSG((TEXT(" m_dwDevIndex == 2\r\n")));         
                pHighWaterPairs = s_HighWaterPairs2;
        }
        else if (m_dwDevIndex == 3)
        {
                ERRMSG((TEXT(" m_dwDevIndex == 3\r\n")));         
                pHighWaterPairs = s_HighWaterPairs3;
        }
        else
        {
                ERRMSG((TEXT(" CPddUart::GetWaterMarkBit Invalid Index\r\n"))); 
		}

        BYTE bReturnKey = (BYTE)pHighWaterPairs[0].Key;
        for (DWORD dwIndex = 8 - 1 ; dwIndex != 0; dwIndex --)
        {
				DBGMSG(UART_DBG, (TEXT("WaterMark Bit = %d, m_dwWaterMark = %d, pHighWaterPairs[dwIndex].AssociatedValue = %d\n"), pHighWaterPairs[dwIndex].Key, m_dwWaterMark,pHighWaterPairs[dwIndex].AssociatedValue));                                 
                if (m_dwWaterMark >= (pHighWaterPairs[dwIndex].AssociatedValue))
                {
                        bReturnKey = (BYTE)pHighWaterPairs[dwIndex].Key;
						DBGMSG(UART_DBG, (TEXT("WaterMark Bit Position = %d, m_dwWaterMark = %d\n"), pHighWaterPairs[dwIndex].Key, m_dwWaterMark));                         
                        break;
                }
        }
        return bReturnKey;
}


DWORD   CPddUart::GetWaterMark()
{
        PAIRS * pHighWaterPairs = s_HighWaterPairs0;
		
        if (m_dwDevIndex == 0)
        {
                pHighWaterPairs = s_HighWaterPairs0;
        }
        else if (m_dwDevIndex == 1)
        {
                pHighWaterPairs = s_HighWaterPairs1;
        }
        else if (m_dwDevIndex == 2)
        {
                pHighWaterPairs = s_HighWaterPairs2;
        }
        else if (m_dwDevIndex == 3)
        {
                pHighWaterPairs = s_HighWaterPairs3;
        }
        else
        {
                ERRMSG((TEXT(" CPddUart::GetWaterMarkBit Invalid Index\r\n"))); 
		}

        BYTE bReturnValue = (BYTE)pHighWaterPairs[0].AssociatedValue;
        for (DWORD dwIndex = dim(pHighWaterPairs)-1 ; dwIndex != 0; dwIndex --)
                {
        
                if (m_dwWaterMark >= pHighWaterPairs[dwIndex].AssociatedValue)
                {
                        bReturnValue = (BYTE)pHighWaterPairs[dwIndex].AssociatedValue;
						DBGMSG(UART_DBG, (TEXT("WaterMark Byte Value = %d\n"), pHighWaterPairs[dwIndex].AssociatedValue));                         						
                        break;
                }
        }
        return bReturnValue;
}

// Receive
BOOL    CPddUart::InitReceive(BOOL bInit)
{
        m_HardwareLock.Lock();
        if (bInit)
        {
                BYTE uWarterMarkBit = GetWaterMarkBit();
#ifdef USE_UART_RX_DMA
        if (m_dwDevIndex == 0)
            uWarterMarkBit= 0;//UART RX trigger level UART0:32B
        else if (m_dwDevIndex == 1)
            uWarterMarkBit= 3;
        else
            uWarterMarkBit= 0;        
#endif 
                // Setup Receive FIFO.
                // Reset Receive Fifo.
                DWORD dwBit = m_pRegUart->Read_UFCON();
                dwBit |= (FCR_RX_FIFO_RESET);
                dwBit &= ~(FCR_FIFO_ENABLE);
                m_pRegUart->Write_UFCON( dwBit);
                // Set Trigger level to WaterMark.
                dwBit &= ~(FCR_RX_FIFO_TRIG_MASK);
                dwBit |= (uWarterMarkBit << FCR_RX_FIFO_TRIG_SHIFT);
                m_pRegUart->Write_UFCON(dwBit);
                // Enable Receive FIFO.
                dwBit &= ~(FCR_RX_FIFO_RESET);
                dwBit |= (FCR_FIFO_ENABLE);
                m_pRegUart->Write_UFCON(dwBit); // Receive FIFO Reset Done..
                m_pRegUart->Read_UERSTAT(); // Clean Line Interrupt.
                dwBit = m_pRegUart->Read_UCON();
#if UART_SCLK
                // Set UART CLK.
                dwBit &= ~(CLKSEL_MASK);
                dwBit |= (CLKSEL_SCLK);
#endif
                if (m_ClockSelectValid) {
                        dwBit &= ~(CLKSEL_MASK);
                        dwBit |= (m_ClockSelect);
                }
                // Set Rx Inerrupt Request Type, Rx Timeout, Rx Interrupt/Polling Mode.
                dwBit &= ~(RX_INT_TYPE_MASK |RX_TIMEOUT_MASK |RX_MODE_MASK);
#ifdef USE_UART_RX_DMA                                
                dwBit |= (RX_INT_TYPE_LEVEL |RX_TIMEOUT_ENABLE |RX_DMA_MODE|RX_ERR_INT_ENABLE|RX_DMA_BURST_32_BYTE);//Single
#else
                dwBit |= (RX_INT_TYPE_LEVEL |RX_TIMEOUT_ENABLE |RX_INT_POLL);
#endif 
                m_pRegUart->Write_UCON(dwBit);

                if (m_dwAFCEnable && (m_dwDevIndex == 0 || m_dwDevIndex == 1 || m_dwDevIndex == 2))                
                {
                        // Enabling AFC
                        m_pRegUart->Write_UMCON( m_pRegUart->Read_UMCON() | MCR_AUTO_FLOW_ENABLE);
                }
#ifdef USE_UART_RX_DMA
                if(m_dwTXDMAEnable)
                {
                        InitRxDMA();
                        StartRxDMA(RX_DMA_TRANS_SIZE);
                }
#endif                 

                EnableInterrupt(INT_RXD | INT_ERR );
        }
        else
        {
                DisableInterrupt(INT_RXD | INT_ERR );
        }
        m_HardwareLock.Unlock();
        return TRUE;
}

#ifdef USE_UART_RX_DMA
ULONG   CPddUart::ReceiveInterruptHandler(PUCHAR pRxBuffer,ULONG *pBufflen)
{
        DEBUGMSG(ZONE_THREAD |ZONE_READ,(TEXT("[UART] [CPddUart::ReceiveInterruptHandler] pRxBuffer = %x,*pBufflen= %x\r\n"),
                    pRxBuffer,pBufflen != NULL?*pBufflen:0));
        
        DWORD dwBytesDropped = 0;
        BOOL bRet=0;
        dwRxLen=0;
        DWORD dwRoomAvail=0;
        
        if (pRxBuffer && pBufflen )
        {
                DWORD dwBytesStored = 0 ;
                DWORD dwRoomLeft = *pBufflen;
                m_bReceivedCanceled = FALSE;
                m_HardwareLock.Lock();

                DBGMSG(DMA_MSG, (TEXT("[UART] +Receive [R:%d] \r\n"), dwRoomLeft));

                if(dwRoomLeft<RX_DMA_TRANS_SIZE)
		   {
			 if(m_IsRXDMA)
                    {
                        DBGMSG(DMA_MSG, (TEXT("[UART] +Trailing \r\n")));
                        bRet=HandleRXDMA(pRxBuffer, dwRoomLeft);
                		if(bRet==FALSE)
                         {
                         		dwBytesStored = 0;
                                ERRMSG((TEXT("[UART] ***** Fail Trailing *****  \n\r")));
                         } 
	                            
				dwRoomLeft -= dwRxLen;
				dwBytesStored += dwRxLen;
                          pRxBuffer+=dwRxLen;
                    }

			while (dwRoomLeft && !m_bReceivedCanceled)
                   {
                        ULONG ulUFSTATE = m_pRegUart->Read_UFSTAT();
                        DWORD dwNumRxInFifo = ((ulUFSTATE & FSR_RX_FIFO_COUNT_MASK) >> FSR_RX_FIFO_COUNT_SHIFT);
                        if ((ulUFSTATE & (FSR_RX_FIFO_MASK)) == FSR_RX_FIFO_FULL) // Overflow.;
                        {
                                dwNumRxInFifo = GetFifoDepth();
                        }
                        DBGMSG(UART_USR3,(TEXT("CPddUart::ReceiveInterruptHandler ulUFSTATE = %x,UTRSTAT= %x, dwNumRxInFifo= %X\r\n"),
                                                ulUFSTATE, m_pRegUart->Read_UTRSTAT(), dwNumRxInFifo));
                        if (dwNumRxInFifo)
                        {
                            	DBGMSG(DMA_MSG, (TEXT("[UART] CPU [F:%d] [R:%d] \r\n"), dwNumRxInFifo, dwRoomLeft));
                                ASSERT((m_pRegUart->Read_UTRSTAT() & RX_BUFFER_STATUS_MASK) != RX_BUFFER_EMPTY);
                                while (dwNumRxInFifo && dwRoomLeft)
                                {
                                        UCHAR uLineStatus = GetLineStatus();
                                        UCHAR uData = m_pRegUart->Read_URXH();
                                        if (DataReplaced( &uData,(uLineStatus & ESR_PARITY_ERROR) != 0))
                                        {
                                                *pRxBuffer++ = uData;
                                                dwRoomLeft--;
                                                dwBytesStored++;
                                        }
                                        dwNumRxInFifo --;
                                        if(dwRoomLeft==0)
                                        {
                                            StartRxDMA(RX_DMA_TRANS_SIZE);//asdf check later, initial  roomleft size 
                                        }
                                }
                        }
                        else
                        {
                                break;
                        }
                   }//while
                    
                }//if(dwRoomLeft>RX_DMA_TRANS_SIZE)
                else //if(dwRoomLeft>RX_DMA_TRANS_SIZE)
                {
			while (dwRoomLeft && !m_bReceivedCanceled)
                   {
                        ULONG ulUFSTATE = m_pRegUart->Read_UFSTAT();
                        DWORD dwNumRxInFifo = ((ulUFSTATE & FSR_RX_FIFO_COUNT_MASK) >> FSR_RX_FIFO_COUNT_SHIFT);
                        if ((ulUFSTATE & (FSR_RX_FIFO_MASK)) == FSR_RX_FIFO_FULL) // Overflow.;
                        {
                                dwNumRxInFifo = GetFifoDepth();
                        }
                        DEBUGMSG(ZONE_THREAD |ZONE_READ,(TEXT("CPddUart::ReceiveInterruptHandler ulUFSTATE = %x,UTRSTAT= %x, dwNumRxInFifo= %X\r\n"),
                                    ulUFSTATE, m_pRegUart->Read_UTRSTAT(), dwNumRxInFifo));
                        if (dwNumRxInFifo)
                        {                          
                                ASSERT((m_pRegUart->Read_UTRSTAT() & RX_BUFFER_STATUS_MASK) != RX_BUFFER_EMPTY);
					DBGMSG(DMA_MSG, (TEXT("[UART] DMA [F:%d] [R:%d] \r\n"), dwNumRxInFifo, dwRoomLeft));
					bRet=HandleRXDMA(pRxBuffer, dwRoomLeft);
                    		if(bRet==FALSE)
	                         {
	                         		dwBytesStored = 0;
	                                break;
	                         } 
		                            
					dwRoomLeft -= dwRxLen;
					dwBytesStored += dwRxLen;
                                pRxBuffer+=dwRxLen;
		                    
                                if(m_IsRXTimeout&&dwRoomLeft)
					{
                    		    
                                    ulUFSTATE = m_pRegUart->Read_UFSTAT();
                                    dwNumRxInFifo = ((ulUFSTATE & FSR_RX_FIFO_COUNT_MASK) >> FSR_RX_FIFO_COUNT_SHIFT);
                                    DBGMSG(DMA_MSG_MDD, (TEXT("[UART] -2 [F:%d] [R:%d] \r\n"), dwNumRxInFifo, dwRoomLeft));
                                    while (dwNumRxInFifo)
                                    {
                                            UCHAR uLineStatus = GetLineStatus();
                                            UCHAR uData = m_pRegUart->Read_URXH();
                                            if (DataReplaced( &uData,(uLineStatus & ESR_PARITY_ERROR) != 0))
                                            {
                                                    *pRxBuffer++ = uData;
                                                    dwRoomLeft--;
                                                    dwBytesStored++;
                                            }
                                            dwNumRxInFifo --;
                                    }
					} 
                                else
					{
	                    		//DBGMSG(DMA_MSG, (TEXT("[UART] E : Receive no Timeout \r\n")));
					}

	                            m_IsRXTimeout=FALSE;
	                            
                                 if(dwRoomLeft>RX_DMA_TRANS_SIZE)
                                 {    
                                    StartRxDMA(RX_DMA_TRANS_SIZE);
                                 }
                                 else// if(dwRoomLeft>DMA_TRANS_SIZE)
                                 {
                                    dwRoomAvail = (dwRoomLeft/DMA_TRANS_SIZE)*DMA_TRANS_SIZE;
                                    //dwRoomAvail = (dwRoomLeft>>5)*28;
                                    StartRxDMA(dwRoomAvail);
                                 }
	                                 
	                            dwRoomLeft=0;	//break;//asdf 
                                                   
                        }
                        else
                        {
                                break;
                        }
                   }

                    
                }//if(dwRoomLeft>RX_DMA_TRANS_SIZE)


                if (m_bReceivedCanceled)
                {
                        dwBytesStored = 0;
                }

                m_HardwareLock.Unlock();
                *pBufflen = dwBytesStored;
        }
        else
        {
                ASSERT(FALSE);
        }
        //DBGMSG(DMA_MSG,(TEXT("[UART] [CPddUart::ReceiveInterruptHandler] pRxBuffer = 0x%x,*pBufflen= %d,dwBytesDropped= %d\r\n"),
        //            pRxBuffer,pBufflen != NULL?*pBufflen:0,dwBytesDropped));
        DBGMSG(DMA_MSG,(TEXT("[UART] Total Read L:%d \r\n"), pBufflen != NULL?*pBufflen:0));
        return dwBytesDropped;
}
#else//#ifdef USE_UART_RX_DMA
ULONG   CPddUart::ReceiveInterruptHandler(PUCHAR pRxBuffer,ULONG *pBufflen)
{
        DBGMSG(UART_USR3,(TEXT("[UART] [CPddUart::ReceiveInterruptHandler] pRxBuffer = %x,*pBufflen= %x\r\n"),
                    pRxBuffer,pBufflen != NULL?*pBufflen:0));
        DWORD dwBytesDropped = 0;
        if (pRxBuffer && pBufflen )
        {
                DWORD dwBytesStored = 0 ;
                DWORD dwRoomLeft = *pBufflen;
                m_bReceivedCanceled = FALSE;
                m_HardwareLock.Lock();

                while (dwRoomLeft && !m_bReceivedCanceled)
                {
                        ULONG ulUFSTATE = m_pRegUart->Read_UFSTAT();
                        DWORD dwNumRxInFifo = ((ulUFSTATE & FSR_RX_FIFO_COUNT_MASK) >> FSR_RX_FIFO_COUNT_SHIFT);
                        if ((ulUFSTATE & (FSR_RX_FIFO_MASK)) == FSR_RX_FIFO_FULL) // Overflow.;
                        {
                                dwNumRxInFifo = GetFifoDepth();
                        }
                        DBGMSG(UART_USR3,(TEXT("CPddUart::ReceiveInterruptHandler ulUFSTATE = %x,UTRSTAT= %x, dwNumRxInFifo= %X\r\n"),
                                    ulUFSTATE, m_pRegUart->Read_UTRSTAT(), dwNumRxInFifo));
                        if (dwNumRxInFifo)
                        {
                                ASSERT((m_pRegUart->Read_UTRSTAT() & RX_BUFFER_STATUS_MASK) != RX_BUFFER_EMPTY);
                                while (dwNumRxInFifo && dwRoomLeft)
                                {
                                        UCHAR uLineStatus = GetLineStatus();
                                        UCHAR uData = m_pRegUart->Read_URXH();
                                        if (DataReplaced( &uData,(uLineStatus & ESR_PARITY_ERROR) != 0))
                                        {
                                                *pRxBuffer++ = uData;
                                                dwRoomLeft--;
                                                dwBytesStored++;
                                        }
                                        dwNumRxInFifo --;
                                }
                        }
                        else
                        {
                                break;
                        }
                }
                if (m_bReceivedCanceled)
                {
                        dwBytesStored = 0;
                }

                m_HardwareLock.Unlock();
                *pBufflen = dwBytesStored;
        }
        else
        {
                ASSERT(FALSE);
        }
        DBGMSG(UART_USR3,(TEXT("[UART] [CPddUart::ReceiveInterruptHandler] pRxBuffer = %x,*pBufflen= %x,dwBytesDropped= %x\r\n"),
                    pRxBuffer,pBufflen != NULL?*pBufflen:0,dwBytesDropped));
        return dwBytesDropped;
}
#endif //#ifdef USE_UART_RX_DMA
ULONG   CPddUart::CancelReceive()
{
        m_bReceivedCanceled = TRUE;
        m_HardwareLock.Lock();
        InitReceive(TRUE);
        m_HardwareLock.Unlock();
        return 0;
}

BOOL    CPddUart::InitModem(BOOL bInit)
{
        m_HardwareLock.Lock();
        m_pRegUart->Write_UMCON( m_pRegUart->Read_UMCON()
                                |MCR_AUTO_FLOW_DISABLE
                                |MCR_MODEM_INTERRUPT_ENABLE
                                |MCR_SET_RTS);
        m_HardwareLock.Unlock();
        return TRUE;
}

ULONG   CPddUart::GetModemStatus()
{
        m_HardwareLock.Lock();
        ULONG ulReturn = 0 ;
        ULONG Events = 0;
        UINT8 ubModemStatus = (UINT8) m_pRegUart->Read_UMSTAT();
        m_HardwareLock.Unlock();

        // Event Notification.
        if (ubModemStatus & (MSR_DELTA_CTS))
        {
                Events |= EV_CTS;
        }
        if (Events != 0)
        {
                EventCallback(Events);
        }

        // Report Modem Status.
        if ( ubModemStatus & (MSR_CTS))
        {
                ulReturn |= MS_CTS_ON;
        }
        return ulReturn;
}


void    CPddUart::SetRTS(BOOL bSet)
{
        if (m_AutoFlowEnabled) {
                return;
        }
        m_HardwareLock.Lock();
        ULONG ulData = m_pRegUart->Read_UMCON();
        if (bSet)
        {
                ulData |= (MCR_SET_RTS);

                if (m_dwAFCEnable && (m_dwDevIndex == 0 || m_dwDevIndex == 1 || m_dwDevIndex == 2))                
                {
                        m_RxInterrupt = TRUE;
                       //To avoid MDD tossing 16 bytes issue..This is effective when AFC is enabled.
                        EnableInterrupt(1);
                }
                DBGMSG(UART_USR4, (TEXT("1")));                         
        }
        else
        {
                if (m_dwAFCEnable && (m_dwDevIndex == 0 || m_dwDevIndex == 1 || m_dwDevIndex == 2))                
                {
                        m_RxInterrupt = FALSE;
                        //To avoid MDD tossing 16 bytes issue..This is effective when AFC is enabled
                        DisableInterrupt(1);
                }

                ulData &= ~(MCR_SET_RTS);
                DBGMSG(UART_USR4, (TEXT("0")));                                                         
        }
        m_pRegUart->Write_UMCON(ulData);
        m_HardwareLock.Unlock();

}


BOOL CPddUart::InitLine(BOOL bInit)
{
        m_HardwareLock.Lock();
        if  (bInit)
        {
                EnableInterrupt( INT_ERR );
        }
        else
        {
                DisableInterrupt(INT_ERR );
        }
        m_HardwareLock.Unlock();
        return TRUE;
}


BYTE CPddUart::GetLineStatus()
{
        m_HardwareLock.Lock();
        ULONG ulData = m_pRegUart->Read_UERSTAT();
        m_HardwareLock.Unlock();
        ULONG ulError = 0;
        if (ulData & (ESR_OVERRUN_ERROR) )
        {
                ulError |=  CE_OVERRUN;
                ERRMSG((TEXT("[UART] E:Overrun (%d)\r\n"), m_dwDevIndex));

                //Clear RX FIFO and Reset Start      
                DWORD dwNumRxInFifo = ((m_pRegUart->Read_UFSTAT() & FSR_RX_FIFO_COUNT_MASK) >> FSR_RX_FIFO_COUNT_SHIFT);    
                UCHAR uData;
                while (dwNumRxInFifo)
                {
                        uData = m_pRegUart->Read_URXH();
                        dwNumRxInFifo --;
                }
                InitReceive(TRUE);    
                //Clear RX FIFO and Reset End
        }
        if (ulData & (ESR_PARITY_ERROR))
        {
                ulError |= CE_RXPARITY;
                ERRMSG((TEXT("[UART] E:Parity (%d)\r\n"), m_dwDevIndex));
        }
        if (ulData & (ESR_FRAME_ERROR))
        {
                ulError |=  CE_FRAME;
                ERRMSG((TEXT("[UART] E:Frame (%d)\r\n"), m_dwDevIndex));
        }
        if (ulError)
        {
                SetReceiveError(ulError);
        }
        if (ulData & (ESR_BREAK_DETECT))
        {
                EventCallback(EV_BREAK);
                ERRMSG((TEXT("[UART] E:Break (%d)\r\n"), m_dwDevIndex));
        }
        return (UINT8)ulData;

}


void    CPddUart::SetBreak(BOOL bSet)
{
        m_HardwareLock.Lock();
        ULONG ulData = m_pRegUart->Read_UCON();
        if (bSet)
        {
                ulData |= (BREAK_SIGNAL_ENABLE);
        }
        else
        {
                ulData &= ~(BREAK_SIGNAL_ENABLE);
        }
        m_pRegUart->Write_UCON(ulData);
        m_HardwareLock.Unlock();
}


BOOL    CPddUart::SetBaudRate(ULONG BaudRate,BOOL /*bIrModule*/)
{
        DWORD dwBit;
        m_HardwareLock.Lock();

        if (m_ClockSelectValid) {

                dwBit = m_pRegUart->Read_UCON();

                dwBit &= ~(CLKSEL_MASK);

                if (BaudRate < (10 * 115200)) 
                {
                        m_ClockSelect = CLKSEL_PCLK;
						DBGMSG(UART_USR4, (TEXT("[UART] CLKSEL_PCLK selected ***************\r\n"))); 
                } 
                else 
                {
                        m_ClockSelect = CLKSEL_SCLK;
						DBGMSG(UART_USR4, (TEXT("[UART] CLKSEL_SCLK selected ***************\r\n"))); 
                }

                dwBit |= (m_ClockSelect);

                m_pRegUart->Write_UCON(dwBit);
        }

        BOOL bReturn = m_pRegUart->Write_BaudRate(BaudRate);
        m_HardwareLock.Unlock();
        return TRUE;
}


BOOL    CPddUart::SetByteSize(ULONG ByteSize)
{
        BOOL bRet = TRUE;
        m_HardwareLock.Lock();
        ULONG ulData = (m_pRegUart->Read_ULCON() & (~(LCR_DATA_LENGTH_MASK)));
        switch ( ByteSize )
        {
            case 5:
                ulData |= (LCR_DATA_LENGTH_5BIT);
                break;
            case 6:
                ulData |= (LCR_DATA_LENGTH_6BIT);
                break;
            case 7:
                ulData |= (LCR_DATA_LENGTH_7BIT);
                break;
            case 8:
                ulData |= (LCR_DATA_LENGTH_8BIT);
                break;
            default:
                bRet = FALSE;
                break;
        }
        if (bRet)
        {
                m_pRegUart->Write_ULCON(ulData);
        }
        m_HardwareLock.Unlock();
        return bRet;
}


BOOL    CPddUart::SetParity(ULONG Parity)
{
        BOOL bRet = TRUE;
        m_HardwareLock.Lock();
        ULONG ulData = (m_pRegUart->Read_ULCON() & (~(LCR_PARITY_MASK)));
        switch ( Parity )
        {
            case ODDPARITY:
                ulData |= (LCR_PARITY_ODD);
                break;
            case EVENPARITY:
                ulData |= (LCR_PARITY_EVEN);
                break;
            case MARKPARITY:
                ulData |= (LCR_PARITY_FORCE_1);
                break;
            case SPACEPARITY:
                ulData |= (LCR_PARITY_FORCE_0);
                break;
            case NOPARITY:
                break;
            default:
                bRet = FALSE;
                break;
        }
        if (bRet)
        {
                m_pRegUart->Write_ULCON(ulData);
        }
        m_HardwareLock.Unlock();
        return bRet;
}


BOOL    CPddUart::SetStopBits(ULONG StopBits)
{
        BOOL bRet = TRUE;
        m_HardwareLock.Lock();
        ULONG ulData = (m_pRegUart->Read_ULCON() & (~(LCR_STOPBIT_MASK)));

        switch ( StopBits )
        {
            case ONESTOPBIT :
                ulData |= (LCR_1_STOPBIT);
                break;
            case TWOSTOPBITS :
                ulData |= (LCR_2_STOPBITS);
                break;
            default:
                bRet = FALSE;
                break;
        }
        if (bRet)
        {
                m_pRegUart->Write_ULCON(ulData);
        }
        m_HardwareLock.Unlock();
        return bRet;
}
void    CPddUart::SetOutputMode(BOOL UseIR, BOOL Use9Pin)
{
        m_HardwareLock.Lock();
        CSerialPDD::SetOutputMode(UseIR, Use9Pin);
        ULONG ulData = (m_pRegUart->Read_ULCON() & (~(LCR_MODE_MASK)));
        ulData |= (UseIR?(LCR_IR_MODE):(LCR_NORMAL_MODE));
        m_pRegUart->Write_ULCON(ulData);
        m_HardwareLock.Unlock();
}



#ifdef USE_DMA

BOOL CPddUart::InitRxDMA(void)
{
        UINT RXH = 0;
        RXH = BASE_REG_PA_UART0 + (m_dwDevIndex*BASE_REG_OFFSET_UART)+0x24;

        DMA_initialize_channel( pRxDMA, TRUE);
#if DMA_BURST_4B        
       // if(DMA_set_channel_source( pRxDMA, RXH, WORD_UNIT, BURST_1, FIXED))
        if(DMA_set_channel_source( pRxDMA, RXH, BYTE_UNIT, BURST_4, FIXED))
#else
        if(DMA_set_channel_source( pRxDMA, RXH, BYTE_UNIT, BURST_1, FIXED))
#endif             
        {
                return FALSE;
        }

        return TRUE; 
}


BOOL CPddUart::StartRxDMA(DWORD dwLen)
{
        DMA_ERROR dma_error_value = DMA_SUCCESS;
        UINT RXH = BASE_REG_PA_UART0 + (m_dwDevIndex*BASE_REG_OFFSET_UART)+0x24;
        
        if(dwLen<DMA_TRANS_SIZE){
            DBGMSG(DMA_MSG,(TEXT("[UART] Return StartRxDMA\r\n")));	
            return TRUE;
        }

        if(!m_IsRXDMA)
	{
#if DMA_BURST_4B 
	        //DMA_set_channel_destination(pRxDMA, (UINT)DmaRxAddress,  WORD_UNIT, BURST_1, INCREASE);
	        DMA_set_channel_destination(pRxDMA, (UINT)DmaRxAddress,  BYTE_UNIT, BURST_4, INCREASE);
#else
	        DMA_set_channel_destination(pRxDMA, (UINT)DmaRxAddress,  BYTE_UNIT, BURST_1, INCREASE);
#endif 
	        DMA_set_channel_transfer_size( pRxDMA, dwLen);                     
	        DMA_initialize_LLI( pRxDMA, 1);                     
	        DMA_set_LLI_entry( pRxDMA, 0, LLI_FIRST_ENTRY, RXH, (UINT)DmaRxAddress, dwLen);                   
	        DMA_channel_start( pRxDMA);
              DBGMSG(DMA_MSG,(TEXT("[UART] +S (L:%d)\r\n"), dwLen));	
		 m_IsRXDMA=TRUE;
         }
        return TRUE;
}

BOOL CPddUart::HandleRXDMA(PUCHAR pRxBuffer, ULONG BuffLen)
{
        DWORD  WaitReturn=0xFFFFFFFF;
        UINT RXH = BASE_REG_PA_UART0 + (m_dwDevIndex*BASE_REG_OFFSET_UART)+0x24;
        unsigned int CurrDMADestAddr=DmaRxAddress;
        
        //DBGMSG(DMA_MSG,(TEXT("[UART] +RD (%d) \r\n"),BuffLen));	

        WaitReturn = WaitForMultipleObjects(2, pUartDma->RXDMAEventHandles, FALSE, READ_TIME_OUT_CONSTANT);
        DMA_channel_stop( pRxDMA);
        m_IsRXDMA=FALSE;
        DBGMSG(DMA_MSG,(TEXT("[UART] -S \r\n")));	

	if(WaitReturn == (WAIT_OBJECT_0+1))    // DMA transfer succeed
	{
             DBGMSG(DMA_MSG,(TEXT("[UART] Dma Done \n\r")));
            DMA_clear_interrupt_pending(pRxDMA);
            DMA_set_interrupt_mask(pRxDMA);
            InterruptDone(pUartDma->dwRxDmaDoneSysIntr);
            DMA_clear_interrupt_mask(pRxDMA);             
	}         

	else if(WaitReturn == WAIT_OBJECT_0) //Time-out interrupt
	{
             DBGMSG(DMA_MSG,(TEXT("[UART] Timeout  \n\r")));
		m_IsRXTimeout=TRUE;             
	}

	else    // DMA transfer fail
	{
		ULONG ulUFSTATE = m_pRegUart->Read_UFSTAT();
		DWORD dwNumRxInFifo = ((ulUFSTATE & FSR_RX_FIFO_COUNT_MASK) >> FSR_RX_FIFO_COUNT_SHIFT);     
        
             ERRMSG((TEXT("[UART] ***** Fail (%d)*****  \n\r"), dwNumRxInFifo));
             ERRMSG((TEXT("ULCON:0x%x\n\r"), m_pRegUart->Read_ULCON()));
             ERRMSG((TEXT("UCON:0x%x		  UFCOn:0x%x		  UFSTAT:0x%x\n\r"),m_pRegUart->Read_UCON() ,m_pRegUart->Read_UFCON() ,m_pRegUart->Read_UFSTAT()));
             ERRMSG((TEXT("UMCON:0x%x		  UTRSTAT:0x%x		  UERSTAT:0x%x\n\r"),m_pRegUart->Read_UMCON() ,m_pRegUart->Read_UTRSTAT() ,m_pRegUart->Read_UERSTAT()));
		ERRMSG((TEXT("UINTP:0x%x		  UINTM:0x%x\r\n"),m_pRegUart->Read_UINTP(), m_pRegUart->Read_UINTM()) );
		return FALSE;
	}
    
	DMA_get_current_channel_destaddress(pRxDMA, DmaRxAddress, &CurrDMADestAddr, &dwRxLen);
	dwRxLen = CurrDMADestAddr-DmaRxAddress;

	if(dwRxLen>0)
	{
		DBGMSG(DMA_MSG,(TEXT("[UART] MC (%d) \r\n"), dwRxLen));	
		memcpy(pRxBuffer, pVirtDmaRxBufferAddr, dwRxLen);             
	}

	//DBGMSG(DMA_MSG,(TEXT("[UART] -RD \r\n")));
	return TRUE;
}
#if 0//NORMAL_DMA_MODE
BOOL CPddUart::StartRxDMA(PUCHAR pRxBuffer, ULONG BuffLen)
{
        DMA_ERROR dma_error_value = DMA_SUCCESS;
        DWORD  WaitReturn=0xFFFFFFFF;
        DWORD interrupts = 0;
        UINT RXH = BASE_REG_PA_UART0 + (m_dwDevIndex*BASE_REG_OFFSET_UART)+0x24;
        dwRxLen=0;
        
        DBGMSG(DMA_MSG,(TEXT("[UART] +RD (%d) \r\n"),BuffLen));	
        //EnableRxDMA(TRUE);

        DMA_set_channel_destination(pRxDMA, (UINT)DmaRxAddress,  BYTE_UNIT, BURST_1, INCREASE);               
        DMA_set_channel_transfer_size( pRxDMA, dwRxLen);                     
        DMA_initialize_LLI( pRxDMA, 1);                     
        DMA_set_LLI_entry( pRxDMA, 0, LLI_FIRST_ENTRY, RXH, (UINT)DmaRxAddress, dwRxLen);                   
        DMA_channel_start( pRxDMA);

        //WaitReturn = WaitForSingleObject(pUartDma->hRxDmaDoneDoneEvent, READ_TIME_OUT_CONSTANT);
        WaitReturn = WaitForMultipleObjects(2, pUartDma->RXDMAEventHandles, FALSE, READ_TIME_OUT_CONSTANT);
        DMA_channel_stop( pRxDMA);

	if(WaitReturn ==( WAIT_OBJECT_0+1))    // DMA transfer succeed
	{
             ERRMSG((TEXT("[UART] Dma Done (%d) \n\r"), BuffLen));
             dwRxLen=BuffLen;
		memcpy(pRxBuffer, pVirtDmaRxBufferAddr, dwRxLen);  
             //m_IsRXTimeout=TRUE;
	} 
    	else if(WaitReturn == WAIT_OBJECT_0)
	{
             ERRMSG((TEXT("[UART] TIMEOUT \n\r"), dwRxLen));
             m_IsRXTimeout=TRUE;
	} 
	else    // DMA transfer fail
	{
             ERRMSG((TEXT("[UART] ***** Fail *****  \n\r")));
             ERRMSG((TEXT("ULCON:0x%x\n\r"), m_pRegUart->Read_ULCON()));
             ERRMSG((TEXT("UCON:0x%x		  UFCOn:0x%x		  UFSTAT:0x%x\n\r"),m_pRegUart->Read_UCON() ,m_pRegUart->Read_UFCON() ,m_pRegUart->Read_UFSTAT()));
             ERRMSG((TEXT("UMCON:0x%x		  UTRSTAT:0x%x		  UERSTAT:0x%x\n\r"),m_pRegUart->Read_UMCON() ,m_pRegUart->Read_UTRSTAT() ,m_pRegUart->Read_UERSTAT()));
		ERRMSG((TEXT("UINTP:0x%x		  UINTM:0x%x\r\n"),m_pRegUart->Read_UINTP(), m_pRegUart->Read_UINTM()) );
        
		ULONG ulUFSTATE = m_pRegUart->Read_UFSTAT();
		DWORD dwNumRxInFifo = ((ulUFSTATE & FSR_RX_FIFO_COUNT_MASK) >> FSR_RX_FIFO_COUNT_SHIFT);        
		int i=0;
		ERRMSG((TEXT("RXD(%d) : \n\r"),dwNumRxInFifo));                    

		for(i=0; i<dwNumRxInFifo; i++)
		{
			ERRMSG((TEXT("0x%x  \n\r"), m_pRegUart->Read_URXH()));   
		}

		//EnableRxDMA(FALSE);
		return FALSE;
	}
    
	pRxBuffer += dwRxLen;
	//EnableRxDMA(FALSE);
	DBGMSG(DMA_MSG,(TEXT("[UART] -RD \r\n")));
	return TRUE;
}
#endif


BOOL CPddUart::InitXmitDMA(void)
{
        UINT TXH = 0;
        TXH = BASE_REG_PA_UART0 + (m_dwDevIndex*BASE_REG_OFFSET_UART)+0x20;

        DMA_initialize_channel( pOutputDMA, TRUE);
        if(DMA_set_channel_destination( pOutputDMA, TXH, BYTE_UNIT, BURST_1, FIXED))
        {
                return FALSE;
        }

        return TRUE; 
}

BOOL CPddUart::EnableTxDMA(BOOL fEnable)
{
        DBGMSG(DMA_MSG, (TEXT("CPddS3CXUart::EnableDMA(%d)\r\n"), fEnable));

        m_HardwareLock.Lock();
        DWORD dwBit = m_pRegUart->Read_UCON();
        if (fEnable)
        {
                dwBit &= ~(TX_MODE_MASK);
                dwBit |= TX_DMA_MODE;  
        }
        else
        {
                // Set Interrupt Tx Mode.
                dwBit &= ~(TX_MODE_MASK);
                dwBit |= TX_INT_POLL;
        }
        m_pRegUart->Write_UCON(dwBit );

        dwBit = m_pRegUart->Read_UFCON();
        if (fEnable)
        {
                dwBit &= ~(FCR_TX_FIFO_TRIG_MASK);//8
        }
        else
        {
                // Set Trigger level to 16.
                dwBit &= ~(FCR_TX_FIFO_TRIG_MASK);//empty
                dwBit |= (FCR_TX_FIFO_TRIG_16);//16
        }
        m_pRegUart->Write_UFCON(dwBit );    

        m_HardwareLock.Unlock();
        return TRUE;
}


BOOL CPddUart::StartXmitDMA(PUCHAR pTxBuffer, ULONG BuffLen)
{
        DWORD dwDmaLen = BuffLen;       
        DMA_ERROR dma_error_value = DMA_SUCCESS;
        ULONG    WaitReturn;
        DWORD interrupts = 0;
        UINT TXH = 0;

        DBGMSG(UART_FUNC,(TEXT("[UART] +StartXmitDMA (%d) \r\n"),dwDmaLen));

        TXH = BASE_REG_PA_UART0 + (m_dwDevIndex*BASE_REG_OFFSET_UART)+0x20;

        EnableTxDMA(TRUE);

        memcpy(pVirtDmaSrcBufferAddr,pTxBuffer,dwDmaLen); 

        DMA_set_channel_source( pOutputDMA, (UINT)DmaSrcAddress, BYTE_UNIT, BURST_1, INCREASE);
        DMA_set_channel_transfer_size( pOutputDMA, dwDmaLen);                     
        DMA_initialize_LLI( pOutputDMA, 1);                     
        DMA_set_LLI_entry( pOutputDMA, 0, LLI_FIRST_ENTRY, (UINT)DmaSrcAddress, TXH, dwDmaLen);                   
        DMA_channel_start( pOutputDMA);

        WaitReturn = WaitForSingleObject(pUartDma->hTxDmaDoneEvent, WRITE_TIME_OUT_CONSTANT);
        if ( WAIT_TIMEOUT == WaitReturn )
        {
                // Timeout
                DBGMSG (UART_DBG, (TEXT("[UART] TX DMA timeout !!!\r\n")));
                DBGMSG (UART_DBG, (TEXT("[UART] LEN:%d, TX:%d\r\n"), dwDmaLen, m_pRegUart->Read_UFSTAT()>>8));                                         
                return FALSE;
        }

        DMA_clear_interrupt_pending(pOutputDMA);
        DMA_set_interrupt_mask(pOutputDMA);
        InterruptDone(pUartDma->dwTxDmaDoneSysIntr);
        DMA_clear_interrupt_mask(pOutputDMA);
        
        DMA_channel_stop( pOutputDMA);

        EnableTxDMA(FALSE);
        DBGMSG(UART_FUNC,(TEXT("[UART] -StartXmitDMA\r\n")));
        return TRUE;
}

#endif //USE_DMA

