// BootModeOpr.cpp : Defines the entry point for the DLL application.
//

#include "stdafx.h"
#include "BMPlatform.h"
//#include <initguid.h>
#include "BootModeOpr.h"
#include <process.h>
#include "OptionHelpper.h"

extern "C"
{
#include "bmpacket.h"
#include "crc16.h"
}

#define  WM_RCV_CHANNEL_DATA              WM_APP + 301 // channel data received
#define  WM_CHANNEL_CLOSE                 WM_RCV_CHANNEL_DATA + 1

#define LOG_FILENAME_PREFIX     _T("BootModeOpr_")
#define MAX_LOG_LEN             256

extern COptionHelpper ohObject;
/////////////////////////////////////////////////////////////////////////////
// CBootModeOpr
CBootModeOpr::CBootModeOpr()
{
    m_lpCurrRead = NULL;
    m_bCheckCrc  = TRUE;
    m_bBigEndian = TRUE;

    ZeroMemory( m_RecvData, MAX_RECV_LEN );
    m_ulRecvDataLen = 0;
    m_iLastSentPktType = BSL_PKT_TYPE_MAX; 
    m_dwLastErrorCode = 0; 
    m_bOperationSuccess = FALSE;
    m_pLogFile  = NULL;    
    m_pChannel = NULL;

    m_dwRecvThreadID = 0;
    m_hRecvThread = NULL;
    m_hRecvThreadState = NULL;
    m_hOprEvent = NULL;

    m_lpReceiveBuffer = NULL;
    m_dwReceiveLen = 0;
    m_dwBufferSize = 0;
    m_dwLastPktSize = 0;
    m_uiReadOffset = 0;
	m_dwCookie = 0;

	m_bHasCheckSum  = FALSE;

	m_dwBaud = DEFAULT_BAUDRATE;

    ::InitializeCriticalSection( &m_csRecvBbuffer );
}

CBootModeOpr::~CBootModeOpr()
{
    FreeRecvBuffer();
    ::DeleteCriticalSection( &m_csRecvBbuffer );
}

BOOL CBootModeOpr::Initialize( LPCTSTR lpszChannelProgID, LPBYTE lpOpenParam,
                              BOOL bBigEndian, DWORD dwCookie  ) 
{
   OpenLogFile( dwCookie );

//   HRESULT hr = E_FAIL;
   CString strLog;
//    hr =  CoInitialize(NULL);
//     if( FAILED( hr ) )
//     {
//         strLog.Format( _T("Call CoInitialize() function fail. [0x%08X]"), hr );
//         Log( strLog );
//         return FALSE;
//     }

    USES_CONVERSION;
    CString strChannel = lpszChannelProgID;
	if(strChannel.CompareNoCase(_T(""))==0)
    {
        strLog.Format( _T("Unknown channel type.[%s]"),lpszChannelProgID);
        Log( strLog );
        return FALSE;
    }

    if( !CreateChannel((ICommChannel **)&m_pChannel,CHANNEL_TYPE_COM) )
    {
		strLog.Format( _T("Create channel object fail.[0x%08X]"), GetLastErrorCode() );
        Log( strLog );
        return FALSE;
    }
   
    m_bBigEndian = bBigEndian;
    ZeroMemory( m_RecvData, MAX_RECV_LEN );
    m_ulRecvDataLen = 0;
    m_iLastSentPktType = BSL_PKT_TYPE_MAX; 
    m_dwLastErrorCode = 0; 
    m_bOperationSuccess = FALSE;

   if( FALSE == CreateRecvThread() )
   {
       Log( _T("Create receive thread fail."));
       return FALSE;
   }

   return ConnectChannel( lpOpenParam );  
}

void CBootModeOpr::Uninitialize()
{
    DisconnectChannel(); 
    
    DestroyRecvThread();

    m_lpCurrRead       = NULL;
    m_bCheckCrc  = TRUE;

    ZeroMemory( m_RecvData, MAX_RECV_LEN );
    m_ulRecvDataLen = 0;
    
    m_iLastSentPktType = BSL_PKT_TYPE_MAX; 

    CloseLogFile();

	ReleaseChannel(m_pChannel);
	m_pChannel = NULL;

 //   CoUninitialize();    
}

BOOL CBootModeOpr::CheckBaud( DWORD dwTimeout /* = 1000  */)
{
    //Send Connect Packet to Phone
    BYTE btSendData[ PACHET_HDR_LEN ];    
    memset( btSendData, 0x7E, PACHET_HDR_LEN);
	
	int nSize = ohObject.Get7ENumOnce();
	if(nSize<0)
	{
		nSize = 1;
	}
	else if(nSize > PACHET_HDR_LEN)
	{
		nSize = PACHET_HDR_LEN;
	}

    BOOL bSuccess = SendData( btSendData, nSize, FALSE, dwTimeout );
	if( m_dwLastErrorCode == BSL_UART_SEND_ERROR)
	{
		Sleep( 500 );
	}
    LogOpr( _T("Check Baudrate"), bSuccess );

    return bSuccess;
}

BOOL CBootModeOpr::Connect( DWORD dwTimeout /* = 1000  */)
{
   BOOL bSuccess = SendCommandData( BSL_CMD_CONNECT, dwTimeout  );
   LogOpr( _T("Connect to Module"), bSuccess );
   
   return bSuccess;
}

BOOL CBootModeOpr::Excute( DWORD dwTimeout /* = 1000  */ )
{
    BOOL bSuccess = SendCommandData( BSL_CMD_EXEC_DATA , dwTimeout );
    LogOpr( _T("Excute  downloaded file"), bSuccess );
    
    return bSuccess;
}

BOOL CBootModeOpr::Reset( DWORD dwTimeout /* = 1000  */ )
{
    BOOL bSuccess = SendCommandData( BSL_CMD_NORMAL_RESET , dwTimeout );
    LogOpr( _T("Reset"), bSuccess );
    
    return bSuccess;
}

BOOL CBootModeOpr::ReadChipType( DWORD dwTimeout /* = 1000  */ )
{
    BOOL bSuccess = SendCommandData( BSL_CMD_READ_CHIP_TYPE , dwTimeout );
    LogOpr( _T("Read Chip Type"), bSuccess);

    return bSuccess;
}

BOOL CBootModeOpr::ReadFlashType( DWORD dwTimeout /* = 1000 */ )
{
    BOOL bSuccess = SendCommandData( BSL_CMD_READ_FLASH_TYPE, dwTimeout );
    LogOpr( _T("Read Flash Type"),bSuccess );
    return bSuccess;
}


BOOL CBootModeOpr::ReadFlashInfo( DWORD dwTimeout /* = 1000 */ )
{
    BOOL bSuccess = SendCommandData( BSL_CMD_READ_FLASH_INFO, dwTimeout );
    LogOpr( _T("Read Flash Info"),bSuccess );
    return bSuccess;
}

BOOL CBootModeOpr::DownFlashSpec( DWORD dwLength, LPBYTE lpDownLoadData, DWORD dwTimeout /*= 1000*/ )
{
    DWORD dwSize = dwLength;
    if( dwSize % 2 )
    {
        dwSize++;
    }    
    
    BYTE* btSendData = new BYTE[PACHET_HDR_LEN + dwSize];
    if( btSendData == NULL )
        return FALSE;    
    
    memset(btSendData,0,PACHET_HDR_LEN + dwSize);	

   //Fill The Packet Type         
    *(short *)&btSendData[PKT_TYPE_POS] = BSL_CMD_DOWN_FLASH_SPEC;
    *(short *)&btSendData[PKT_LEN_POS]  = (short)dwSize;

    
    memcpy(&btSendData[PKT_DATA_POS], lpDownLoadData, dwLength  );
    BOOL bSuccess = FALSE;
    bSuccess = SendData( btSendData, PACHET_HDR_LEN + dwSize, TRUE, dwTimeout );    
    
    delete []btSendData;

    if( m_dwLastErrorCode == BSL_REP_FLASH_SPEC_ERROR )
    {
        Log( _T("DownFlashSpec:Flash Spec error") );
    }
    else
    {
        LogOpr( _T("DownFlashSpec"), bSuccess );
    }

    
    return bSuccess;
}

BOOL CBootModeOpr::EndData( DWORD dwTimeout /* = 1000  */ )
{
    BOOL bSuccess = SendCommandData( BSL_CMD_END_DATA, dwTimeout );
    if( !bSuccess &&  m_dwLastErrorCode == BSL_REP_DOWN_DEST_ERROR )
    {
        Log( _T("End Data:Doownload error in previous command") );
    }
    else
    {
        LogOpr( _T("End Data"), bSuccess );
    }
    
    return bSuccess;
}

BOOL CBootModeOpr::ChangeBaud( DWORD dwTimeout /* = 1000  */ )
{
    DWORD dwBaud = 0;
    BOOL bOK  = m_pChannel->GetProperty( 0, CH_PROP_BAUD, &dwBaud );
    if( !bOK )
    {
        return TRUE;
    }   
	
    if( dwBaud == CHANGE_BAUD_NOT_AVAILABLE || dwBaud == m_dwBaud )
    {
        return TRUE;
    }

    UINT uiDataLen = sizeof( DWORD );
    LPBYTE lpPackData = new BYTE[uiDataLen];
    if( lpPackData == NULL )
        return FALSE; 
    
    *(DWORD *)&lpPackData[ 0 ] = m_dwBaud;    
    
    BOOL bSuccess = SendPacketData( BSL_CMD_CHANGE_BAUD, 
                        uiDataLen  + PACHET_HDR_LEN , 
                        lpPackData, uiDataLen, dwTimeout  );
    delete []lpPackData;

    _TCHAR szOpr[ MAX_LOG_LEN ];
    _stprintf( szOpr, _T("FDL:Change Baudrate( %d )"), m_dwBaud );
    LogOpr( szOpr, bSuccess );
    
    if( bSuccess )
    {
        bSuccess = m_pChannel->SetProperty( 0, CH_PROP_BAUD, (LPCVOID)&m_dwBaud );
        Sleep( 500 );
    }
    
    _stprintf( szOpr, _T("BMPlatform:Change Baudrate( %d )"), dwBaud );
    LogOpr( szOpr, bSuccess );            
    
    return bSuccess;
}

BOOL CBootModeOpr::ReadFlash( DWORD dwBase,DWORD dwLength,
                             DWORD dwTimeout /* = 1000  */  )
{
    UINT uiDataLen =  START_BSL_PKT_LEN - PACHET_HDR_LEN;
    LPBYTE lpPackData = new BYTE[uiDataLen];
    if( lpPackData == NULL )
        return FALSE; 
    
    m_uiReadOffset = dwBase % 4;
    dwLength += m_uiReadOffset;    

    *(DWORD *)&lpPackData[ 0 ] = dwBase;
    *(DWORD *)&lpPackData[ sizeof(DWORD) ] = dwLength;
    m_dwLastPktSize = dwLength;

    m_lpCurrRead = NULL;

    BOOL bSuccess = SendPacketData( BSL_CMD_READ_FLASH, START_BSL_PKT_LEN, 
        lpPackData, uiDataLen , dwTimeout  );
    delete []lpPackData;

    _TCHAR szOpr[ MAX_LOG_LEN ];
    _stprintf( szOpr,_T("%s( Base:0x%08X, Size:0x%08X )"), _T("Read Flash"), dwBase, dwLength );
    LogOpr( szOpr, bSuccess );
    
    return bSuccess;
}

// dwBase gives partition id,dwOffset gives offset in this partition
BOOL CBootModeOpr::ReadPartitionFlash( DWORD dwBase,DWORD dwLength,DWORD dwOffset,DWORD dwTimeout /* = 1000 */)
{
    UINT uiDataLen =  START_BSL_PKT_LEN - PACHET_HDR_LEN + sizeof( DWORD );
    LPBYTE lpPackData = new BYTE[uiDataLen];
    if( lpPackData == NULL )
        return FALSE; 
    
    m_uiReadOffset = 0;
    
    *(DWORD *)&lpPackData[ 0 ] = dwBase;
    *(DWORD *)&lpPackData[ sizeof(DWORD) ] = dwLength;
    *(DWORD *)&lpPackData[ sizeof( DWORD ) * 2 ] = dwOffset;
    m_dwLastPktSize = dwLength;
    
    m_lpCurrRead = NULL;
    
    BOOL bSuccess = SendPacketData( BSL_CMD_READ_FLASH, START_BSL_PKT_LEN + sizeof( DWORD ), 
        lpPackData, uiDataLen , dwTimeout  );
    delete []lpPackData;
    
    _TCHAR szOpr[ MAX_LOG_LEN ];
    _stprintf( szOpr,_T("%s( Base:0x%08X, Size:0x%08X, Offset:0x%08X )"), _T("Read Flash"), dwBase, dwLength,dwOffset );
    LogOpr( szOpr, bSuccess );
    
    return bSuccess;
}

BOOL CBootModeOpr::ReadNVItem( DWORD dwStartAddr, DWORD dwEndAddr, 
                              unsigned short uiItemID, DWORD dwTimeout /* = 1000  */ )
{
    LPBYTE lpSendData = new BYTE[ READ_NVITEM_PKT_LEN];
    if( lpSendData == NULL )
    {
        Log( _T("new operation fail [out of memory].") );        
        return FALSE;
    }
    ZeroMemory(lpSendData, READ_NVITEM_PKT_LEN);
    
    //Fill The Packet Type         
    *(short *)&lpSendData[PKT_TYPE_POS]   = BSL_CMD_READ_NVITEM;
    *(short *)&lpSendData[PKT_LEN_POS]    = READ_NVITEM_PKT_LEN - PACHET_HDR_LEN ;  

    if( m_bBigEndian )
    {
        DWORD dwSoruceValue, dwDestValue;
        dwSoruceValue =  dwStartAddr;    
        dwDestValue   = 0;
        CONVERT_INT( dwSoruceValue, dwDestValue);
        *(DWORD *)&lpSendData[PKT_DATA_POS ] = dwDestValue;

        dwSoruceValue = dwEndAddr;    
        dwDestValue   = 0;
        CONVERT_INT( dwSoruceValue, dwDestValue);
        *(DWORD *)&lpSendData[PKT_DATA_POS +  sizeof( DWORD) ] = dwDestValue;

        unsigned short uiSourceValue, uiDestValue;        
        uiSourceValue =  uiItemID;    
        uiDestValue   = 0;
        CONVERT_SHORT( uiSourceValue, uiDestValue);
        *(unsigned short *)&lpSendData[PKT_DATA_POS + 2 * sizeof( DWORD) ] = uiDestValue;
    }
    else
    {
        *(DWORD *)&lpSendData[ PKT_DATA_POS ] = dwStartAddr;
        *(DWORD *)&lpSendData[ PKT_DATA_POS + sizeof( DWORD)  ] = dwEndAddr;
        *(unsigned short *)&lpSendData[ PKT_DATA_POS + 2 * sizeof( DWORD)  ] = uiItemID;        
    }    
    
    BOOL bSuccess = SendData( lpSendData, READ_NVITEM_PKT_LEN , TRUE, dwTimeout );
    delete []lpSendData;

    _TCHAR szOpr[ MAX_LOG_LEN ];
    _stprintf( szOpr,_T("%s( Start:0x%08X, End:0x%08X, Item ID:%d )"), _T("Read NV Item"), 
        dwStartAddr, dwEndAddr, uiItemID );
    LogOpr( szOpr, bSuccess );
    
    return bSuccess;
}

BOOL CBootModeOpr::EraseFlash( DWORD dwBase,DWORD dwLength , DWORD dwTimeout /* = 1000  */ )
{
    DWORD dwSize = dwLength;
    if( dwSize % 2 )
    {
        dwSize++;
    }
    
    UINT uiDataLen =  2 * sizeof(DWORD) ;
    LPBYTE lpPackData = new BYTE[uiDataLen];
    if( lpPackData == NULL )
        return FALSE;    
    
    *(DWORD *)&lpPackData[ 0 ] = dwBase;
    *(DWORD *)&lpPackData[ sizeof(DWORD) ] = dwSize;
        
    BOOL bSuccess = SendPacketData( BSL_CMD_ERASE_FLASH,
        ERASE_FLASH_PKT_LEN, lpPackData, 
        uiDataLen , dwTimeout );
    delete []lpPackData;
    
    _TCHAR szOpr[ MAX_LOG_LEN ];
    _stprintf( szOpr,_T("%s( Base:0x%08X, Size:0x%08X)"), _T("Erase Flash"), dwBase, dwLength );
    LogOpr( szOpr, bSuccess );
    
    return bSuccess;    
}

BOOL CBootModeOpr::StartData( DWORD dwBase,DWORD dwLength , 
                                 LPBYTE lpDownLoadData,
                                  DWORD dwTimeout /* = 1000  */ )
{
    DWORD dwSize = dwLength;
    if( dwSize % 2 )
    {
        dwSize++;
    }

	_TCHAR szOpr[ MAX_LOG_LEN ] = {0};
	
	int nSendDataLen = START_BSL_PKT_LEN;
    
    UINT uiDataLen =  2 * sizeof(DWORD) ;
	DWORD dwCheckSum = 0;
	if(m_bHasCheckSum)
	{
		uiDataLen += sizeof(DWORD);
		nSendDataLen += sizeof(DWORD);
		dwCheckSum = DoCheckSum(lpDownLoadData,dwLength);
		if( 0 == dwCheckSum )
		{
			_stprintf( szOpr,_T("%s( Base:0x%08X, Size:0x%08X, CheckSum:0x%08X)"), _T("Start Data"),
					   dwBase, dwLength, dwCheckSum);
			LogOpr( szOpr, FALSE );
			return FALSE;
		}
	}	
	
    LPBYTE lpPackData = new BYTE[uiDataLen];
    if( lpPackData == NULL )
        return FALSE;    
    
    *(DWORD *)&lpPackData[ 0 ] = dwBase;
    *(DWORD *)&lpPackData[ sizeof(DWORD) ] = dwSize;

	if(m_bHasCheckSum)
	{
		*(DWORD *)&lpPackData[ sizeof(DWORD)*2 ] = dwCheckSum;
	}

    m_lpCurrRead = lpDownLoadData;

    
    BOOL bSuccess = SendPacketData( BSL_CMD_START_DATA,
                        nSendDataLen, lpPackData, 
                        uiDataLen , dwTimeout );
    delete []lpPackData;
    
    
	if(!m_bHasCheckSum)
	{
		_stprintf( szOpr,_T("%s( Base:0x%08X, Size:0x%08X)"), _T("Start Data"), dwBase, dwLength );
	}
	else
	{
		_stprintf( szOpr,_T("%s( Base:0x%08X, Size:0x%08X, CheckSum:0x%08X)"), _T("Start Data"),
			       dwBase, dwLength, dwCheckSum);
	}
    LogOpr( szOpr, bSuccess );
    
    return bSuccess;
}

BOOL CBootModeOpr::MidstData( DWORD dwLength, 
                             DWORD dwTimeout /* = 1000  */ )
{
    BOOL  bOddLength = FALSE;
    if( (0x1) == (dwLength & 0x1)  )
    {
        dwLength = dwLength + 1;
        bOddLength = TRUE;
    }
    
    BYTE*  btSendData =  new BYTE[ PACHET_HDR_LEN + dwLength ];
    if( btSendData == NULL )
        return FALSE;

    BOOL  bState;

    //Clear the memory
    memset(btSendData, 0, PACHET_HDR_LEN + dwLength  );
    
    //Fill The Packet Type         
    *(short *)&btSendData[PKT_TYPE_POS] = BSL_CMD_MIDST_DATA;
    *(short *)&btSendData[PKT_LEN_POS]  = (short)dwLength;
    
    if(TRUE == bOddLength)
    {
        memcpy(&btSendData[PKT_DATA_POS], m_lpCurrRead, dwLength - 1 );
        btSendData[PACHET_HDR_LEN + dwLength - 1] = 0x0;
        m_lpCurrRead += dwLength - 1;    
    }
    else
    {
        memcpy(&btSendData[PKT_DATA_POS], m_lpCurrRead, dwLength  );
        m_lpCurrRead += dwLength;    
    }
    
    bState = SendData( btSendData, dwLength + PACHET_HDR_LEN , TRUE, dwTimeout );    
    
    delete []btSendData;

    if( m_dwLastErrorCode == BSL_REP_DOWN_DEST_ERROR )
    {
        Log( _T("Download:Download error in previous command") );
    }
    else
    {
        LogOpr( _T("Download"), bState );
    }

    
    return bState;
}

BOOL CBootModeOpr::Repartition( DWORD dwTimeout )
{
    BOOL bSuccess = SendCommandData( BSL_CMD_REPARTITION , dwTimeout );
    LogOpr( _T("Repartition"), bSuccess );
    
    return bSuccess;
}


BOOL CBootModeOpr::SendData( LPBYTE lpData, 
                            int iDataSize, 
                            BOOL bCRC /* = TRUE */,
                            DWORD dwTimeout /* = 1000  */ )
{
    LPBYTE lpSendData = NULL;
    int    iSendDataLen = 0;
        
    //Save the last send packet type
    m_iLastSentPktType = (pkt_type_s)( lpData[PKT_TYPE_POS] );   
    if( m_iLastSentPktType == BSL_CMD_MIDST_DATA || m_iLastSentPktType == BSL_CMD_END_DATA )
    {
        if( m_dwLastErrorCode == BSL_REP_DOWN_DEST_ERROR )
        {
            return FALSE;
        }
    }
    
    //Convert the data header
    if(TRUE == m_bBigEndian )
    {
        P_Packet_Header tmpPktHeader = (P_Packet_Header)lpData;
        
        short SoruceValue, DestValue;
        
        SoruceValue = tmpPktHeader->PacketType;
        DestValue   = 0;      
        CONVERT_SHORT(SoruceValue, DestValue);      
        tmpPktHeader->PacketType = DestValue;
        
        SoruceValue = tmpPktHeader->DataSize;
        DestValue   = 0;      
        CONVERT_SHORT(SoruceValue, DestValue);      
        tmpPktHeader->DataSize = DestValue;
    }
    
    if( bCRC )
    {
        if(!encode_bmmsg( lpData, iDataSize, &lpSendData, &iSendDataLen, m_bCheckCrc))
        {
            Log( _T("Encode message fail.") );
            return FALSE;
        }        
    }
    else
    {
        lpSendData    = lpData;
        iSendDataLen = iDataSize;
    }
    
    ResetEvent( m_hOprEvent );
    m_dwLastErrorCode = 0;
    m_bOperationSuccess = FALSE;

    if( iSendDataLen != (int)(m_pChannel->Write( lpSendData, iSendDataLen )) )
    {
        if((bCRC) && (lpSendData))
        {
            delete lpSendData;
        }
		m_dwLastErrorCode = BSL_UART_SEND_ERROR;
        Log( _T("Write channel fail.") );
        return FALSE;
    } 

    if((bCRC) && (lpSendData) && m_iLastSentPktType != BSL_CMD_READ_FLASH )
    {
        delete lpSendData;
    }
    
    WaitForSingleObject( m_hOprEvent, dwTimeout );
    
    ZeroMemory( m_RecvData, MAX_RECV_LEN );
    m_ulRecvDataLen = 0;    
    
    if( m_iLastSentPktType == BSL_CMD_READ_FLASH )
    {
        if( !m_bOperationSuccess )
        {
            ResetEvent( m_hOprEvent );
            m_dwLastErrorCode = 0;
            m_bOperationSuccess = FALSE;
            
            Log( _T("Repeat send read flash command."));

            if( iSendDataLen != (int)(m_pChannel->Write( lpSendData, iSendDataLen )) )
            {
                if((bCRC) && (lpSendData))
                {
                    delete lpSendData;
                }
                Log( _T("Write channel fail.") );
                return FALSE;
            } 
            
            
            if((bCRC) && (lpSendData))
            {
                delete lpSendData;
            }
            
            WaitForSingleObject( m_hOprEvent, dwTimeout );
            
            ZeroMemory( m_RecvData, MAX_RECV_LEN );
            m_ulRecvDataLen = 0; 
        }
        else
        {
            if((bCRC) && (lpSendData) )
            {
                delete lpSendData;
            }
        }
    }
    
    if( m_dwLastErrorCode == 0 )
    {
        m_dwLastErrorCode = BSL_PHONE_WAIT_INPUT_TIMEOUT;        
    }
    
    return m_bOperationSuccess;
}

BOOL CBootModeOpr::ProcessData( LPBYTE lpData, int iDataLen )
{
   if( m_dwLastErrorCode != 0  && m_dwLastErrorCode != BSL_REP_INCOMPLETE_DATA)//have error occured.
        return FALSE;

    int     iRecvDataLen;
    LPBYTE  lpRecvData = NULL;
    int     iDecodeDataLen = 0;
    LPBYTE  lpDecodeData = NULL;
    BOOL    bReturnCode = TRUE;
    
    lpRecvData = lpData;
    iRecvDataLen = iDataLen;
    
    lpDecodeData = NULL;

    if( iRecvDataLen > MAX_RECV_LEN )
    {
        m_pChannel->FreeMem( lpData );
        Log(_T("The received packet too long."));
        m_dwLastErrorCode = BSL_REP_TOO_MUCH_DATA;
        return FALSE;        
    }

    if( m_ulRecvDataLen + iRecvDataLen > MAX_RECV_LEN )
    {
        ZeroMemory( m_RecvData, MAX_RECV_LEN );
        m_ulRecvDataLen = 0;   
        Log(_T("receiving packet occurs error."));
    }
/*for test
    CString str = "";
    str.Format( "%d:", iRecvDataLen );
    CString str2 = "";
    for( int i = 0; i < iRecvDataLen; i++ )
    {
        str2.Format(" %X ", lpRecvData[i] );
        str += str2;
    }
    Log( str );
*/    
    memcpy( m_RecvData + m_ulRecvDataLen, lpRecvData, iRecvDataLen );
    m_ulRecvDataLen += iRecvDataLen;

    int nRet = decode_bmmsg( m_RecvData, m_ulRecvDataLen,
                        &lpDecodeData, &iDecodeDataLen,m_bCheckCrc);
    if( nRet >= PART_PACKET )
    {
        if( nRet > PART_PACKET )
        {
            m_ulRecvDataLen -= nRet - PART_PACKET;
            memmove( m_RecvData, m_RecvData + nRet - PART_PACKET, 
                m_ulRecvDataLen );
        }
        
        m_pChannel->FreeMem( lpData );
        m_dwLastErrorCode = BSL_REP_INCOMPLETE_DATA;
        return FALSE;
    }

    ZeroMemory( m_RecvData, MAX_RECV_LEN );
    m_ulRecvDataLen = 0;
    m_pChannel->FreeMem( lpData );

    if( nRet == ERROR_PACKET )
    {        
        m_dwLastErrorCode = BSL_REP_DECODE_ERROR;
        return FALSE;
    }       

    P_Packet_Header tmpPktHeader = (P_Packet_Header)lpDecodeData;
    
    //Convert the data header
    if(TRUE == m_bBigEndian)
    {        
        short SoruceValue, DestValue;
        
        SoruceValue = tmpPktHeader->PacketType;
        DestValue   = 0;      
        CONVERT_SHORT(SoruceValue, DestValue);      
        tmpPktHeader->PacketType = DestValue;
        
        SoruceValue = tmpPktHeader->DataSize;
        DestValue   = 0;      
        CONVERT_SHORT(SoruceValue, DestValue);      
        tmpPktHeader->DataSize = DestValue;
    }
    
    m_dwLastErrorCode = tmpPktHeader->PacketType;
    bReturnCode = FALSE;
    
    //According the Response message type implement operation
    if(tmpPktHeader->PacketType == BSL_REP_ACK)
    {
        bReturnCode = TRUE;
    }
    else if(tmpPktHeader->PacketType == BSL_REP_VER)
    {
        bReturnCode = TRUE;
    }    
    else if(tmpPktHeader->PacketType == BSL_REP_READ_FLASH)
    {
        bReturnCode = TRUE;
        if( NULL != lpDecodeData  &&  m_iLastSentPktType == BSL_CMD_READ_FLASH )
        {
            // The buffer is read from module
            if( iDecodeDataLen <= PACHET_HDR_LEN  ||
                m_dwLastPktSize != iDecodeDataLen - PACHET_HDR_LEN)
            {
                // Data read error
                delete lpDecodeData;
                lpDecodeData = NULL;
                m_dwLastErrorCode = BSL_REP_OPERATION_FAILED;
                return FALSE;
            }
            ::EnterCriticalSection( &m_csRecvBbuffer );
            if( m_lpReceiveBuffer != NULL )
            {
                if( m_dwReceiveLen + iDecodeDataLen - PACHET_HDR_LEN - m_uiReadOffset <= m_dwBufferSize  )
                {
                    memcpy( m_lpReceiveBuffer + m_dwReceiveLen,
                        lpDecodeData + PACHET_HDR_LEN + m_uiReadOffset,
                        iDecodeDataLen - PACHET_HDR_LEN - m_uiReadOffset );
                    m_dwReceiveLen += ( iDecodeDataLen - PACHET_HDR_LEN - m_uiReadOffset );
                    m_uiReadOffset = 0;
                }
            }
            ::LeaveCriticalSection( &m_csRecvBbuffer );
        }

    } else if( tmpPktHeader->PacketType == BSL_REP_READ_CHIP_TYPE || 
        tmpPktHeader->PacketType == BSL_REP_READ_NVITEM ||
        tmpPktHeader->PacketType == BSL_REP_READ_FLASH_TYPE ||
		tmpPktHeader->PacketType == BSL_REP_READ_FLASH_INFO||
		tmpPktHeader->PacketType == BSL_REP_READ_SECTOR_SIZE)
    {        
        bReturnCode = TRUE; 
        if(  NULL != lpDecodeData  )
        {
            ::EnterCriticalSection( &m_csRecvBbuffer );
            if( m_lpReceiveBuffer != NULL )
            {
                m_dwReceiveLen = 0; 
                if( iDecodeDataLen - PACHET_HDR_LEN  == m_dwBufferSize )
                {
                    memcpy( m_lpReceiveBuffer ,lpDecodeData + PACHET_HDR_LEN, m_dwBufferSize );
                    m_dwReceiveLen = m_dwBufferSize;
                }
            }
            ::LeaveCriticalSection( &m_csRecvBbuffer );
        }
    }

    if( NULL != lpDecodeData )
    {      
        delete lpDecodeData;
        lpDecodeData = NULL;
    }    
    return bReturnCode;    
}

BOOL CBootModeOpr::SendCommandData( pkt_type_s nCmdType, DWORD dwTimeout /* = 1000  */ )
{
    BYTE btSendData[PACHET_HDR_LEN];
    
    memset(&btSendData, 0, PACHET_HDR_LEN);    
    *(short *)&btSendData[PKT_TYPE_POS] = (short)nCmdType;
    
    return SendData( btSendData, PACHET_HDR_LEN , TRUE, dwTimeout );    
}

BOOL CBootModeOpr::SendPacketData( pkt_type_s nCmdType, 
                                  const int nSendDataLen,
                                  LPBYTE lpPacketData,
                                  UINT uiDataLen,
                                  DWORD dwTimeout /* = 1000  */ )
{
    LPBYTE lpSendData = new BYTE[ nSendDataLen];
    if( lpSendData == NULL )
    {
        Log( _T("new operation fail [out of memory].") );
        return FALSE;
    }
    
    //Clear Memory
    ZeroMemory(lpSendData, nSendDataLen);
    
    //Fill The Packet Type         
    *(short *)&lpSendData[PKT_TYPE_POS]   = (short)nCmdType;
    *(short *)&lpSendData[PKT_LEN_POS]    = (short)uiDataLen ;  
    
    for( UINT i = 0; i < uiDataLen / sizeof( DWORD ); i++ )
    {
        if( m_bBigEndian )
        {
            DWORD dwSoruceValue, dwDestValue;

            dwSoruceValue =  *(DWORD *)&lpPacketData[ i * sizeof( DWORD) ];    
            dwDestValue   = 0;
            CONVERT_INT( dwSoruceValue, dwDestValue);
            *(DWORD *)&lpSendData[PKT_DATA_POS + i * sizeof( DWORD) ] = dwDestValue;
        }
        else
        {    
            *(DWORD *)&lpSendData[ PKT_DATA_POS + i * sizeof( DWORD)  ] 
                            = *(DWORD *)&lpPacketData[ i * sizeof( DWORD) ];
        }
    }
    
    BOOL bRet = SendData( lpSendData, nSendDataLen , TRUE, dwTimeout );
    delete []lpSendData;

    return bRet;
}

BOOL CBootModeOpr::AllocRecvBuffer( DWORD dwSize )
{
    ::EnterCriticalSection( &m_csRecvBbuffer );
    if( m_lpReceiveBuffer != NULL )
    {
        if( m_dwBufferSize == dwSize )
        {
            ZeroMemory( m_lpReceiveBuffer, m_dwBufferSize );
            m_dwReceiveLen = 0;
        }
        else
        {
            delete[] m_lpReceiveBuffer;
            m_lpReceiveBuffer = NULL;
            m_dwReceiveLen = 0;
            m_dwBufferSize = 0;            
        }
    }
    if( m_lpReceiveBuffer == NULL )
    {
        m_lpReceiveBuffer = new BYTE [ dwSize ];
        if( m_lpReceiveBuffer == NULL )
            return FALSE;
        m_dwReceiveLen = 0;
        m_dwBufferSize = dwSize;
    }
    
    ::LeaveCriticalSection( &m_csRecvBbuffer );
    
    return TRUE;
}

LPBYTE CBootModeOpr::GetRecvBuffer()
{
    return m_lpReceiveBuffer;
}

DWORD CBootModeOpr::GetRecvBufferSize()
{
    return m_dwReceiveLen;
}

void CBootModeOpr::FreeRecvBuffer()
{
    ::EnterCriticalSection( &m_csRecvBbuffer );
    if( m_lpReceiveBuffer != NULL )
    {
        delete[] m_lpReceiveBuffer;
        m_lpReceiveBuffer = NULL;
        m_dwReceiveLen = 0;    
        m_dwBufferSize = 0;
    }
    ::LeaveCriticalSection( &m_csRecvBbuffer );

}

BOOL CBootModeOpr::ConnectChannel( LPBYTE lpOpenParam )
{
    m_pChannel->SetReceiver( WM_RCV_CHANNEL_DATA, TRUE, (LPVOID)m_dwRecvThreadID );

	DWORD dwPort = *((DWORD*)lpOpenParam);
	DWORD dwBaud = *((DWORD*)(lpOpenParam+sizeof(DWORD)));
	m_dwBaud = dwBaud;

	CHANNEL_ATTRIBUTE ca;
	ca.ChannelType = CHANNEL_TYPE_COM;
	ca.Com.dwPortNum = dwPort;
	ca.Com.dwBaudRate = DEFAULT_BAUDRATE;
         

    BOOL bOK  = m_pChannel->Open( &ca );
    
    if( !bOK )
    {
        Log( _T("Open Channel fail.") );
        return FALSE;
    }    
    return TRUE;    
}

BOOL CBootModeOpr::DisconnectChannel()
{
    if( m_pChannel == NULL )
        return FALSE;

    m_pChannel->Close();
//    m_pChannel = NULL;
    return TRUE;
}

BOOL CBootModeOpr::CreateRecvThread()
{
    m_hRecvThreadState = CreateEvent(NULL, TRUE, FALSE, NULL);
    m_hOprEvent = CreateEvent( NULL, TRUE, FALSE, NULL );
    Log( _T("Create receive thread"));
    m_hRecvThread = (HANDLE)_beginthreadex(NULL,
								0,
                                (PBEGINTHREADEX_THREADFUNC)GetRcvDataThread,
                                this,
                                NULL,
                                (PBEGINTHREADEX_THREADID)&m_dwRecvThreadID);
    if(m_hRecvThread == NULL)
    {
        return FALSE;
    }
    
    //Wait For UARTCM Thread be Running
    DWORD bWaitCode = WaitForSingleObject(m_hRecvThreadState,5000);
    if( bWaitCode != WAIT_OBJECT_0)
    {			
        return FALSE;
    }
    else
    {
        //Reset the UARTCM thread state event, the next signal
        //is thread ending
        ResetEvent(m_hRecvThreadState);
    }    
    return TRUE;
}

void CBootModeOpr::DestroyRecvThread() 
{
    if( m_hRecvThread == NULL )
        return;
    
    Log( _T("Destroy receive thread"));
    
    ::PostThreadMessage (m_dwRecvThreadID, WM_QUIT, 0, 0);

    WaitForSingleObject( m_hRecvThreadState, 5000 );

    CloseHandle(m_hRecvThread);
    m_hRecvThread = NULL;
    
    CloseHandle(m_hRecvThreadState);
    m_hRecvThreadState = NULL;

    CloseHandle( m_hOprEvent );
    m_hOprEvent = NULL;

    m_dwRecvThreadID = NULL;    
}

DWORD  WINAPI CBootModeOpr::GetRcvDataThread( LPVOID lpParam )
{
    CBootModeOpr * pThis = ( CBootModeOpr * )lpParam;
    return pThis->RcvDataThreadProc();
}

DWORD CBootModeOpr::RcvDataThreadProc()
{
    MSG msg;
    
    SetEvent(m_hRecvThreadState);
    
    // Set up the message queue
    PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
    
    // Wait for the UART data
    while ( GetMessage(&msg, NULL, 0, 0) )
    {
        switch (msg.message )
        {
        case WM_RCV_CHANNEL_DATA:
            m_bOperationSuccess = ProcessData( ( LPBYTE )msg.wParam , (int)msg.lParam );

            if( m_bOperationSuccess || m_dwLastErrorCode != BSL_REP_INCOMPLETE_DATA )
            {
                SetEvent( m_hOprEvent );
            }
            break;
        case WM_CHANNEL_CLOSE:
            m_bOperationSuccess = FALSE;
            break;
        default:
            break;
       }
    }
    
    SetEvent(m_hRecvThreadState);
    
    return 0;
}

DWORD CBootModeOpr::GetLastErrorCode()
{
    return m_dwLastErrorCode;
}


void CBootModeOpr::GetLastErrorDescription(
                                           DWORD dwErrorCode,
                                           LPTSTR lpszErrorDescription, 
                                           int nSize )
{
    if( lpszErrorDescription == NULL || nSize == 0 )
        return;
    
    _TCHAR szErrorIniFile[MAX_PATH];
    
    GetModuleFilePath( g_theApp.m_hInstance, szErrorIniFile );
    _tcscat( szErrorIniFile,  _T("\\BMError.ini") );
    
    _TCHAR szKey[ 10 ];
    _itot( dwErrorCode, szKey, 10 );
    
    GetPrivateProfileString(  _T("ErrorDescription"), szKey,
        _T("Unknown Error"), lpszErrorDescription, 
        nSize, szErrorIniFile );  
    
    return;
}

BOOL CBootModeOpr::OpenLogFile( DWORD dwCookie )
{
    CloseLogFile();

	int nLogFlag = ohObject.GetLogFlag();
	if(0 == nLogFlag)
	{
		m_pLogFile = NULL;
		return TRUE;
	}
	
    m_dwCookie = dwCookie;
    _TCHAR szLogFileName[ MAX_PATH ];
    GetModuleFilePath( NULL, szLogFileName );

    SYSTEMTIME  currentTime ;
    GetLocalTime( &currentTime);
    _stprintf( szLogFileName, _T("%s\\%sCOM_%d_%d_%d_%d.log"), 
                        szLogFileName, LOG_FILENAME_PREFIX, 
                        dwCookie,
                        currentTime.wYear,
                        currentTime.wMonth,
                        currentTime.wDay );

    m_pLogFile = _tfopen( szLogFileName, _T("a+") );  
    if( m_pLogFile != NULL )
    {
        GetModuleFileName( g_theApp.m_hInstance, 
                        szLogFileName, MAX_PATH );
        _ftprintf( m_pLogFile, _T("===============================%s\n"),             
            szLogFileName );   
        return TRUE;
    }
    return FALSE;
}

void CBootModeOpr::CloseLogFile()
{
    if( m_pLogFile != NULL )
    {
        fclose( m_pLogFile );
        m_pLogFile = NULL;
    }    
}

void CBootModeOpr::Log( LPCTSTR lpszLog )
{
    if( m_pLogFile != NULL )
    {
        SYSTEMTIME  currentTime;
        GetLocalTime( &currentTime);
        _ftprintf( m_pLogFile, _T("%02d:%02d:%02d  %s\n"),  
                            currentTime.wHour,
                            currentTime.wMinute,
                            currentTime.wSecond,
                            lpszLog );
        TRACE( _T("COM%d    %02d:%02d:%02d  %s\n"),  
                    m_dwCookie,
                    currentTime.wHour,
                    currentTime.wMinute,
                    currentTime.wSecond,
                    lpszLog);        
    }
}

void CBootModeOpr::LogOpr( LPCTSTR lpszOperation, BOOL bSuccess )
{
    if( m_pLogFile != NULL)
    {
        _TCHAR szLog[ MAX_LOG_LEN ];
        _tcscpy( szLog, lpszOperation );
        if( bSuccess )
        {
            _tcscat( szLog, _T(": Success.") );
            Log( szLog );
        }
        else
        {
            _TCHAR szDescription[ MAX_LOG_LEN ];
            DWORD dwErrorCode = m_dwLastErrorCode;
            GetLastErrorDescription( m_dwLastErrorCode, szDescription, MAX_LOG_LEN );
            _stprintf( szLog, _T("%s: Fail. [%d:%s]"), szLog, dwErrorCode, szDescription );
            Log( szLog );
            fflush( m_pLogFile );
        }
    }    
}

void CBootModeOpr::SetIsCheckCrc( BOOL bCheckCrc  )
{
    m_bCheckCrc = bCheckCrc;
}

void CBootModeOpr::StopOpr()
{
    m_dwLastErrorCode = BSL_USER_CANCEL;
    SetEvent( m_hOprEvent );
}

DWORD CBootModeOpr::GetChannelPtr()
{
    return (DWORD)m_pChannel;
}

void CBootModeOpr::SetChannel( DWORD pChannel )
{
    m_pChannel = (ICommChannel*)pChannel;
}

BOOL CBootModeOpr::SetDebugLogState(DWORD dwState)
{
	return m_pChannel->SetProperty( 0, CH_PROP_DEBUGMODE, (LPCVOID)&dwState );

}

void  CBootModeOpr::SetStartDataInfo(BOOL bHasCheckSum)
{
	m_bHasCheckSum  = bHasCheckSum;
}

DWORD CBootModeOpr::DoCheckSum(LPBYTE pDataBuf,DWORD dwSize)
{
	//////////////////////////////////////////////////////////////////////////
	//make crc and add to the first two byte of data
	//notice: pDataBuf is bigendian, and do CRC not including the first WORD
	WORD wUpdateCrc = crc16(0,pDataBuf+sizeof(WORD),dwSize-sizeof(WORD));
	*pDataBuf = HIBYTE(wUpdateCrc);
	*(pDataBuf+1) = LOBYTE(wUpdateCrc);
					
	
	DWORD dwSum = 0;
	for(UINT i= 0; i< dwSize; i++)
	{
		dwSum += (BYTE)(*(pDataBuf+i));
	}

	return dwSum;
}

BOOL CBootModeOpr::ReadSectorSize( DWORD dwTimeout/* = 1000*/ )
{
    BOOL bSuccess = SendCommandData( BSL_CMD_READ_SECTOR_SIZE, dwTimeout );
    LogOpr( _T("Read sector size"),bSuccess );
    return bSuccess;
}
