/*
============================================================================   
   /      
  / /     
 / / /\   
 \\\/ /// CLabSys
  \\ ///  Cyber Lab System
   \\//   www.clabsys.com
    \/    

----------------------------------------------------------------------------
Description

*/

#include <windows.h>
#include <halether.h>
#include <bsp.h>


#include "loader.h"
#include "usbintr.h"
#include "usbapi.h"
#include <delay.h>
#include "usb.h"


// heratic <<
#define PDA_PACKET_HEADER_LENGTH			16
#define NANDFLASH_SECTOR_LENGTH				512
#define PDA_PACKET_LENGTH                   2048
#define PDA_ACK_LENGTH						4
#define RECORD_ADDRESS_OFFSET                        16
#define RECORD_LENGTH_OFFSET                        20
#define RECORD_CHECKSUM_OFFSET                    24
#define BIN_ADDRESS_OFFSET                        23
#define BIN_LENGTH_OFFSET                        27
typedef enum
{
 	Bin_NoError       =     0,   //0
 	Bin_Error ,                      // 1
 	Bin_Complete                 // 2              
} _BinErrorDefine;
//#define PDA_PACKET_LENGTH					(NANDFLASH_SECTOR_LENGTH*4 + PDA_PACKET_HEADER_LENGTH)	// 512 * 4 + 16(header)
// >>

//EBoot Command List
#define EBOOT_COMMAND_WRITETOMEM				1
#define EBOOT_COMMAND_LAUNCH_PDA				2
#define EBOOT_COMMAND_DUMP						3       // Not use
#define EBOOT_COMMAND_FLASH_BOOTLOADER			4
#define EBOOT_COMMAND_NOTIFY_UPDATE_IMAGE		5
#define EBOOT_COMMAND_ERASE						6       // Not use
#define EBOOT_COMMAND_NOTIFY_STARTDOWNLOADING 	7
#define EBOOT_COMMAND_CHECKSUM					10      // Not use
#define EBOOT_COMMAND_RESET_PHONE				11      // Not use
#define EBOOT_COMMAND_FLASH_WITHOUT_ERASE		12      // Not use
#define EBOOT_COMMAND_GETVERSION				13
#define EBOOT_COMMAND_SWITCH_USB_TO_PHONE		15
#define EBOOT_COMMAND_RESET_WIBRO				21      // Not use
#define EBOOT_COMMAND_SWITCH_USB_TO_WIBRO		25      // Not use
#define EBOOT_COMMAND_LCD_OFF					36      // Not use
#define EBOOT_COMMAND_SAVE_LAUNCH_ADDRESS		37      // Not use
#define EBOOT_COMMAND_CHECK_SIGNATURE			38      // Not use
#define EBOOT_COMMAND_GET_HW_REV				39      // Not use
#define EBOOT_COMMAND_GETMAGICNUMBER			40
#define EBOOT_COMMAND_MAGNETO_SAVE_LAUNCH_ADDR 	50      // Not use
#define	EBOOT_COMMAND_PDA_RESET					99      // Not use
#define EBOOT_COMMAND_SET_UGM_MODE		        100
#define EBOOT_COMMAND_GET_UGM_MODE		        101
#define EBOOT_COMMAND_DISABLE_CLEANBOOT			104
#define EBOOT_COMMAND_FLASH_HIDDENSTORE         105
#define EBOOT_COMMAND_OPENSTL					1000
#define EBOOT_COMMAND_PDAIMAGEWRITE				1001
#define EBOOT_COMMAND_CLOSESTL					1002
#define EBOOT_COMMAND_PRINT_PDADOWNLOAD2		1003
#define EBOOT_COMMAND_FLASH_PHONE				1004
#define EBOOT_COMMAND_FLASH_PDABIN				1005
//BIN PDA image download command
#define EBOOT_COMMAND_CSCIMAGEWRITE				1006
#define BIN_HEADER                                            2001
#define RECORD_HEADER                                     2002
#define RECORD_DATA                                         2003


// Update Image 
#define EBOOT_UPDATE 						(0x1<<0)
#define PDA_UPDATE 							(0x1<<1)
#define WIBROBOOT_UPDATE 					(0x1<<2)
#define WIBROLOADER_UPDATE 					(0x1<<3)
#define WIBROIMG_UPDATE 					(0x1<<4)
#define PHONE_UPDATE 						(0x1<<5)
// Erase NV Case
#define NV_DATA 							(0x1<<6)
#define NV_AREA 							(0x1<<7)
#define NV_BACKUP 							(0x1<<8)
#define EBOOT_CONFIG 						(0x1<<9)
// >>

extern int DMA_cnt;
extern DWORD           g_ImageType;

UCHAR RxBuffer[PDA_PACKET_LENGTH];

// Add to Emergency Download Mode (silisand)
BOOL 	g_USBRequest;

extern void INTC_Enable(UINT32 intNum);
extern void INTC_Disable(UINT32 intNum);
extern UINT32 USBWriteString(UINT8 * pbData, UINT32 cbData);

static void recvPacket(void);
static void processPacket(PUCHAR pRxBuffer);
static void SendOKUP(int progress);
int DWORDToBytes(UCHAR *szData, const DWORD dwData);
static int BytesToDWORD(UCHAR *szData, DWORD *dwData);
static DWORD CheckSum(DWORD org, UCHAR *data, DWORD len);
void hang(void);
static BOOL ReceivePDAPacket(DWORD targetAddress);
static DWORD ProcessPDAPacket(UCHAR *pRxBuffer, DWORD targetAddress);
static DWORD BINHeaderCheck(UCHAR *pRxBuffer, DWORD data_length, DWORD rxChecksum);
static DWORD ReceiveRecordHeader(UCHAR *pRxBuffer, DWORD data_length, DWORD rxChecksum, 
	                                      DWORD*record_address, DWORD *record_length, DWORD *record_checksum);
static DWORD ReceiveRecordData(DWORD targetAddress,  UCHAR *pRxBuffer, DWORD data_length, DWORD rxChecksum, DWORD record_num,
					DWORD BinStartAddress, DWORD record_address, DWORD record_length, DWORD record_checksum);
static void PDAReset(void);

BOOL	g_bErase = FALSE;
int down_phase=0;
BOOL 	g_bExit = FALSE;

extern 	BOOL            g_bDownloadImage ;
extern 	BOOL      	   g_bUSBDownload ;

#ifdef NEVER_USE
BOOL WritePdaRecord(DWORD targetAddress, DWORD BinStartAddress, DWORD record_address, DWORD record_length, DWORD record_num)
{
	DWORD nCnt;
	INT32 nErr;
	int SECTOR_PER_PAGE = 4;
	int SPARE8bit_PER_PAGE = 8;
	static DWORD dwPreRecordAddr = 0; //이전 record address 저장 
    static UINT32  dwSecNum = 0;  

	if(!targetAddress)
	{
		return FALSE;
	}
	
    	if(record_num == 1)
    	{
		    //GetPartitionInfo(targetAddress);
    	}
	
        if(record_address - dwPreRecordAddr>record_length)
        {
             // Do you know why there is a two-blocks-gap(-nRecLen*2) between partitons?
            dwSecNum += ((record_address - dwPreRecordAddr - record_length*3)/(XSR_SECTOR_SIZE*SECTOR_PER_PAGE + SPARE8bit_PER_PAGE))<<2;
        }

        for(nCnt=0; nCnt<record_length; nCnt+=XSR_SECTOR_SIZE*SECTOR_PER_PAGE + SPARE8bit_PER_PAGE)
        {
            /* Write OS image into OneNAND using STL */
            nErr = STL_Write(0, PARTITION_ID_COPIEDOS, dwSecNum, SECTOR_PER_PAGE, (UINT8 *)targetAddress);
            if(nErr != STL_SUCCESS)
            {
                XSR_RTL_PRINT( (TEXT("[ONW: ERR]  STL_Write Error at %d sector\r\n"), nCnt) );
                hang();
            }
            dwSecNum += SECTOR_PER_PAGE;
            targetAddress += (XSR_SECTOR_SIZE*SECTOR_PER_PAGE) + SPARE8bit_PER_PAGE;                                       
        }
	dwPreRecordAddr = record_address;

	return TRUE;
}
#endif

// Add to Emergency Download Mode (silisand)
BOOL IsUSBRequest(void)
{
	DWORD dwStartTime;
	DWORD dwCurrTime;
	DWORD dwWait=10;
    
	dwStartTime = OEMEthGetSecs();
	dwCurrTime = dwStartTime;

    if(USBIsCableAttached())
    {
        // USB Power On & detect
    	memset(RxBuffer, 0x0, PDA_PACKET_LENGTH);
        USBSetReadBuffer(RxBuffer);


		//EdbgOutputDebugString("EDNL: AFTER WAIT!\r\n");

		USBConnect(TRUE);          // D+ high
        
    	while((dwCurrTime - dwStartTime) < dwWait)	// emergency : 2000 sec, normal 6 sec
    	{
            if(g_USBRequest == TRUE)//繫법g_USBRequest털뙤USB角뤠헝헹냥묘
            {
                //EdbgOutputDebugString("EDNL: USBRequest OK!\r\n");
                return TRUE;
            }

            dwCurrTime = OEMEthGetSecs();
    	}

        // USB driver unload...
        if(USBIsCableAttached())
        {
        	USBConnect(FALSE);
            OALWaitUsec(200);
            OALWaitUsec(200);
            OALWaitUsec(200);
            OALWaitUsec(200);            
        }
    }

	return FALSE;
}

BOOL USBDownload(void) 
{
	if(IsUSBRequest()==FALSE)
	{
        EdbgOutputDebugString("EDNL: USB Request Time out \r\n");
        return FALSE;
	}

	recvPacket();
	return TRUE;
}

void recvPacket(void) 
{
	DWORD dwStartTime=0;
	DWORD dwCurrTime=0;
	DWORD Datalen;

	g_bExit = FALSE;

	memset(RxBuffer, 0x0, PDA_PACKET_LENGTH);
	USBSetReadBuffer(RxBuffer);

	while(!g_bExit)
    {
		Datalen = USBGetReadSize();

		if(Datalen == PDA_PACKET_LENGTH)
        {
            INTC_Disable(PHYIRQ_OTG); //NUM_OTG);
            processPacket(RxBuffer);
            INTC_Enable(PHYIRQ_OTG);
        }
        else if(Datalen > PDA_PACKET_LENGTH)
        {
            EdbgOutputDebugString(" Data overflow= %d\r\n", Datalen);
    		//printlcd("Download overflow...    ");
            USBSetReadBuffer(RxBuffer);
        }
    }

   OALMSG(TRUE,(L"EDNL: End of command loop...\r\n"));
}

void processPacket(PUCHAR pRxBuffer)
{
	UCHAR ackdata[4];
	DWORD cmd=0, address=0, length=0, rxChecksum=0; // received checksum
	PUCHAR targetAddress;                           // address to write received data
	unsigned int i      =0;
	DWORD dwBlockNum=0;
	DWORD calChecksum   =0;                         // calculated checksum
	//DWORD dwBootVer;

	// lee.kl <<
	static BOOL bCleanBoot              = TRUE;
	static DWORD totalPageNumber        = 0;
	static DWORD ramAddress             = 0;
	static DWORD bkBack                 = 0;
	static UINT32 curPackets            = 0;
	static UINT32 totalPackets          = 0;
	static DWORD percent_num            = 0;
	static UINT32 downloadPhase         = 0;
	static DWORD updateImage            = 0;
	static DWORD updateImageCount       = 0;
	// >>

	static DWORD dwDLType;
	
	BytesToDWORD(pRxBuffer, &cmd);
	BytesToDWORD(pRxBuffer+4, &address);
	BytesToDWORD(pRxBuffer+8, &length);
	BytesToDWORD(pRxBuffer+12, &rxChecksum);

	if (cmd == 0 )
	{
		EdbgOutputDebugString("EDNL: cmd %x start, downindex %d\r\n!", cmd, USBGetReadSize());
		for (i = 0 ; i<2048;i++)
		{
			if (i%16==0)
				EdbgOutputDebugString("\r\n");	
			EdbgOutputDebugString("0x%x!",pRxBuffer[i]);
		}
				EdbgOutputDebugString("\r\n");			
	}

	
	if(cmd == EBOOT_COMMAND_WRITETOMEM)    
	{
	    calChecksum = CheckSum(0, pRxBuffer+16, length);
	}
	else
	{
	    calChecksum = 0;
	}

	if(cmd == EBOOT_COMMAND_WRITETOMEM && calChecksum != rxChecksum) // Verify checksum only when cmd is write command
	{
	    EdbgOutputDebugString("EDNL: Checksum mismatch %x, %x!", rxChecksum, calChecksum);
            hang();
	}
    
	targetAddress = (PUCHAR)address;
	DWORDToBytes(ackdata, calChecksum);

	switch(cmd)
	{
		case EBOOT_COMMAND_NOTIFY_UPDATE_IMAGE:
			EdbgOutputDebugString("EDNL: SMDKV210 dose not support to update image!!! 0x%x 0x%x 0x%x\n",cmd,address,length,rxChecksum);
			updateImage = address;
			updateImageCount = length;
			if(rxChecksum & NV_AREA)
			{
				EdbgOutputDebugString("EDNL: [5] Erase NV Info : %x %x \n", rxChecksum, rxChecksum & NV_AREA);
				//EraseBMLPartition(PARTITION_ID_CP);
			}
			if(rxChecksum & NV_DATA)
			{
				EdbgOutputDebugString("EDNL: [5] Erase NV Data : %x %x \n", rxChecksum, rxChecksum & NV_DATA);
				//EraseBMLPartition(PARTITION_ID_NVDATA);
			}
			if(rxChecksum & NV_BACKUP)
			{
				EdbgOutputDebugString("EDNL: [5] Erase NV Backup : %x %x \n", rxChecksum, rxChecksum & NV_BACKUP);
				//EraseBMLPartition(PARTITION_ID_NVBACKUP);
			}
			if(rxChecksum & EBOOT_CONFIG)
			{
				EdbgOutputDebugString("EDNL: [5] Erase Eboot Config : %x %x \n", rxChecksum, rxChecksum & EBOOT_CONFIG);
				//EraseBMLPartition(PARTITION_ID_EBOOTCFG);
			}
			break;

		case EBOOT_COMMAND_WRITETOMEM:	//Write to SDRAM
            //EdbgOutputDebugString("EDNL: [1] Write to 0x%X. length=0x%X\r\n", targetAddress, length);
            if ( dwDLType == 0)
				memcpy((PBYTE)(ROM_RAMIMAGE_START + ((UINT32)targetAddress) - 0x80200000 ), pRxBuffer+16, length);
			else if (dwDLType == 1)
				memcpy((PBYTE)(EBOOT_USB_BUFFER_UA_START + ((UINT32)targetAddress)- 0x80200000), pRxBuffer+16, length);
	   	    break;

		case EBOOT_COMMAND_LAUNCH_PDA: 	// Launch
			EdbgOutputDebugString("EDNL: [2] Launch (PDA reset ignoring address:%X, CleanBoot:%x)\r\n", address, bCleanBoot);
            OALWaitUsec(200);
			PDAReset();
	   		break;

		case EBOOT_COMMAND_DUMP:	// Dump, Not use
			break;         

		case EBOOT_COMMAND_FLASH_BOOTLOADER: 	// Flash
			EdbgOutputDebugString("EDNL: [4]Flash write RAMAddr=%X, Length=%X, ROMAddr=%X\r\n", targetAddress, length, rxChecksum);
			if (dwDLType == 0)
			{
				g_ImageType = IMAGE_TYPE_BOOTIMAGE;
				WriteRawImageToBootMedia(0,length,0);				
			}
			else if (dwDLType == 1)
			{
				g_ImageType = IMAGE_TYPE_NK;
				
				g_bUSBDownload = TRUE;
				g_bDownloadImage = TRUE;
				g_bExit = TRUE;
			}

			EdbgOutputDebugString("EDNL: Store Start address %X & Length %X to flash.\r\n", targetAddress, length);
			
			EdbgOutputDebugString("EDNL: ### Clear password ###\r\n");
			break;

		case EBOOT_COMMAND_ERASE:	// Erase flash
	   	    break;

		case EBOOT_COMMAND_NOTIFY_STARTDOWNLOADING: //7
			downloadPhase = address;
			totalPackets = length;
			bkBack = 0;
			curPackets = 0;
			//Locate(EBOOT_MSG_OUT_ROW, 0);
			if(downloadPhase == 0)
			{
				EdbgOutputDebugString("EDNL: boot Downloading...   \r\n");
				dwDLType = 0;
			}
			else if(downloadPhase == 1)
			{
				EdbgOutputDebugString("EDNL: PDA Downloading...      \r\n");
				dwDLType =1;
			}
			else if(downloadPhase == 2)
			{
				EdbgOutputDebugString("EDNL: Phone Downloading...      \r\n");
			}
			else
			{
				EdbgOutputDebugString("EDNL: Download complete      \r\n");
			}
			break;

		case EBOOT_COMMAND_CHECKSUM: // Calculate checksum
			break;

		case EBOOT_COMMAND_RESET_PHONE:
		    break;

		case EBOOT_COMMAND_FLASH_WITHOUT_ERASE:	// Flash without erase
			break;
			
		case EBOOT_COMMAND_GETVERSION: // Get eboot version
			EdbgOutputDebugString("EDNL: [13]Eboot version=\r\n");
			DWORDToBytes(ackdata, 0x31313030); // to sync with TN DOWNLOADER tool
			USBWriteString(ackdata, 4);
	   		break;
			
		case EBOOT_COMMAND_OPENSTL:
			EdbgOutputDebugString("EDNL: [1000] OPENSTL cmd\r\n");
			memset((void*)FILE_CACHE_START, 0xff, FILE_CACHE_LENGTH);
			//PocketStoreII_STL_Format(PARTITION_ID_COPIEDOS);
			break;
			
		case EBOOT_COMMAND_PRINT_PDADOWNLOAD2:
			totalPageNumber = address;
			//Locate(EBOOT_MSG_OUT_ROW, 0);
			//printlcd("Writing PDA Image 2...  ");
			break;
			
		case EBOOT_COMMAND_PDAIMAGEWRITE:
			EdbgOutputDebugString("EDNL: [13] SMDKV210 dose not support PDAIMAGEWRITE cmd\r\n");
			break;
		case EBOOT_COMMAND_FLASH_PDABIN:
			USBSetReadBuffer(pRxBuffer);    // for USB High Speed... HERATIC
			USBWriteString(ackdata, 4);
			INTC_Enable(PHYIRQ_OTG);
    		if(!ReceivePDAPacket((DWORD)targetAddress))
    		{
    			EdbgOutputDebugString("EDNL: PDA file write failed\r\n");
				INTC_Disable(PHYIRQ_OTG);
				hang();
			}
			INTC_Disable(PHYIRQ_OTG);
			break;
			
		case EBOOT_COMMAND_CSCIMAGEWRITE:
			EdbgOutputDebugString("EDNL: Write CSCImage \r\n");
			break;
			

			
		case EBOOT_COMMAND_CLOSESTL:
			EdbgOutputDebugString("EDNL: [1002] Close STL \r\n");
			break;

		case EBOOT_COMMAND_SET_UGM_MODE:	// Clear Upgrade Mager Mode
			EdbgOutputDebugString("EDNL: [100] Change Upgrade Mager Mode = %d\r\n", address);
			break;

		case EBOOT_COMMAND_GET_UGM_MODE:	// Query Upgrade Mager Mode Flag
			EdbgOutputDebugString("EDNL: [101] Query Upgrade Mager Mode\r\n");
			break;

		case EBOOT_COMMAND_DISABLE_CLEANBOOT:	// Do not Clean Boot after Image download. 
			EdbgOutputDebugString("EDNL: [104] Disable Clean Boot Option at reboot.\r\n");
			bCleanBoot = FALSE;
			break;

		case EBOOT_COMMAND_FLASH_HIDDENSTORE:	// Do not Clean Boot after Image download. 
            break;
        
		case EBOOT_COMMAND_SWITCH_USB_TO_PHONE: // USB to phone and launch
			EdbgOutputDebugString("EDNL: SMDKV210 dose not suppport to switch USB\r\n",address);
			PDAReset();
    
    		break;
			
		case EBOOT_COMMAND_SWITCH_USB_TO_WIBRO:
			break;
			
		case EBOOT_COMMAND_LCD_OFF:    // Turn off backlight
			break;

		case EBOOT_COMMAND_SAVE_LAUNCH_ADDRESS:    // save launch address
			break;

		case EBOOT_COMMAND_GETMAGICNUMBER: // KJ 20051129 Returns eboot cfg magic number.
			EdbgOutputDebugString("EDNL: [40]EBOOT_CFG Magic Number\r\n");
			DWORDToBytes(ackdata, EBOOT_CFG_MAGIC_NUMBER);
			USBWriteString(ackdata, 4);
	   		break;

		case EBOOT_COMMAND_PDA_RESET: // Reset command
			//PDAReset();
			break;

		case EBOOT_COMMAND_FLASH_PHONE: 	// phone file Flash
			EdbgOutputDebugString("EDNL: SMDKV210 dosen't support Phone Image\r\n");
			break;

		default:
			EdbgOutputDebugString("EDNL: Unknown command = %d %d\r\n", cmd, address);
			break;
	}

	if(cmd != EBOOT_COMMAND_PDAIMAGEWRITE && cmd != EBOOT_COMMAND_FLASH_PDABIN && cmd != EBOOT_COMMAND_FLASH_HIDDENSTORE && cmd != EBOOT_COMMAND_FLASH_PHONE)
    {
		//EdbgOutputDebugString(".");	
		USBSetReadBuffer(pRxBuffer);    // for USB High Speed... HERATIC	
        USBWriteString(ackdata, 4);
    }
}

DWORD CheckSum(DWORD org, UCHAR *data, DWORD len) 
{
	DWORD temp = org;
	DWORD i;

	for( i=0 ; i<len ; i++)
	{
	    temp += *(data+i);
	}
	return temp;
}

int BytesToDWORD(UCHAR *szData, DWORD *dwData) 
{
	*dwData = szData[0];
	*dwData |= szData[1] << 8;
	*dwData |= szData[2] << 16;
	*dwData |= szData[3] << 24;
	return 0;      
}

int DWORDToBytes(UCHAR *szData, const DWORD dwData) 
{
	szData[0] = (UCHAR)dwData & 0x000000ff;
	szData[1] = (UCHAR)(dwData >> 8) & 0x000000ff;
	szData[2] = (UCHAR)(dwData >> 16) & 0x000000ff;
	szData[3] = (UCHAR)(dwData >> 24) & 0x000000ff;
	return 0;
}

void SendOKUP(int progress) 
{
	UCHAR ackdata[4] = {'O', 'K', 'U', 'P'};
	UCHAR negdata[4] = {'O', 'K', 'D', 'N'};

	if (progress == 101) //writing is finished
	{
		USBWriteString(negdata, 4);
	}
	else if (progress == 105) //writing is finished
	{
		ackdata[3] = 0xFF;
		USBWriteString(ackdata, 4);	// send progress of program
	}
	else
	{
		ackdata[3] = (UCHAR)progress;
		USBWriteString(ackdata, 4);	// send progress of program
	}
}
void hang(void)
{
	EdbgOutputDebugString("EDNL: Because of error, hang~~\r\n");
	while(1);
}

DWORD BinStartAddressGlobal;
DWORD BinlengthGlobal;
DWORD RamAddressGlobal;
DWORD record_address_Global;
DWORD record_length_Global;
DWORD record_checksum_Global;
DWORD total_length_Global;
BOOL ReceivePDAPacket(DWORD targetAddress)
{
	DWORD Datalen = 0;
	DWORD result = Bin_NoError;

	memset(RxBuffer, 0x0, PDA_PACKET_LENGTH);
	USBSetReadBuffer(RxBuffer);
	RamAddressGlobal = targetAddress;

	while(TRUE)
	{
    	Datalen = USBGetReadSize();
//		EdbgOutputDebugString("PDAPacketReceive Datalen %d\r\n", Datalen);

    	if(Datalen > PDA_PACKET_LENGTH)
   		{
			EdbgOutputDebugString("PDAPacketReceive overflow= %d\r\n", Datalen);
			//printlcd("PDAPacketReceive overflow...    ");
			USBSetReadBuffer(RxBuffer);
		}
		else if(Datalen == PDA_PACKET_LENGTH)
		{
			INTC_Disable(PHYIRQ_OTG); //NUM_OTG);
			result = ProcessPDAPacket(RxBuffer, targetAddress);
			INTC_Enable(PHYIRQ_OTG);
			if(result == Bin_Complete)
				return TRUE;
			else if(result == Bin_Error)
				return FALSE;
			else{}
	   	}
	}
	return TRUE;
}
DWORD ProcessPDAPacket(UCHAR *pRxBuffer, DWORD targetAddress)
{
	DWORD command = 0; 
	DWORD data_length = 0;
	DWORD record_num = 0;
	DWORD rxChecksum = 0;
	DWORD result = Bin_NoError;
	
	BytesToDWORD(pRxBuffer, &command);
	BytesToDWORD(pRxBuffer+4, &data_length);
	BytesToDWORD(pRxBuffer+8, &record_num);
	BytesToDWORD(pRxBuffer+12, &rxChecksum);

	switch((int)command)
	{
		case BIN_HEADER:
			result = BINHeaderCheck(pRxBuffer, data_length, rxChecksum);
			break;
		case RECORD_HEADER:
			result = ReceiveRecordHeader(pRxBuffer, data_length, rxChecksum, &record_address_Global, &record_length_Global, &record_checksum_Global);
			break;
		case RECORD_DATA:
			result = ReceiveRecordData(targetAddress,  pRxBuffer, data_length, rxChecksum, record_num,  BinStartAddressGlobal,
									record_address_Global, record_length_Global, record_checksum_Global);
			break;
		default:
			break;
	}
	USBSetReadBuffer(RxBuffer);
	return result;
}
//============ acks==================================
//
// {'O', 'K', 'P', 'K'}  data packet download complete
// {'O', 'K', 'D', 'N'}  full download complete
// {'N', 'A', 'C', 'K'}  no ack (when device receive data, checksum is wrong)
// {'O', 'K', 'R', 'C'}  record download complete
// {record length,'C'} send record length in record head
//
//==================================================
DWORD BINHeaderCheck(UCHAR *pRxBuffer, DWORD data_length, DWORD rxChecksum)
{
	UCHAR ackdata[4] =     {'O', 'K', 'R', 'C'};
	UCHAR noackdata[4] = {'N', 'A', 'C', 'K'};
	
	if(memcmp (pRxBuffer + 16, "B000FF\x0A", 7))
	{
		EdbgOutputDebugString("BINHeaderCheck False\r\n");
		USBWriteString(noackdata,PDA_ACK_LENGTH);
		return Bin_Error;
	}
	else
	{
		EdbgOutputDebugString("BINHeaderCheck True\r\n");
		BytesToDWORD(pRxBuffer+BIN_ADDRESS_OFFSET, &BinStartAddressGlobal);
	  	BytesToDWORD(pRxBuffer+BIN_LENGTH_OFFSET, &BinlengthGlobal);
		USBWriteString(ackdata,PDA_ACK_LENGTH);
		return Bin_NoError;
	}
	return 0;
}
DWORD ReceiveRecordHeader(UCHAR *pRxBuffer, DWORD data_length, DWORD rxChecksum, 
	                                      DWORD*record_address, DWORD *record_length, DWORD *record_checksum)
{
	UCHAR ackdata[4] =         {'O', 'K', 'P', 'K'};
	UCHAR completeack[4] = {'O', 'K', 'D', 'N'};
	UCHAR noackdata[4] =     {'N', 'A', 'C', 'K'};
	DWORD calChecksum;
	
	calChecksum = CheckSum(0, pRxBuffer+16, data_length);
	if(calChecksum != rxChecksum)
	{
		EdbgOutputDebugString("ReceiveRecordHeader:: checksum error\r\n");
		USBWriteString(noackdata,PDA_ACK_LENGTH);
		return Bin_Error;
	}
	BytesToDWORD(pRxBuffer+RECORD_ADDRESS_OFFSET, record_address);
	BytesToDWORD(pRxBuffer+RECORD_LENGTH_OFFSET, record_length);
	BytesToDWORD(pRxBuffer+RECORD_CHECKSUM_OFFSET, record_checksum);

	if(*record_address == 0 &&  *record_checksum ==0)
	{
		EdbgOutputDebugString("ReceiveRecordHeader:: PDA image completed\r\n");
		USBWriteString(completeack, PDA_ACK_LENGTH);
		return Bin_Complete;
	}

	DWORDToBytes(ackdata, *record_length);
	
	ackdata[3] = 'R';  // record 길이가 3byte는 넘지 않는다.

	USBWriteString(ackdata, PDA_ACK_LENGTH);
	
	return Bin_NoError;
}
DWORD ReceiveRecordData(DWORD targetAddress,  UCHAR *pRxBuffer, DWORD data_length, DWORD rxChecksum, DWORD record_num,
					DWORD BinStartAddress, DWORD record_address, DWORD record_length, DWORD record_checksum)
{
	DWORD calChecksum = 0;
	UCHAR ackdata[4] =            {'O', 'K', 'P', 'K'};
	UCHAR RecordAckdata[4] = {'O', 'K', 'R', 'C'};
	
	memcpy((UCHAR *)RamAddressGlobal, pRxBuffer+16, data_length);
	
	calChecksum = CheckSum(0, (UCHAR *)RamAddressGlobal, data_length);

	if(rxChecksum != calChecksum)
	{
		EdbgOutputDebugString("ReceiveRecordData:: checksum error\r\n");
		return Bin_Error;
	}

	total_length_Global +=  data_length;

	if(total_length_Global == record_length)
	{
		calChecksum = CheckSum(0, (UCHAR *)targetAddress, total_length_Global);
		if(calChecksum != record_checksum)
		{
			EdbgOutputDebugString("ReceiveRecordData:: Total checksum error\r\n");
			return Bin_Error;
		}
		EdbgOutputDebugString(".");

		//TODO write record
		//if(!WritePdaRecord(targetAddress, BinStartAddress, record_address, record_length, record_num))
		//	return Bin_Error;
		
		RamAddressGlobal = targetAddress;
		total_length_Global = 0;
		USBWriteString(RecordAckdata,PDA_ACK_LENGTH);
	}
	else
	{
		RamAddressGlobal += data_length;
		USBWriteString(ackdata,PDA_ACK_LENGTH);
	}

	return Bin_NoError;
}

void PDAReset(void)
{
	volatile WATCHDOG_REG *pWDRegs = (volatile WATCHDOG_REG *) OALPAtoVA(BASE_REG_PA_WATCHDOG, FALSE);

	// Set WDT
	pWDRegs->WTDAT = 0;
    pWDRegs->WTCNT = 5;         // Load count with low value.
    pWDRegs->WTCON = 0x8021;    // Enable watchdog timer...

	// Wait for watchdog reset...
	//
	while(TRUE);
}

BOOL CheckUSBDownload(void)
{
	// 2008/11/21 chul2.park: temporaily modified
    // Init USB
	USBInit();                  // USB path off, OTG pwr on, OSCotg off, D+ low.
	InitOTG(TRUE);                  // OTG init, interrupt enable
	
	if( USBIsCableAttached())
	{
		//EdbgOutputDebugString("BOOT: Entering DL or Emergency \r\n");
		if( !USBDownload() )
		{
			USBInit();                  // USB path off, OTG pwr on, OSCotg off, D+ low
			InitOTG(FALSE);                  // OTG init, interrupt enable	
            OALWaitUsec(200);
            OALWaitUsec(200);
            OALWaitUsec(200);
            OALWaitUsec(200);
            OALWaitUsec(200);
            OALWaitUsec(200);
            OALWaitUsec(200);
            OALWaitUsec(200);
            OALWaitUsec(200);
			USBDownload();
		}
	}

	INTC_Disable(PHYIRQ_OTG);

    //USBDeInit();	

	return TRUE;
}
