/*===========================================================================
                    Combined Charging System (CCS): SECC
                              FWUpdate.c

                        initiated by Vern, Joseph
                           (since 2019/07/19)
=============================================================================*/
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <linux/termios.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/mman.h>
#include <linux/sockios.h>
#include <linux/socket.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/time.h>
#include <sys/timeb.h>
#include <math.h>//for pow
#include "define.h"
#include "FWUpdate.h"
#include <unistd.h>

struct SysConfigAndInfo             *ShmSysConfigAndInfo;
struct StatusCodeData               *ShmStatusCodeData;
struct CcsData                      *ShmCcsData;
struct InternalComm               *ShmInternalComm;
//struct InternalCommAC               *ShmInternalCommAC;

unsigned char buf_log_fwupdate[SIZE_OF_LOG_BUFFER];

/*===========================================================================
FUNCTION: Check_V2G_Flow_Status
DESCRIPTION:
PRE-CONDITION:
INPUT:
OUTPUT:
GLOBAL VARIABLES:
=============================================================================*/
unsigned char Check_V2G_Flow_Status()
{
    unsigned char result = 0;

    switch (ShmCcsData->CommProtocol)
    {
        case V2GT_MSG_PROTOCOL_DIN70121:        //0
        {
            result = ShmCcsData->V2GMessage_DIN70121.PresentMsgFlowStatus;
            break;
        }

        case V2GT_MSG_PROTOCOL_ISO15118_2014:   //1
        {
            result = ShmCcsData->V2GMessage_ISO15118_2014.PresentMsgFlowStatus;
            break;
        }

        case V2GT_MSG_PROTOCOL_ISO15118_2018:   //2
        {
            result = ShmCcsData->V2GMessage_ISO15118_2018.PresentMsgFlowStatus;
            break;
        }

        default:
            break;
    }
    return result;
}

/*===========================================================================
FUNCTION: StoreLogMsg
DESCRIPTION:
PRE-CONDITION:
INPUT:
OUTPUT:
GLOBAL VARIABLES:
=============================================================================*/
#if SAVE_SYS_LOG_MSG_FWUPDATE_SWITCH == ENABLE
int StoreLogMsg(unsigned char *DataString)
{
    static unsigned char Buf[1024];
    static time_t CurrentTime;
    static struct tm *tm;
    static struct timeval tv;

    memset(Buf, 0, sizeof(Buf));
    CurrentTime = time(NULL);
    tm = localtime(&CurrentTime);
    gettimeofday(&tv, NULL); // get microseconds, 10^-6

    sprintf(Buf, "echo \"[%04d%02d%02d: %02d:%02d:%02d.%06d][FWUpdate][%d][%02d]%s\" >> /Storage/SystemLog/[%04d.%02d]SystemLog",
            tm->tm_year + 1900,
            tm->tm_mon + 1,
            tm->tm_mday,
            tm->tm_hour,
            tm->tm_min,
            tm->tm_sec,
            tv.tv_usec,
            EVCOMM_SYS_INFO.CpState,
            Check_V2G_Flow_Status(),
            DataString,
            tm->tm_year + 1900,
            tm->tm_mon + 1);
    system(Buf);

    DEBUG_PRINTF_FWUPDATE_SYSTEM_LOG("[%02d:%02d:%02d.%06d][FWUpdate][%d][%02d]%s \n",
            tm->tm_hour,
            tm->tm_min,
            tm->tm_sec,
            tv.tv_usec,
            EVCOMM_SYS_INFO.CpState,
            Check_V2G_Flow_Status(),
            DataString);

    //Reset the buf_log_fwupdate Buffer, i.e. DataString
    memset(buf_log_fwupdate, 0, SIZE_OF_LOG_BUFFER);
}
#endif

/*===========================================================================
FUNCTION: fsize
DESCRIPTION:
PRE-CONDITION:
INPUT:
OUTPUT:
GLOBAL VARIABLES:
=============================================================================*/
unsigned long fsize(char* filename)
{
    SAVE_SYS_LOG_MSG_FWUPDATE("[fsize]image size: caculating...");
    unsigned long length = 0;
    FILE *file;

    file = fopen(filename, "rb");
    if (file == NULL)
    {
        SAVE_SYS_LOG_MSG_FWUPDATE("[fsize]fopen: fail");
        return 0;
    }
    else
    {
        SAVE_SYS_LOG_MSG_FWUPDATE("[fsize]fopen: OK");
        fseek(file, 0, SEEK_END);
        length = (unsigned long) ftell(file);
    }
    fclose(file);
    sprintf(buf_log_fwupdate, "[fsize]image size: %ld bytes", length);
    SAVE_SYS_LOG_MSG_FWUPDATE(buf_log_fwupdate);
    return length;
}

/*===========================================================================
FUNCTION: crc32
DESCRIPTION:
PRE-CONDITION:
INPUT:
OUTPUT:
GLOBAL VARIABLES:
=============================================================================*/
uint32_t crc32(uint8_t *data, unsigned int length)
{
    SAVE_SYS_LOG_MSG_FWUPDATE("[crc32]ongoing...");
    uint8_t i;
    uint32_t cnt = 0;
    uint32_t crc = 0xffffffff;  // Initial value
    while(length--)
    {
    	if(cnt > 33 && cnt < 48)
        {
    		data++;
    	}
        else
        {
    		crc ^= *data++;         // crc ^= *data; data++;
			for (i = 0; i < 8; ++i)
			{
				if (crc & 1)
					crc = (crc >> 1) ^ 0xEDB88320;// 0xEDB88320= reverse 0x04C11DB7
				else
					crc = (crc >> 1);
			}
    	}
    	cnt++;
    }
    return ~crc;
}

/*===========================================================================
FUNCTION: ShareMemory_Init
DESCRIPTION
        Initialize all share memories.
PRE-CONDITION:
INPUT:
OUTPUT:
GLOBAL VARIABLES:
=============================================================================*/
int ShareMemory_Init()
{
    int MeterSMId;

    //[1/5] create ShmSysConfigAndInfo
    if((MeterSMId = shmget(ShmSysConfigAndInfoKey, sizeof(struct SysConfigAndInfo),  0777)) < 0)
    {
        SAVE_SYS_LOG_MSG_FWUPDATE("ShareMemory_Init:shmget ShmSysConfigAndInfo NG");
        return 0;
    }
    else if((ShmSysConfigAndInfo = shmat(MeterSMId, NULL, 0)) == (void *)-1)
    {
        SAVE_SYS_LOG_MSG_FWUPDATE("ShareMemory_Init:shmat ShmSysConfigAndInfo NG");
        return 0;
    }

    //[2/5] create ShmStatusCodeData
    if((MeterSMId = shmget(ShmStatusCodeKey, sizeof(struct StatusCodeData),  0777)) < 0)
    {
        SAVE_SYS_LOG_MSG_FWUPDATE("ShareMemory_Init:shmget ShmStatusCodeData NG");
        return 0;
    }
    else if((ShmStatusCodeData = shmat(MeterSMId, NULL, 0)) == (void *)-1)
    {
        SAVE_SYS_LOG_MSG_FWUPDATE("ShareMemory_Init:shmat ShmStatusCodeData NG");
        return 0;
    }

    //[3/5] create ShmCcsData
    if((MeterSMId = shmget(ShmCcsCommKey, sizeof(struct CcsData),  0777)) < 0)
    {
        SAVE_SYS_LOG_MSG_FWUPDATE("ShareMemory_Init:shmget ShmCcsData NG");
        return 0;
    }
    else if((ShmCcsData = shmat(MeterSMId, NULL, 0)) == (void *)-1)
    {
        SAVE_SYS_LOG_MSG_FWUPDATE("ShareMemory_Init:shmat ShmCcsData NG");
        return 0;
    }

    //[4/5] create ShmInternalComm
    if((MeterSMId = shmget(ShmInternalCommKey, sizeof(struct InternalComm),  IPC_CREAT | 0777)) < 0)
    {
        SAVE_SYS_LOG_MSG_FWUPDATE("ShareMemory_Init:shmget ShmInternalComm NG");
        return 0;
    }
    else if((ShmInternalComm = shmat(MeterSMId, NULL, 0)) == (void *)-1)
    {
        SAVE_SYS_LOG_MSG_FWUPDATE("ShareMemory_Init:shmat ShmInternalComm NG");
        return 0;
    }
	#if 0
    //[5/5] create ShmInternalCommAC
    if((MeterSMId = shmget(ShmInternalCommACKey, sizeof(struct InternalCommAC),  IPC_CREAT | 0777)) < 0)
    {
        SAVE_SYS_LOG_MSG_FWUPDATE("ShareMemory_Init:shmget ShmInternalCommAC NG");
        return 0;
    }
    else if((ShmInternalCommAC = shmat(MeterSMId, NULL, 0)) == (void *)-1)
    {
        SAVE_SYS_LOG_MSG_FWUPDATE("ShareMemory_Init:shmat ShmInternalCommAC NG");
        return 0;
    }
	#endif
    return 1;
}

/*===========================================================================
FUNCTION: Update_MLO
DESCRIPTION:
PRE-CONDITION:
INPUT:
OUTPUT:
GLOBAL VARIABLES:
=============================================================================*/
unsigned char Update_MLO()
{
    SAVE_SYS_LOG_MSG_FWUPDATE("-------- [MLO]start --------");

    unsigned char result = PASS;
    long int MaxLen = 48*1024*1024;
    unsigned long file_length = 0;
    unsigned char *ptr = malloc(MaxLen);

    if(ptr == NULL)
    {
        return FAIL;
    }

    memset(ptr, 0, MaxLen);

    file_length = fsize("/root/MLO");

    int fd = 0;
    int wrd = 0;
    FILE *file;
    fd = open("/dev/mtdblock0", O_RDWR);
    file = fopen("/root/MLO", "rb");

    if (fd < 0)
    {
        SAVE_SYS_LOG_MSG_FWUPDATE("[MLO]Can not open mtdblock0\n");
        free(ptr);
        return FAIL;
    }
    else if (file == NULL)
    {
        SAVE_SYS_LOG_MSG_FWUPDATE("[MLO]Can not open or find the downloaded image file\n");
        free(ptr);
        return FAIL;
    }
    else
    {
        //[VITAL] Closing unneccesary task to supoort rapid CAN data receving..
        SAVE_SYS_LOG_MSG_FWUPDATE("[MLO] closing SeccComm...");
        system("killall SeccComm");

        // Write image to flash
        SAVE_SYS_LOG_MSG_FWUPDATE("[MLO]Writing image to mtdblock0...");
        fread(ptr, sizeof(unsigned char), file_length, file);
        wrd = write(fd, ptr, file_length);

        close(fd);
        fclose(file);
        sprintf(buf_log_fwupdate, "[MLO]mtdblock0 - written length: %d bytes\n", wrd);
        SAVE_SYS_LOG_MSG_FWUPDATE(buf_log_fwupdate);

        if(wrd != file_length)
        {
            free(ptr);
            return FAIL;
        }
    }
    free(ptr);
    return result;
}

/*===========================================================================
FUNCTION: IMAGE_TYPE_BOOTLOADER_UBOOT
DESCRIPTION:
PRE-CONDITION:
INPUT:
OUTPUT:
GLOBAL VARIABLES:
=============================================================================*/
unsigned char Update_uboot()
{
    SAVE_SYS_LOG_MSG_FWUPDATE("-------- [uboot]start --------");

    unsigned char result = PASS;
    long int MaxLen = 48*1024*1024;
    unsigned long file_length = 0;
    unsigned char *ptr = malloc(MaxLen);

    if(ptr == NULL)
    {
        return FAIL;
    }

    memset(ptr, 0, MaxLen);

    file_length = fsize("/root/u-boot.img");

    int fd = 0;
    int wrd = 0;
    FILE *file;
    fd = open("/dev/mtdblock1", O_RDWR);
    file = fopen("/root/u-boot.img", "rb");
    if (fd < 0)
    {
        SAVE_SYS_LOG_MSG_FWUPDATE("[uboot]Can not open mtdblock1\n");
        free(ptr);
        return FAIL;
    }
    else if (file == NULL)
    {
        SAVE_SYS_LOG_MSG_FWUPDATE("[uboot]Can not open the downloaded image file\n");
        free(ptr);
        return FAIL;
    }
    else
    {
        //[VITAL] Closing unneccesary task to supoort rapid CAN data receving..
        SAVE_SYS_LOG_MSG_FWUPDATE("[uboot] closing SeccComm...");
        system("killall SeccComm");

        // Write image to flash
        SAVE_SYS_LOG_MSG_FWUPDATE("[uboot]Writing image to mtdblock1...");
     //   fread(ptr, sizeof(unsigned char), file_length, file);
      
        //wrd = write(fd, ptr, file_length);

        close(fd);
        fclose(file);
                
        system("/bin/flash_erase /dev/mtd1 0 2");
	system("/usr/sbin/nandwrite -p /dev/mtd1 /root/u-boot.img");
	system("/bin/flash_erase /dev/mtd3 0 2");
	system("/usr/sbin/nandwrite -p /dev/mtd3 /root/u-boot.img");

        
        sprintf(buf_log_fwupdate, "[uboot]mtdblock1 - written length: %d bytes\n", wrd);
        SAVE_SYS_LOG_MSG_FWUPDATE(buf_log_fwupdate);
#if 0
        if(wrd != file_length)
        {
            free(ptr);
            return FAIL;
        }
        else
        {
            // Open flash target mtdblock
            fd = open("/dev/mtdblock3", O_RDWR);
            if (fd < 0)
            {
                SAVE_SYS_LOG_MSG_FWUPDATE("[uboot]Can not open mtdblock3\n");
                free(ptr);
                return FAIL;
            }
            else if (file == NULL)
            {
                SAVE_SYS_LOG_MSG_FWUPDATE("[uboot]Can not open the downloaded image file\n");
                free(ptr);
                return FAIL;
            }
            else
            {
                // Write image to flash
                SAVE_SYS_LOG_MSG_FWUPDATE("[uboot]Writing image to mtdblock3...");
                wrd = write(fd, ptr, file_length);

                close(fd);
                sprintf(buf_log_fwupdate, "[uboot]mtdblock3 - written length: %d bytes\n", wrd);
                SAVE_SYS_LOG_MSG_FWUPDATE(buf_log_fwupdate);

                if(wrd != file_length)
                {
                    free(ptr);
                    return FAIL;
                }
            }
        }
#endif    
    }
    return result;
}

/*===========================================================================
FUNCTION: Update_DTB
DESCRIPTION:
PRE-CONDITION:
INPUT:
OUTPUT:
GLOBAL VARIABLES:
=============================================================================*/
unsigned char Update_DTB()
{
    SAVE_SYS_LOG_MSG_FWUPDATE("-------- [DTB]start --------");

    unsigned char result = PASS;
    long int MaxLen = 48*1024*1024;
    unsigned long file_length = 0;
    unsigned char *ptr = malloc(MaxLen);

    if(ptr == NULL)
    {
        return FAIL;
    }

    memset(ptr, 0, MaxLen);

    file_length = fsize("/root/am335x-evm.dtb");

    int fd = 0;
    int wrd = 0;
    FILE *file;
    fd = open("/dev/mtdblock4", O_RDWR);
    file = fopen("/root/am335x-evm.dtb", "rb");
    if (fd < 0)
    {
        SAVE_SYS_LOG_MSG_FWUPDATE("[DTB]Can not open mtdblock4\n");
        free(ptr);
        return FAIL;
    }
    else if (file == NULL)
    {
        SAVE_SYS_LOG_MSG_FWUPDATE("[DTB]Can not open the downloaded image file\n");
        free(ptr);
        return FAIL;
    }
    else
    {
        //[VITAL] Closing unneccesary task to supoort rapid CAN data receving..
        SAVE_SYS_LOG_MSG_FWUPDATE("[DTB] closing SeccComm...");
        system("killall SeccComm");

        // Write image to flash
        SAVE_SYS_LOG_MSG_FWUPDATE("[DTB]Writing image to mtdblock4...");
      //  fread(ptr, sizeof(unsigned char), file_length, file);
      //  wrd = write(fd, ptr, file_length);

        close(fd);
        fclose(file);
        
        system("/bin/flash_erase /dev/mtd4 0 1");
	system("/usr/sbin/nandwrite -p /dev/mtd4 /root/am335x-evm.dtb");

	system("/bin/flash_erase /dev/mtd5 0 1");
	system("/usr/sbin/nandwrite -p /dev/mtd5 /root/am335x-evm.dtb");
        
        
        sprintf(buf_log_fwupdate, "[DTB]mtdblock4 - written length: %d bytes\n", wrd);
        SAVE_SYS_LOG_MSG_FWUPDATE(buf_log_fwupdate);
#if 0
        if(wrd != file_length)
        {
            free(ptr);
            return FAIL;
        }
        else
        {
            // Open flash target mtdblock
            fd = open("/dev/mtdblock5", O_RDWR);
            if (fd < 0)
            {
                SAVE_SYS_LOG_MSG_FWUPDATE("[DTB]Can not open mtdblock5");
                free(ptr);
                return FAIL;
            }
            else
            {
                // Write image to flash
                SAVE_SYS_LOG_MSG_FWUPDATE("[DTB]Writing image to mtdblock5...");
                wrd=write(fd, ptr, file_length);
                close(fd);
                sprintf(buf_log_fwupdate, "[DTB]mtdblock5 - written length: %d bytes\n", wrd);
                SAVE_SYS_LOG_MSG_FWUPDATE(buf_log_fwupdate);
                if(wrd != file_length)
                {
                    free(ptr);
                    return FAIL;
                }
            }
        }
#endif        
    }
    return result;
}

/*===========================================================================
FUNCTION: Update_zImage
DESCRIPTION:
PRE-CONDITION:
INPUT:
OUTPUT:
GLOBAL VARIABLES:
=============================================================================*/
unsigned char Update_zImage()
{
    SAVE_SYS_LOG_MSG_FWUPDATE("-------- [zImage]start --------");

    unsigned char result = PASS;
    long int MaxLen = 48*1024*1024;
    unsigned long file_length = 0;
    unsigned char *ptr = malloc(MaxLen);

    if(ptr == NULL)
    {
        return FAIL;
    }

    memset(ptr, 0, MaxLen);

    file_length = fsize("/root/zImage");

    int fd = 0;
    int wrd = 0;
    FILE *file;
    fd = open("/dev/mtdblock6", O_RDWR);
    file = fopen("/root/zImage", "rb");
    if (fd < 0)
    {
        SAVE_SYS_LOG_MSG_FWUPDATE("[zImage]Can not open mtdblock6\n");
        free(ptr);
        return FAIL;
    }
    else if (file == NULL)
    {
        SAVE_SYS_LOG_MSG_FWUPDATE("[zImage]Can not open the downloaded image file\n");
        free(ptr);
        return FAIL;
    }
    else
    {
        //[VITAL] Closing unneccesary task to supoort rapid CAN data receving..
        SAVE_SYS_LOG_MSG_FWUPDATE("[zImage] closing SeccComm...");
        system("killall SeccComm");

        // Write image to flash
        SAVE_SYS_LOG_MSG_FWUPDATE("[zImage]Writing image to mtdblock6...");
      //  fread(ptr, sizeof(unsigned char), file_length, file);
       // wrd = write(fd, ptr, file_length);

        close(fd);
        fclose(file);
        
	
	system("/bin/flash_erase /dev/mtd6 0 20");
	system("/usr/sbin/nandwrite -p /dev/mtd6 /root/zImage");

	system("/bin/flash_erase /dev/mtd7 0 20");
	system("/usr/sbin/nandwrite -p /dev/mtd7 /root/zImage");
        
        sprintf(buf_log_fwupdate, "[zImage]mtdblock6 - written length: %d bytes\n", wrd);
        SAVE_SYS_LOG_MSG_FWUPDATE(buf_log_fwupdate);
#if 0
        if(wrd != file_length)
        {
            free(ptr);
            return FAIL;
        }
        else
        {
            // Open flash target mtdblock
            fd = open("/dev/mtdblock7", O_RDWR);
            if (fd < 0)
            {
                SAVE_SYS_LOG_MSG_FWUPDATE("[zImage]Can not open mtdblock7");
                free(ptr);
                return FAIL;
            }
            else
            {
                // Write image to flash
                SAVE_SYS_LOG_MSG_FWUPDATE("[zImage]Writing image to mtdblock7...");
                wrd = write(fd, ptr, file_length);
                close(fd);
                sprintf(buf_log_fwupdate, "[zImage]mtdblock7 - written length: %d bytes\n", wrd);
                SAVE_SYS_LOG_MSG_FWUPDATE(buf_log_fwupdate);
                if(wrd != file_length)
                {
                    free(ptr);
                    return FAIL;
                }
            }
        }
#endif        
    }
    return result;
}

/*===========================================================================
FUNCTION: Update_ramdisk
DESCRIPTION:
PRE-CONDITION:
INPUT:
OUTPUT:
GLOBAL VARIABLES:
=============================================================================*/
unsigned char Update_ramdisk()
{
    SAVE_SYS_LOG_MSG_FWUPDATE("-------- [ramdisk]start --------");

    unsigned char result = PASS;
    long int MaxLen = 48*1024*1024;
    unsigned long file_length = 0;
    unsigned char *ptr = malloc(MaxLen);

    if(ptr == NULL)
    {
        return FAIL;
    }

    memset(ptr, 0, MaxLen);

    file_length = fsize("/root/ramdisk.gz");

    int fd = 0;
    int wrd = 0;
    FILE *file;
    fd = open("/dev/mtdblock8", O_RDWR);
    file = fopen("/root/ramdisk.gz", "rb");
    if(fd < 0)
    {
        SAVE_SYS_LOG_MSG_FWUPDATE("[ramdisk]UpdateRootfs NG - can not open rootfs\n");
        return FAIL;
    }
    else if (file == NULL)
    {
        SAVE_SYS_LOG_MSG_FWUPDATE("[ramdisk]Can not open the downloaded image file\n");
        return FAIL;
    }
    else
    {
        //[VITAL] Closing unneccesary task to supoort rapid CAN data receving..
        SAVE_SYS_LOG_MSG_FWUPDATE("[ramdisk] closing SeccComm...");
        system("killall SeccComm");

        SAVE_SYS_LOG_MSG_FWUPDATE("[ramdisk]Writing image to mtdblock8...");
        //fread(ptr, sizeof(unsigned char), file_length, file);
        //wrd = write(fd, ptr, file_length);
        close(fd);
        fclose(file);
        

       system("/bin/flash_erase /dev/mtd8 0 96");
       system("/usr/sbin/nandwrite -p /dev/mtd8 /root/ramdisk.gz");

       system("/bin/flash_erase /dev/mtd9 0 96");
       system("/usr/sbin/nandwrite -p /dev/mtd9 /root/ramdisk.gz");
                                	
                                	
        sprintf(buf_log_fwupdate, "[ramdisk]mtdblock8 - written length: %d bytes\n", wrd);
        SAVE_SYS_LOG_MSG_FWUPDATE(buf_log_fwupdate);
#if 0      
        if(wrd != file_length)
        {
            free(ptr);
            return FAIL;
        }
        else
        {
            fd = open("/dev/mtdblock9", O_RDWR);
            if(fd < 0)
            {
                SAVE_SYS_LOG_MSG_FWUPDATE("[ramdisk]UpdateRootfs NG - can not open rootfs");
                free(ptr);
                return FAIL;
            }

            SAVE_SYS_LOG_MSG_FWUPDATE("[ramdisk]Writing image to mtdblock9...");
            wrd = write(fd, ptr, file_length);

            close(fd);
            sprintf(buf_log_fwupdate, "[ramdisk]mtdblock9 - written length: %d bytes\n", wrd);
            SAVE_SYS_LOG_MSG_FWUPDATE(buf_log_fwupdate);

            if(wrd != file_length)
            {
                free(ptr);
                return FAIL;
            }
            else
            {
                //result = PASS;
                SAVE_SYS_LOG_MSG_FWUPDATE("[ramdisk]deleting Storage-root files.\n");
                system("cd /Storage;rm -rf root/*");

                #if (FIRMWARE_VERSION_COMPILE_SETTING_RELEASE_MODE == DISABLE)
                {
                    SAVE_SYS_LOG_MSG_FWUPDATE("[ramdisk]sync...");
                    system("sync");
                }
                #endif
            }
        }
#endif        
    }
    return result;
}


/*===========================================================================
FUNCTION: Update_user_config
DESCRIPTION:
PRE-CONDITION:
INPUT:
OUTPUT:
GLOBAL VARIABLES:
=============================================================================*/
unsigned char Update_user_config()
{
    SAVE_SYS_LOG_MSG_FWUPDATE("-------- [user_config]start --------");

    unsigned char result = PASS;
    long int MaxLen = 48*1024*1024;
    unsigned long file_length = 0;
    unsigned char *ptr = malloc(MaxLen);

    if(ptr == NULL)
    {
        return FAIL;
    }

    memset(ptr, 0, MaxLen);

    file_length = fsize("/root/FactoryConfig.bin");

    // Open flash target mtdblock
    int fd = 0;
    int wrd = 0;
    FILE *file;
    fd = open("/dev/mtdblock10", O_RDWR);
    file = fopen("/root/FactoryConfig.bin", "rb");
    if (fd < 0)
    {
        SAVE_SYS_LOG_MSG_FWUPDATE("[user_config]Can not open mtdblock10\n");
        free(ptr);
        return FAIL;
    }
    else if (file == NULL)
    {
        SAVE_SYS_LOG_MSG_FWUPDATE("[user_config]Can not open the downloaded image file\n");
        free(ptr);
        return FAIL;
    }
    else
    {
        //[VITAL] Closing unneccesary task to supoort rapid CAN data receving..
        SAVE_SYS_LOG_MSG_FWUPDATE("[user_config] closing SeccComm...");
        system("killall SeccComm");

        // Write image to flash
        SAVE_SYS_LOG_MSG_FWUPDATE("[user_config]Writing image to mtdblock10...");
      //  fread(ptr, sizeof(unsigned char), file_length, file);
        //wrd = write(fd, ptr, file_length);
        close(fd);
        fclose(file);
        system("/bin/flash_erase /dev/mtd10 0 12");
	system("/usr/sbin/nandwrite -p /dev/mtd10 /root/FactoryConfig.bin");

	system("/bin/flash_erase /dev/mtd11 0 12");
	system("/usr/sbin/nandwrite -p /dev/mtd11 /root/FactoryConfig.bin");
									
        sprintf(buf_log_fwupdate,
                "[user_config]mtdblock10 - written length: %d bytes\n",
                wrd);
        SAVE_SYS_LOG_MSG_FWUPDATE(buf_log_fwupdate);
#if 0      
        if(wrd != file_length)
        {
            free(ptr);
            return FAIL;
        }
        else
        {
            // Open flash target mtdblock
            fd = open("/dev/mtdblock11", O_RDWR);
            if (fd < 0)
            {
                SAVE_SYS_LOG_MSG_FWUPDATE("[user_config]Can not open mtdblock11");
                free(ptr);
                return FAIL;
            }
            else
            {
                // Write image to flash
                SAVE_SYS_LOG_MSG_FWUPDATE("[user_config]Writing image to mtdblock11...");
                wrd = write(fd, ptr, file_length);
                close(fd);
                sprintf(buf_log_fwupdate,
                        "[user_config]mtdblock11 - written length: %d bytes\n",
                        wrd);
                SAVE_SYS_LOG_MSG_FWUPDATE(buf_log_fwupdate);
                if(wrd != file_length)
                {
                    free(ptr);
                    return FAIL;
                }
            }
        }
#endif        
    }
    return result;
}

/*===========================================================================
FUNCTION: Update_root_app
DESCRIPTION:
PRE-CONDITION:
INPUT:
OUTPUT:
GLOBAL VARIABLES:
=============================================================================*/
unsigned char Update_root_app()
{
    SAVE_SYS_LOG_MSG_FWUPDATE("-------- [root_app]start --------");

    unsigned char result = 0;

    //STEP 1: Unzip tmp (.tar.gz) (also checking the file completion)
    system("tar -C /Storage/ -zxvf /Storage/tmp");   //.tar.gz
    //[zip command] tar -zcvf tmp image/

    //STEP 2: Checking File Existance (to-be enhanced)
    FILE *file;
    file = fopen("/root/ramdisk_app_type_5.tar.gz", "rb");
    if (file == NULL)
    {
        result = 0;  //Failed on update
        SAVE_SYS_LOG_MSG_FWUPDATE("[root_app]checking downloaded file: FAIL");
        system("rm -rf /Storage/image/");
        return 0;
    }
    else
    {
        fclose(file);
        SAVE_SYS_LOG_MSG_FWUPDATE("[root_app]checking downloaded file: OK");
        SAVE_SYS_LOG_MSG_FWUPDATE("[root_app]updtating...");
        system("cp -rfv /Storage/image/* /Storage/root/");
        system("rm -rf /Storage/image/");

        #if (FIRMWARE_VERSION_COMPILE_SETTING_RELEASE_MODE == DISABLE)
        {
            SAVE_SYS_LOG_MSG_FWUPDATE("[root_app]sync...");
            system("sync");
        }
        #endif

        SAVE_SYS_LOG_MSG_FWUPDATE("[root_app]update: done");
        result = 1; //Successful on update
    }
    return result;
}


/*===========================================================================
FUNCTION: Update_ALL
DESCRIPTION:
PRE-CONDITION:
INPUT:
OUTPUT:
GLOBAL VARIABLES:
=============================================================================*/
int Update_ALL()
{
    char result;

    result = FAIL;

    //[VITAL] Closing unneccesary task to supoort rapid CAN data receving..
    SAVE_SYS_LOG_MSG_FWUPDATE("[Update_ALL] closing SeccComm...");
    system("killall SeccComm");

    if (Update_MLO() == FAIL)
    {
         SAVE_SYS_LOG_MSG_FWUPDATE("[Update]MLO: fail");
    }
    else if (Update_uboot() == FAIL)
    {
         SAVE_SYS_LOG_MSG_FWUPDATE("[Update]MLO: fail");
    }
    else if (Update_DTB() == FAIL)
    {
         SAVE_SYS_LOG_MSG_FWUPDATE("[Update]DTB: fail");
    }
    else if (Update_zImage() == FAIL)
    {
         SAVE_SYS_LOG_MSG_FWUPDATE("[Update]zImage: fail");
    }
    else if (Update_ramdisk() == FAIL)
    {
         SAVE_SYS_LOG_MSG_FWUPDATE("[Update]ramdisk: fail");
    }
    /*
    else if (Update_user_config() == FAIL)
    {
         SAVE_SYS_LOG_MSG_FWUPDATE("[Update]configuration: fail");
    }
    */
    //else if (Update_root_app() == FAIL)
    //{
    //}
    else
    {
        result = PASS;
    }

    SAVE_SYS_LOG_MSG_FWUPDATE("-------------[FW Update Summary]-----------");
    if (result == FAIL)
    {
        SAVE_SYS_LOG_MSG_FWUPDATE("[ALL]Fail");
        CSUCOMMDC_TASK_FLAG.FW_Update_result = FAIL;
    }
    else if (result == PASS)
    {
        SAVE_SYS_LOG_MSG_FWUPDATE("[ALL]Pass");
        CSUCOMMDC_TASK_FLAG.FW_Update_result = PASS;
    }
    else
    {
        CSUCOMMDC_TASK_FLAG.FW_Update_result = FAIL;
        sprintf(buf_log_fwupdate, "[ALL]unexpected response content (0x%X)", result);
        SAVE_SYS_LOG_MSG_FWUPDATE(buf_log_fwupdate);
    }
    CSUCOMMDC_TASK_FLAG.FW_Update_Done = TRUE;    //sending CAN Res msg to CSU and reboot
    return result;
}

/*===========================================================================
FUNCTION: FWImage_HeaderParser
DESCRIPTION:
PRE-CONDITION:
INPUT:
OUTPUT:
GLOBAL VARIABLES:
=============================================================================*/
int FWImage_HeaderParser(struct FwImageDataType *image)
{
    SAVE_SYS_LOG_MSG_FWUPDATE("[HeaderParser]starting...");

    //Step 0: Initialize
    unsigned char result = 0;
    long int MaxLen = 48*1024*1024;
    unsigned char *ptr = malloc(MaxLen);
    if(ptr == NULL)
    {
        SAVE_SYS_LOG_MSG_FWUPDATE("[HeaderParser]malloc: fail");
        free(ptr);
        return FAIL;
    }
    else
    {
        SAVE_SYS_LOG_MSG_FWUPDATE("[HeaderParser]malloc: OK");
    }

    memset(ptr, 0, MaxLen);

    FILE *file;
    file = fopen(image->path, "rb");
    if (file == NULL)
    {
        SAVE_SYS_LOG_MSG_FWUPDATE("[HeaderParser]fopen: fail");
        free(ptr);
        return FAIL;
    }
    else
    {
        SAVE_SYS_LOG_MSG_FWUPDATE("[HeaderParser]fopen: OK");
    }
    fread(ptr, sizeof(unsigned char), image->size, file);
    fclose(file);

    //Step 1: Check FW Image Type
    image->type = (((unsigned int)ptr[16])<<24 | ((unsigned int)ptr[17])<<16 | ((unsigned int)ptr[18])<<8 | ((unsigned int)ptr[19]));

    if (image->type != FW_IMAGE_TYPE_CCS_DTB &&
        image->type != FW_IMAGE_TYPE_CCS_UBOOT &&
        image->type != FW_IMAGE_TYPE_CCS_ZIMAGE &&
        image->type != FW_IMAGE_TYPE_CCS_RAMDISK)
    {
        sprintf(buf_log_fwupdate,
                "[HeaderParser]incorrect type (0x%X)",
                image->type);
        SAVE_SYS_LOG_MSG_FWUPDATE(buf_log_fwupdate);
        free(ptr);
        return FAIL;
    }

    //Step 2: Check FW Image Length
    unsigned long size_header = 0;
    size_header = (((unsigned int)ptr[20])<<24 | ((unsigned int)ptr[21])<<16 | ((unsigned int)ptr[22])<<8 | ((unsigned int)ptr[23]));
    if(image->size != (size_header + 48))
    {
        sprintf(buf_log_fwupdate,
                "[HeaderParser]image length is not matched (header = %d, calculated = %d, bytes)",
                size_header,
                image->size);
        SAVE_SYS_LOG_MSG_FWUPDATE(buf_log_fwupdate);
        free(ptr);
        return FAIL;
    }

    //Step 3: Check CRC
    unsigned int crc_cal = 0;
    image->crc = ((unsigned int)ptr[34])<<24 | ((unsigned int)ptr[35])<<16 | ((unsigned int)ptr[36])<<8 | ((unsigned int)ptr[37]);
    crc_cal = crc32(ptr, image->size);
    if(image->crc != crc_cal)
    {
        sprintf(buf_log_fwupdate,
                "[HeaderParser]CRC error (header = 0x%X, calculated = 0x%X)",
                image->crc,
                crc_cal);
        SAVE_SYS_LOG_MSG_FWUPDATE(buf_log_fwupdate);
        free(ptr);
        return FAIL;
    }
    else
    {
        SAVE_SYS_LOG_MSG_FWUPDATE("[HeaderParser]CRC: OK");
    }
    free(ptr);
    return PASS;
}

/*===========================================================================
FUNCTION: FWImage_Normalize
DESCRIPTION:
PRE-CONDITION:
INPUT:
OUTPUT:
GLOBAL VARIABLES:
=============================================================================*/
int FWImage_Normalize(struct FwImageDataType *image)
{
    //Step 0: Initialize
    unsigned char result = 0;
    long int MaxLen = 48*1024*1024;
    unsigned char *ptr = malloc(MaxLen);
    if(ptr == NULL)
    {
        SAVE_SYS_LOG_MSG_FWUPDATE("[Normalize] malloc fail");
        free(ptr);
        return FAIL;
    }

    memset(ptr, 0, MaxLen);

    FILE *file;
    file = fopen(image->path, "rb");
    if (file == NULL)
    {
        SAVE_SYS_LOG_MSG_FWUPDATE("[Normalize] fopen fail");
        free(ptr);
        return FAIL;
    }
    fread(ptr, sizeof(unsigned char), image->size, file);
    fclose(file);

    //Step 1: Delete the original image
    char buf[64];
    memset(buf, 0, 64);
    sprintf(buf, "rm -rf %s", image->path);
    system(buf);

    //Step 2: Remove header and create a renamed image accoring to its firmware type
    struct FwImageDataType image_new;
    switch (image->type)
    {
        case FW_IMAGE_TYPE_CCS_UBOOT:       //0x10000007
        {
            sprintf(image_new.path, "/root/u-boot.img");
            break;
        }
        case FW_IMAGE_TYPE_CCS_DTB:         //0x10000008
        {
            sprintf(image_new.path, "/root/am335x-evm.dtb");
            break;
        }
        case FW_IMAGE_TYPE_CCS_ZIMAGE:      //0x10000009
        {
            sprintf(image_new.path, "/root/zImage");
            break;
        }
        case FW_IMAGE_TYPE_CCS_RAMDISK:     //0x1000000A
        {
            sprintf(image_new.path, "/root/ramdisk.gz");
            break;
        }
        default:
        {
            break;
        }
    }

    memset(buf, 0, 64);
    sprintf(buf, "rm -rf %s", image_new.path);
    system(buf);

    SAVE_SYS_LOG_MSG_FWUPDATE("[Normalize]creating new image: ongoing...");
    image_new.size = image->size - 48;
    file = fopen(image_new.path, "wb+");
    fwrite(&ptr[48], image_new.size, 1, file); //48: header size
    fclose(file);
    free(ptr);
    SAVE_SYS_LOG_MSG_FWUPDATE("[Normalize]creating new image: done");
    return PASS;
}

/*===========================================================================
FUNCTION: FWImage_Init
DESCRIPTION:
PRE-CONDITION:
INPUT:
    1. image file name: "ccs.image"
OUTPUT:
    1. type: FW image type
       - 0:          file is not exist or error occours
       - 0x10000007: FW_IMAGE_TYPE_CCS_UBOOT
       - 0x10000008: FW_IMAGE_TYPE_CCS_DTB
       - 0x10000009: FW_IMAGE_TYPE_CCS_ZIMAGE
       - 0x1000000A: FW_IMAGE_TYPE_CCS_RAMDISK
GLOBAL VARIABLES:
=============================================================================*/
unsigned int FWImage_Init()
{
    struct FwImageDataType image;
    sprintf(image.path, "/root/ccs.image");

    //STEP 1: Check if image exist?
    image.size = fsize(image.path);
    if (image.size == 0)
    {
        SAVE_SYS_LOG_MSG_FWUPDATE("[Init][Error]image does not exist.");
        return FAIL;    //FAIL: 0
    }

    //STEP 2: Check FW image type = ?
    if (FWImage_HeaderParser(&image) != PASS)
    {
        return FAIL;    //FAIL: 0
    }

    //STEP 4: Remove header and rename the image file accoring to its firmware type
    if (FWImage_Normalize(&image) != PASS)
    {
        return FAIL;    //FAIL: 0
    }

    //Step 5: return result
    return image.type;
}

/*===========================================================================
FUNCTION: Update_viaWebService
DESCRIPTION:
PRE-CONDITION:
INPUT:
OUTPUT:
GLOBAL VARIABLES:
=============================================================================*/
int Update_viaWebService()
{
    SAVE_SYS_LOG_MSG_FWUPDATE("-------- [viaWebService]start --------");

    unsigned char result = FAIL;
    unsigned int type = 0;
    type = FWImage_Init();

    switch (type)
    {
        case FW_IMAGE_TYPE_CCS_UBOOT:
        {
            result = Update_uboot();
            break;
        }
        case FW_IMAGE_TYPE_CCS_DTB:
        {
            result = Update_DTB();
            break;
        }
        case FW_IMAGE_TYPE_CCS_ZIMAGE:
        {
            result = Update_zImage();
            break;
        }
        case FW_IMAGE_TYPE_CCS_RAMDISK:
        {
            result = Update_ramdisk();
            break;
        }
        default:
        {
            result = FAIL;
            sprintf(buf_log_fwupdate,
                    "[Error]undefined FW image type or file not exist.(0x%X)",
                    type
                    );
            SAVE_SYS_LOG_MSG_FWUPDATE(buf_log_fwupdate);
            break;
        }
    }

    if (result == PASS)
    {
        CSUCOMMDC_TASK_FLAG.FW_Update_result = PASS;
    }
    else
    {
        CSUCOMMDC_TASK_FLAG.FW_Update_result = FAIL;
    }
    CSUCOMMDC_TASK_FLAG.FW_Update_Done = TRUE; //sending CAN Res msg to CSU and reboot
}

/*===========================================================================
FUNCTION: main
DESCRIPTION:
PRE-CONDITION:
INPUT:
OUTPUT:
GLOBAL VARIABLES:
=============================================================================*/
int main(int argc, char *argv[])
{
    // ======== [STEP 1/5] Initialize Share Memory ========
    if(ShareMemory_Init() == 0)
    {

        SAVE_SYS_LOG_MSG_FWUPDATE("[main]ShareMemory_Init NG");


        if(ShmStatusCodeData != NULL)
        {
            ShmStatusCodeData->AlarmCode.AlarmEvents.bits.FailToCreateShareMemory = 1;
        }
        sleep(5);
        return 0;
    }

    //Arguments Parsing for Commands
    if (argv[1][0] == '-' )
    {
        switch (argv[1][1])
        {
            case 'h':   //help
            {
                sprintf(buf_log_fwupdate,
                        " ================= [HELP] CCS FW Update ==============");
                SAVE_SYS_LOG_MSG_FWUPDATE(buf_log_fwupdate);

                sprintf(buf_log_fwupdate,
                        " -h \t :help");
                SAVE_SYS_LOG_MSG_FWUPDATE(buf_log_fwupdate);

                sprintf(buf_log_fwupdate,
                        " -w \t :image update from WebService (with header, named as ccs.image)");
                SAVE_SYS_LOG_MSG_FWUPDATE(buf_log_fwupdate);

                sprintf(buf_log_fwupdate,
                        " -a \t :full images update \t(no header, named as original names)");
                SAVE_SYS_LOG_MSG_FWUPDATE(buf_log_fwupdate);

                sprintf(buf_log_fwupdate,
                        " -m \t :MLO update \t\t(no header, named as MLO)");
                SAVE_SYS_LOG_MSG_FWUPDATE(buf_log_fwupdate);

                sprintf(buf_log_fwupdate,
                        " -u \t :uboot update \t\t(no header, named as u-boot.img)");
                SAVE_SYS_LOG_MSG_FWUPDATE(buf_log_fwupdate);

                sprintf(buf_log_fwupdate,
                        " -d \t :dtb update \t\t(no header, named as am335x-evm.dtb)");
                SAVE_SYS_LOG_MSG_FWUPDATE(buf_log_fwupdate);

                sprintf(buf_log_fwupdate,
                        " -z \t :zImage update \t(no header, named as zImage)");
                SAVE_SYS_LOG_MSG_FWUPDATE(buf_log_fwupdate);

                sprintf(buf_log_fwupdate,
                        " -r \t :ramdisk update \t(no header, named as ramdisk.gz)");
                SAVE_SYS_LOG_MSG_FWUPDATE(buf_log_fwupdate);

                break;
            }
            case 'w':   //image update from WebService
            {
                Update_viaWebService();
                break;
            }
            case 'a':   //all image update
            {
                Update_ALL();
                break;
            }
            case 'm':   //MLO update
            {
                Update_MLO();
                break;
            }
            case 'u':   //uboot update
            {
                Update_uboot();
                break;
            }
            case 'd':   //dtb update
            {
                Update_DTB();
                break;
            }
            case 'z':   //zImage update
            {
                Update_zImage();
                break;
            }
            case 'r':   //ramdisk update
            {
                Update_ramdisk();
                break;
            }
            default:
            {
                sprintf(buf_log_fwupdate, "Unknown option -%c\n\n", argv[1][1]);
                SAVE_SYS_LOG_MSG_FWUPDATE(buf_log_fwupdate);
                break;
            }
        }
    }
}