//-----------------------------------------------------------------------------
// FUNCTION:    main
// SCOPE:       Bootloader application system function
// DESCRIPTION: bootloader main function             
// RETURNS:     0
//----------------------------------------------------------------------------- 
int main(void)
{	
    volatile unsigned long loop_counter = 0;  // counter of loop without received frame
    
	Byte enableBootMode = 0; 			//  0: enter APP, 1: enter BOOT mode, 2: enter verify mode

	DisableInterrupts;
	//WDG_Disable();				//watchdog can be enable or disabled in kinetis_sysinit.c
	
	INIT_CLOCKS_TO_MODULES;			// init clock module
	Boot_Button_Init();  			// init SW1 button (PTC3 port) on FRDM_KL26 board
	INIT_BOOT_LED;
	
    //condition for entering boot:  
    if(((*(unsigned long*)(RELOCATED_VECTORS + 8)) == 0xffffffff)                          //1. no valid code in APP vector section,
    || Boot_StrCompare(( Byte*)APPOK_START_ADDRESS, str_app_ok, APPOK_LENGTH) == CHECK_FAIL	   //2."APP_OK" is wrong in address APPOK_START_ADDRESS.
    || ((GPIO_PDIR_REG(BOOT_PIN_ENABLE_GPIO_BASE) & (1 << BOOT_PIN_ENABLE_NUM)) == 0)      //3. SW1 is pressed
    || (AppIDC == 0x0000000B))                                                             //4. App request boot
    {
    	 enableBootMode = 1; // enable boot
    	 BOOT_LED_ON;
    	 AppIDC = 0;
    }
    else if (AppIDC == 0x0000000A)   // App request verify
    {
    	 enableBootMode = 2; // enable verify mode
    	 BOOT_LED_ON;
    	 AppIDC = 0;
    }
    
    if(enableBootMode)
    {
    	// if use external clock
#ifdef USE_EXTERNAL_CLOCK
    	Boot_Init_Clock( );
#endif
    	PIN_INIT_AS_UART;   			// set PTA1/UART0_RX and PTA2/UART0_TX pins as UART
    	UART_Initialization();  		// init UART module
    }

	////////////////////////////////////////////////////////////////////////
	//BOOT Model 
	////////////////////////////////////////////////////////////////////////
	while(enableBootMode)  // enter boot or verify mode, execute all command from GUI
	{  
		volatile Byte frame_start_flag  = 0;    // if frame header is received. 1: receive $, start to receive frame;  0: no $ received
		volatile Byte frame_length 	= 0xFF;    // SCI received frame length, initialized as 0xFF
		volatile Byte data_length = 0;
		volatile Byte buff_index = 1;   	   // receive buffer index for sci_buffer[]
		volatile Byte data_checked  = 0; 	   // check frame end 0xAA 0x55. 1: correct frame ; 0: wrong frame
				
		FLASH_Initialization();

		sci_buffer[0] = UART_GetChar();
		if( sci_buffer[0] == '$') 			//check frame header: whether it is '$'
		{
			loop_counter = 0;
			frame_start_flag  = 1;			
		}   
		
		if(frame_start_flag == 1)
		{
			sci_buffer[1] = UART_GetChar();     // sci_buffer[1] is the station number
			UART_GetChar();   					// sci_buffer[2] is reserved
			sci_buffer[3] = UART_GetChar();     // sci_buffer[3] is the Data length
			data_length = sci_buffer[3];
			frame_length = 4 + sci_buffer[3] + 2;  // frame_length = frame head + data + frame end 
			buff_index = 4;
			while(data_length --)
				sci_buffer[buff_index ++] = UART_GetChar();
			sci_buffer[buff_index ++] = UART_GetChar();  // frame end. It should be 0xAA
			sci_buffer[buff_index ++] = UART_GetChar();  // frame end. It should be 0x55
			frame_start_flag = 0;
		}
		
		if((sci_buffer[frame_length-2] == 0xAA) && (sci_buffer[frame_length-1] == 0x55)) //Check if Frame end is correct
		{
			data_checked = 1; //  Correct frame received. 
		}

		// all the data in frame was correctly received (data_checked = 1) above, now perform frame analysis below.
		// sci_buffer[] now is switched to tx buffer
		if(data_checked == 1)
		{ 
			Byte i = 0;  
			Byte j = 0;  	
			Byte s19length = 0;
			Byte s19buffer[WR_BLOCK_BYTE]; //extract app file burning code from received frame, store them to s19buffer[]
			
			WDG_Refresh();
			switch(sci_buffer[4])
			{
				
				case 'I':	// receive 'I' command, send  version information to UART    
							i = 4;
						    while((sci_buffer[i] = Identifier[i-4]) !='\0')	//assign chip part number info to sci_buffer[]
						    {
						    	i++;
						    }
						    sci_buffer[3] = i - 4;   // tx data length in one frame 
						    Boot_Send(sci_buffer);
						    if(enableBootMode == 1)  // if in boot mode
						      FLASH_EraseSector((LWord)APPOK_START_ADDRESS);	//erase APP_OK
						    break;
						    
				case 'E':	// receive 'E' command, erase sector, then send confirm frame to UART
							Boot_ReadAddress();
							if(!( FLASH_EraseSector(address.complete)))
							{
								 sci_buffer[3] = 00;							  
								 Boot_Send(sci_buffer);
							}
							break; 

				case'W':	// receive 'W' command, extract app  burning code,  program flash. then send confirm frame to UART
							Boot_ReadAddress();
							s19length = sci_buffer[8];
				
							for(j=0,i=9;j<s19length;j++,i++) // extract the prepared writing data from sci_buff[] to S19buffer[]
							{
								s19buffer[j] = sci_buffer[i];   
							}
							if(!(FLASH_ProgramSectionByLongs (address.complete, (LWord*)s19buffer, s19length/4))) 
							{            
								 sci_buffer[3] = 00;
								 Boot_Send(sci_buffer);
								 break;
							}

				case'R':	// receive 'R' command, read out the memory data, then send the data in frame to UART
							Boot_ReadAddress();
							s19length = sci_buffer[8];
							for(i=0;i<s19length;i++)
							{ 
								sci_buffer[i + 4] = ((Byte*)(address.complete))[i];
							}
							sci_buffer[3] = s19length;
							Boot_Send(sci_buffer);
							break;

				case'G':	// receive 'G' command, go to app. 
					        if(enableBootMode == 1)
				    		  FLASH_ProgramSectionByLongs ((LWord)APPOK_START_ADDRESS, (LWord*)str_app_ok, APPOK_LENGTH/4);   
					        
							Boot_Cpu_SystemReset( );
							// if we want  to jump to user application, we need to deinitialize used modules and switch MCG back to FEI mode
							break;

				default :
				         	break;
			}// end switch(sci_buffer[4])		
			frame_start_flag  = 0;
			for (buff_index = 0; buff_index<FRAME_BUFF_LENGTH; buff_index++)	// clear sci_buffer[]
				sci_buffer[buff_index] = 0;
			
		}// end if(data_checked == 1)
		loop_counter ++;
		if(loop_counter >= 10 && enableBootMode == 2)                         // 100 * timeout of UART_GetChar();
		{
			Boot_Cpu_SystemReset( );
		}
	}// end while(enableBootMode)
	
	// deinitialization of used modules
	UART_Deinitialization();
	DEINIT_BOOT_LED;
	DEINIT_CLOCKS_TO_MODULES;
	
	// relocate vector table
	SCB_VTOR = RELOCATED_VECTORS;	
	AppIDC = 0;
	// Jump to user application
	JumpToUserApplication(*((unsigned long*)RELOCATED_VECTORS), *((unsigned long*)(RELOCATED_VECTORS+4)));  
	return 0;
} // end main 
//-----------------------------------------------------------------------------
// FUNCTION:    main
// SCOPE:       Bootloader application system function
// DESCRIPTION: bootloader main function             
// RETURNS:     0
//----------------------------------------------------------------------------- 
int main(void)
{	
    volatile unsigned long loop_counter = 0;    // counter of loop without received frame

    Byte enableBootMode = 0; 			//  0: enter APP, 1: enter BOOT mode, 2: enter verify mode
    //Byte s19buffer[WR_BLOCK_BYTE] = {1};        //extract app file burning code from received frame, store them to s19buffer[]

    DisableInterrupts;


    INIT_CLOCKS_TO_MODULES;			// init clock module
    Boot_Button_Init();  			// init SW3 button (PTC3 port) on FRDM_K22 board
    INIT_BOOT_LED;
    //  condition for entering boot:
    if(((*(unsigned long*)(RELOCATED_VECTORS + 8)) == 0xffffffff)                          //1. no valid code in APP vector section,
    || Boot_StrCompare(( Byte*)APPOK_START_ADDRESS, str_app_ok, APPOK_LENGTH) == CHECK_FAIL	   //2."APP_OK" is wrong in address APPOK_START_ADDRESS.
    || ((GPIO_PDIR_REG(BOOT_PIN_ENABLE_GPIO_BASE) & (1 << BOOT_PIN_ENABLE_NUM)) == 0)      //3. SW1 is pressed
    || (AppIDC == 0x0000000B))                                                             //4. App request boot
    {
        enableBootMode = 1; // enable boot
        BOOT_LED_ON;
        AppIDC = 0;
    }
    else if (AppIDC == 0x0000000A)   // App request verify
    {
        enableBootMode = 2; // enable verify mode
        BOOT_LED_ON;
        AppIDC = 0;
    }
    if(enableBootMode)
    {
    // if use external clock
#ifdef USE_EXTERNAL_CLOCK
        Boot_Init_Clock();
#endif
        PIN_INIT_AS_UART;   			// set PTEA1/UART1_RX and PTE0/UART1_TX pins as UART
        UART_Initialization();  		// init UART module
    }   
    ////////////////////////////////////////////////////////////////////////
    //BOOT Model
    ////////////////////////////////////////////////////////////////////////
    while(enableBootMode)  // enter boot or verify mode, execute all command from GUI
    {        
        volatile Byte frame_start_flag  = 0;    // if frame header is received. 1: receive $, start to receive frame;  0: no $ received
        volatile Byte frame_length 	= 0xFF;    // SCI received frame length, initialized as 0xFF
        volatile Byte data_length = 0;
        volatile Byte buff_index = 1;   	   // receive buffer index for sci_buffer[]
        volatile Byte data_checked  = 0; 	   // check frame end 0xAA 0x55. 1: correct frame ; 0: wrong frame
        WDG_Refresh();
        FLASH_Initialization();

        sci_buffer[0] = UART_GetChar();
        if( sci_buffer[0] == '$') 			//check frame header: whether it is '$'
        {
            loop_counter = 0;
            frame_start_flag  = 1;
        }

        if(frame_start_flag == 1)
        {
            sci_buffer[1] = UART_GetChar();     // sci_buffer[1] is the station number
            UART_GetChar();   					// sci_buffer[2] is reserved
            sci_buffer[3] = UART_GetChar();     // sci_buffer[3] is the Data length
            data_length = sci_buffer[3];
            frame_length = 4 + sci_buffer[3] + 2;  // frame_length = frame head + data + frame end
            buff_index = 4;
            while(data_length --)
                      sci_buffer[buff_index ++] = UART_GetChar();
            sci_buffer[buff_index ++] = UART_GetChar();  // frame end. It should be 0xAA
            sci_buffer[buff_index ++] = UART_GetChar();  // frame end. It should be 0x55
            frame_start_flag = 0;
        }

        if((sci_buffer[frame_length-2] == 0xAA) && (sci_buffer[frame_length-1] == 0x55)) //Check if Frame end is correct
        {
            data_checked = 1; //  Correct frame received.
        }

       // all the data in frame was correctly received (data_checked = 1) above, now perform frame analysis below.
       // sci_buffer[] now is switched to tx buffer
        if(data_checked == 1)
        {
            Byte i = 0;
            Byte j = 0;
            Byte burn_data_length = 0;
            Byte s19buffer[WR_BLOCK_BYTE]; //extract app file burning code from received frame, store them to s19buffer[]

            //WDG_Refresh();
            switch(sci_buffer[4])
            {
                case 'I':	// receive 'I' command, send  version information to UART
                            i = 4;
                            while((sci_buffer[i] = MCU_Identification.targ_name[i-4]) !='\0')	//assign chip part number info to sci_buffer[]
                            {
                                    i++;
                            }
                            sci_buffer[i++] = '#';
                            j = i;
                            while((sci_buffer[i] = MCU_Identification.Boot_version[i-j]) !='\0')	//assign bootloader version info to sci_buffer[]
                            {
                                    i++;
                            }
                            sci_buffer[i++] = '#';
                            sci_buffer[i++] = (MCU_Identification.wrblk & 0xFF00)>>8;		//assign write block size
                            sci_buffer[i++] = MCU_Identification.wrblk & 0x00FF;

                            sci_buffer[i++] = (MCU_Identification.erblk & 0xFF00)>>8;		//assign erase block size
                            sci_buffer[i++] = MCU_Identification.erblk & 0x00FF;

                            sci_buffer[i++] = MCU_Identification.addr_limit.Bytes.hl;		 //assign MCU Flash end address
                            sci_buffer[i++] = MCU_Identification.addr_limit.Bytes.lh;
                            sci_buffer[i++] = MCU_Identification.addr_limit.Bytes.ll;

                            sci_buffer[i++] = MCU_Identification.dontcare_addrl.Bytes.hl;		//assign bootloader dont care memory start adddress
                            sci_buffer[i++] = MCU_Identification.dontcare_addrl.Bytes.lh;
                            sci_buffer[i++] = MCU_Identification.dontcare_addrl.Bytes.ll;

                            sci_buffer[i++] = MCU_Identification.dontcare_addrh.Bytes.hl;		//assign bootloader dont care memory end adddress
                            sci_buffer[i++] = MCU_Identification.dontcare_addrh.Bytes.lh;
                            sci_buffer[i++] = MCU_Identification.dontcare_addrh.Bytes.ll;

                            sci_buffer[i++] = MCU_Identification.relocated_vectors.Bytes.hl;		//assign bootloader start adddress
                            sci_buffer[i++] = MCU_Identification.relocated_vectors.Bytes.lh;
                            sci_buffer[i++] = MCU_Identification.relocated_vectors.Bytes.ll;

                            sci_buffer[i++] = MCU_Identification.targ_core; 	// target is M4 core
                            
                            sci_buffer[3] = i - 4;   // tx data length in one frame 
                            Boot_Send(sci_buffer);
                            if(enableBootMode == 1)  // if in boot mode
                                FLASH_EraseSector((LWord)APPOK_START_ADDRESS);	//erase APP_OK
                            WDG_Refresh();
                            break;

                case 'E':	// receive 'E' command, erase sector, then send confirm frame to UART
                            Boot_ReadAddress();
                            if(!( FLASH_EraseSector(address.complete)))
                            {
                                sci_buffer[3] = 00;
                                Boot_Send(sci_buffer);
                            }
                            WDG_Refresh();
                            break;

                case'W':	// receive 'W' command, extract app  burning code,  program flash. then send confirm frame to UART
                            Boot_ReadAddress();
                            burn_data_length = sci_buffer[8];
                            for(j=0,i=9;j<burn_data_length;j++,i++) // extract the prepared writing data from sci_buff[] to S19buffer[]
                            {
                                s19buffer[j] = sci_buffer[i];
                            }
                            if(!(FLASH_ProgramSectionByLongs (address.complete, (LWord*)s19buffer, burn_data_length/4)))
                            {
                                sci_buffer[3] = 00;
                                Boot_Send(sci_buffer);
                                
                            }
                            WDG_Refresh();
                            break;
                case'R':	// receive 'R' command, read out the memory data, then send the data in frame to UART
                            Boot_ReadAddress();
                            burn_data_length = sci_buffer[8];
                            for(i=0;i<burn_data_length;i++)
                            {
                                sci_buffer[i + 4] = ((Byte*)(address.complete))[i];
                            }
                            sci_buffer[3] = burn_data_length;
                            Boot_Send(sci_buffer);
                            WDG_Refresh();
                            break;

                case'G':	// receive 'G' command, go to app.
                            if(enableBootMode == 1)
                            FLASH_ProgramSectionByLongs ((LWord)APPOK_START_ADDRESS, (LWord*)str_app_ok, APPOK_LENGTH/4);
                            //BOOT_LED_OFF;
                            NVIC_SystemReset();
                            break;

                default :
                            break;
            }// end switch(sci_buffer[4])
            frame_start_flag  = 0;
            for (buff_index = 0; buff_index<FRAME_BUFF_LENGTH; buff_index++)	// clear sci_buffer[]
                sci_buffer[buff_index] = 0;

          }// end if(data_checked == 1)
          loop_counter ++;
          if(loop_counter >= 10 && enableBootMode == 2)                         // 100 * timeout of UART_GetChar();
          {
              NVIC_SystemReset();
          }
    }// end while(enableBootMode)