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

                             initiated by Joseph
                              (since 2020/05/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 <linux/can.h>
#include <linux/can/raw.h>
#include <sys/time.h>
#include <sys/timeb.h>
#include <math.h>//for pow
#include <net/if.h>
#include <unistd.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <linux/if_packet.h>
#include <netinet/if_ether.h>
#include <netinet/ether.h>
#include <errno.h>

#include "define.h"
#include "CsuComm.h"
#include "SeccComm.h"
#include "PCBATester.h"

#if SAVE_SYS_LOG_MSG_PCBATESTER_SWITCH == ENABLE
unsigned char buf_log_pcbatstr[SIZE_OF_LOG_BUFFER];
#endif

unsigned char QcaMac[6];
unsigned char CsuMac[6] = {0x60, 0x39, 0x1F, 0x01, 0x02, 0x03};

int RawSock = 0;
unsigned char *V2gtpMsgRxBuf, *V2gtpMsgTxBuf;
struct sockaddr_ll DestSocketAddress;
struct ifreq Req;

struct timeb SeqStartTime, SeqEndTime;

/*===========================================================================
FUNCTION: StoreLogMsg
DESCRIPTION:
PRE-CONDITION:
INPUT:
OUTPUT:
GLOBAL VARIABLES:
=============================================================================*/
#if SAVE_SYS_LOG_MSG_PCBATESTER_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][PCBATester]%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,
            DataString,
            tm->tm_year + 1900,
            tm->tm_mon + 1);
    system(Buf);

    DEBUG_PRINTF_PCBATESTER_SYSTEM_LOG("[%02d:%02d:%02d.%06d][PCBATester]%s \n",
            tm->tm_hour,
            tm->tm_min,
            tm->tm_sec,
            tv.tv_usec,
            DataString);

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

/*===========================================================================
FUNCTION: DiffTimeb
DESCRIPTION:
    Caculating the time difference in ms precision (milli-second).
PRE-CONDITION:
INPUT:
OUTPUT:
GLOBAL VARIABLES:
=============================================================================*/
double DiffTimeb(struct timeb ST, struct timeb ET)
{
    //return milli-second
    double StartTime, EndTime;
    double t_diff;

    StartTime = ((double)ST.time)*1000 + (double)ST.millitm;
    EndTime = ((double)ET.time)*1000 + (double)ET.millitm;
    t_diff = EndTime - StartTime;

    //printf("%.02lf - %.02lf = %.02lf\n", EndTime, StartTime, t_diff);

    if (t_diff < 0)
    {
        #if 0
        if (t_diff < -1000)   //1000ms
        {
            sprintf(buf_log_pcbatstr,
                    "[Warning]StartTime(%.02lf) > EndTime(%.02lf), d(%.02lf)",
                    StartTime,
                    EndTime,
                    t_diff);
            SAVE_SYS_LOG_MSG_PCBATESTER(buf_log_pcbatstr);
        }
        #endif

        return -1;
    }
    return t_diff;
}

/*===========================================================================
FUNCTION: GetQca7kMac
DESCRIPTION:
PRE-CONDITION:
INPUT:
OUTPUT:
GLOBAL VARIABLES:
=============================================================================*/
int GetQca7kMac()
{
    int i = 0;
    struct QcaVendorMmeHeader SendPacket;

    memset(&SendPacket, 0, sizeof(struct QcaVendorMmeHeader));
    memset(SendPacket.ODA, 0xFF, 6);    //broadcast
    memcpy(SendPacket.OSA, CsuMac, 6);
    SendPacket.MTYPE = htons(EtherType_HomePlug);
    SendPacket.MMV = 0x00;
    SendPacket.MMTYPE = MMTYPE_VENDOR_VS_NW_INFO;
    SendPacket.OUI[0] = 0x00;
    SendPacket.OUI[1] = 0xB0;
    SendPacket.OUI[2] = 0x52;

    i = sendto(RawSock, &SendPacket, 20, 0, (struct sockaddr*)&DestSocketAddress, sizeof(struct sockaddr_ll));

    DEBUG_PRINTF_EVCOMM_DETAIL("GetQca7kMac: send size =%d\n", i);
    SAVE_SYS_LOG_MSG_PCBATESTER("[QCA7K][Tx][VENDOR_VS_NW_INFO]Req for QCA7K MacAddr");
}

unsigned int isKernelSupportNAT()
{
	unsigned int result = 0;
	unsigned int version = 0;
	FILE *fp;
	char cmd[256];
	char buf[512];

	// Get IP address & net mask
	strcpy(cmd, "uname -v");
	fp = popen(cmd, "r");
	if(fp != NULL)
	{
		if(fgets(buf, sizeof(buf), fp) != NULL)
		{
			sscanf(buf, "#%d", &version);
		//	DEBUG_INFO("Kernel version: %d\n", version);

			if(version >= 30)
				result = 1;
		}
	}
	pclose(fp);

	return result;
}

/*===========================================================================
FUNCTION: CANBus_Init
DESCRIPTION:
PRE-CONDITION:
INPUT:
OUTPUT:
GLOBAL VARIABLES:
=============================================================================*/
int CANBus_Init()
{
    int s0, nbytes;
    struct timeval tv;
    struct ifreq ifr0;
    struct sockaddr_can addr0;

    system("/sbin/ip link set can0 type can bitrate 500000 restart-ms 100");
    system("/sbin/ip link set can0 up");

    s0 = socket(PF_CAN, SOCK_RAW, CAN_RAW);
    if (s0 < 0) //added by Joseph (not full implemented)
    {
        SAVE_SYS_LOG_MSG_PCBATESTER("[ERROR] Fail on initializing CAN Bus socket");
        return -1;
    }

    tv.tv_sec = 0;
    tv.tv_usec = 10000;

    if(setsockopt(s0, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(struct timeval)) < 0)
    {
        SAVE_SYS_LOG_MSG_PCBATESTER("CANBus_Init:Set SO_RCVTIMEO NG");
    }

    nbytes = 40960;

    if(setsockopt(s0, SOL_SOCKET,  SO_RCVBUF, &nbytes, sizeof(int)) < 0)
    {
        SAVE_SYS_LOG_MSG_PCBATESTER("CANBus_Init:Set SO_RCVBUF NG");
    }

    nbytes = 40960;

    if(setsockopt(s0, SOL_SOCKET, SO_SNDBUF, &nbytes, sizeof(int)) < 0)
    {
        SAVE_SYS_LOG_MSG_PCBATESTER("CANBus_Init:Set SO_SNDBUF NG");
    }

    strcpy(ifr0.ifr_name, "can0" );
    ioctl(s0, SIOCGIFINDEX, &ifr0); /* ifr.ifr_ifindex gets filled with that device's index */
    addr0.can_family = AF_CAN;
    addr0.can_ifindex = ifr0.ifr_ifindex;
    bind(s0, (struct sockaddr *)&addr0, sizeof(addr0));
    return s0;
}

/*===========================================================================
FUNCTION: CAN_Tx_MSG
DESCRIPTION:
PRE-CONDITION:
INPUT:
OUTPUT:
GLOBAL VARIABLES:
=============================================================================*/
int CAN_Tx_MSG(int Fd, unsigned int MsgId, unsigned char SlaveAddress, unsigned char DataLength, unsigned char *SendData)
{
    struct can_frame frame;
    unsigned int tmp = 0;
    int nbytes = 0;
    int i = 0;

    //Protection: To avoid unexpected length for CAN bus payload.
    if (DataLength > 8)
    {
        DataLength = 8;
    }

    memset(&frame, 0, sizeof(struct can_frame));
    frame.can_id = 0x80000000 | CAN_SEND_DIRECTION | MsgId | SlaveAddress;  //0x80000000: extension ID format
    frame.can_dlc = DataLength;
    memcpy(frame.data, SendData, DataLength);
    nbytes = write(Fd, &frame, sizeof(struct can_frame));

    #if 0
    DEBUG_PRINTF_PCBATESTER_DETAIL("[CsuComm][CAN_Tx_MSG] <%X> ", frame.can_id);
    for (i = 0; i < frame.can_dlc; i++)
    {
        DEBUG_PRINTF_PCBATESTER_DETAIL("%02X ", frame.data[i]);
    }
    DEBUG_PRINTF_PCBATESTER_DETAIL("(%d Bytes)\n", frame.can_dlc);
    #endif

    return nbytes;
}

/*===========================================================================
FUNCTION: PRINT_CAN_FRAME
DESCRIPTION:
PRE-CONDITION:
INPUT:
OUTPUT:
GLOBAL VARIABLES:
=============================================================================*/
void PRINT_CAN_FRAME(struct can_frame *frame)
{
    if (frame->can_dlc != 0)
    {
        int i = 0;
        DEBUG_PRINTF_PCBATESTER_DETAIL("[CsuComm] Got CAN Message: \n\
                \t - Length: %d (Bytes, DEC)\n\
                \t - ID: %08X\n\
                \t - Payload: ",
                frame->can_dlc,
                frame->can_id
                );
        for (i = 0; i< frame->can_dlc; i++)
        {
            DEBUG_PRINTF_PCBATESTER_DETAIL("%02X ", frame->data[i]);
        }
        DEBUG_PRINTF_PCBATESTER_DETAIL("\n");
    }
}


/*===========================================================================
FUNCTION: System_Init
DESCRIPTION:
PRE-CONDITION:
INPUT:
OUTPUT:
GLOBAL VARIABLES:
=============================================================================*/
int System_Init()
{
    system("../root/stop.sh");
}

/*===========================================================================
FUNCTION: ReadAdcVolt
DESCRIPTION:
    1. fork1

    #define ADC_CHANNEL_AIN0_NTC1   0
    #define ADC_CHANNEL_AIN1_NTC2   1
    #define ADC_CHANNEL_AIN2_PP     2
    #define ADC_CHANNEL_AIN3_CP     3
PRE-CONDITION:
INPUT:
OUTPUT:
GLOBAL VARIABLES:
=============================================================================*/
float ReadAdcVolt(unsigned char AdcChannel)
{
    //AIN0=CCS GUN Temp 1
    //AIN1=CCS GUN Temp 2
    //AIN2=CCS_Proximity/2
    //AIN3=pilot voltage

    int fd;
    int AvgTimes = 0;
    unsigned char SampleBuf[4];
    float TmpVolt = 0;
    float AvgSample = 0;
    float result = 0;

    switch (AdcChannel)
    {
        case ADC_CHANNEL_AIN0_NTC1: //0
        {
            fd = open("/sys/bus/iio/devices/iio\:device0/in_voltage0_raw", O_RDONLY);
            break;
        }
        case ADC_CHANNEL_AIN1_NTC2: //1
        {
            fd = open("/sys/bus/iio/devices/iio\:device0/in_voltage1_raw", O_RDONLY);
            break;
        }
        case ADC_CHANNEL_AIN2_PP:   //2
        {
            fd = open("/sys/bus/iio/devices/iio\:device0/in_voltage2_raw", O_RDONLY);
            break;
        }
        case ADC_CHANNEL_AIN3_CP:   //3
        {
            fd = open("/sys/bus/iio/devices/iio\:device0/in_voltage3_raw", O_RDONLY);
            break;
        }
        default:
        {
            sprintf(buf_log_pcbatstr, "[ReadAdcVolt]unexpected AdcChannel(%d)", AdcChannel);
            SAVE_SYS_LOG_MSG_PCBATESTER(buf_log_pcbatstr);
            return -1;
        }
    }

    if(fd > 0)
    {
        for(AvgTimes = 0; AvgTimes < 5; AvgTimes++) //get 5 samples
        {
            read(fd, SampleBuf, 4);
            TmpVolt = (float)atoi(SampleBuf);
            lseek(fd, 0, SEEK_SET);
            AvgSample += TmpVolt;
        }
        close(fd);
        AvgSample /= AvgTimes;

        switch (AdcChannel)
        {
            case ADC_CHANNEL_AIN0_NTC1: //0
            {
                result = (3.6 * AvgSample) / 4095;
                //sprintf(buf_log_pcbatstr, "[ReadAdcVolt]AIN0_NTC1 = %.02f", result);
                //SAVE_SYS_LOG_MSG_PCBATESTER(buf_log_pcbatstr);
                break;
            }
            case ADC_CHANNEL_AIN1_NTC2: //1
            {
                result = (3.6 * AvgSample) / 4095;
                //sprintf(buf_log_pcbatstr, "[ReadAdcVolt]AIN1_NTC2 = %.02f", result);
                //SAVE_SYS_LOG_MSG_PCBATESTER(buf_log_pcbatstr);
                break;
            }
            case ADC_CHANNEL_AIN2_PP:   //2
            {
                result = (3.6 * AvgSample) / 4095;
                //sprintf(buf_log_pcbatstr, "[ReadAdcVolt]AIN2_PP = %.02f", result);
                //SAVE_SYS_LOG_MSG_PCBATESTER(buf_log_pcbatstr);
                break;
            }
            case ADC_CHANNEL_AIN3_CP:   //3
            {
                result = (0.954-(1.8*AvgSample/4095))/0.06;
                //sprintf(buf_log_pcbatstr, "[ReadAdcVolt]AIN3_CP = %.02f", result);
                //SAVE_SYS_LOG_MSG_PCBATESTER(buf_log_pcbatstr);
                break;
            }
            default:
            {
                sprintf(buf_log_pcbatstr, "[ReadAdcVolt]unknown AdcChannel(%d)", AdcChannel);
                SAVE_SYS_LOG_MSG_PCBATESTER(buf_log_pcbatstr);
                result = -1;
                break;
            }
        }
        return result;
    }
    else
    {
        SAVE_SYS_LOG_MSG_PCBATESTER("[ReadAdcVolt]failed on open adc channel");
        return -1;
    }
}

/*===========================================================================
FUNCTION: PCBATstr_canbus
DESCRIPTION:
    1. Tx: AA 55
    2. Rx: 55 AA (expected)
PRE-CONDITION:
INPUT:
OUTPUT:
GLOBAL VARIABLES:
=============================================================================*/
int PCBATstr_canbus()
{
    int FD_CAN_Socket = 0;
    int result = PASS;
    int err_cnt = 0;
    SAVE_SYS_LOG_MSG_PCBATESTER("-------- [canbus] start --------");
    unsigned char can_tx_payload[8];
    struct can_frame frame;

    //Step 1: CAN Bus
    FD_CAN_Socket = CANBus_Init();
    if (FD_CAN_Socket < 0)
    {
         return FAIL;
    }

    while(1)
    {
        //Step 2: Tx CAN Message
        memset(can_tx_payload, 0, sizeof(can_tx_payload));
        can_tx_payload[0] = 0xAA;
        can_tx_payload[1] = 0x55;

        CAN_Tx_MSG(FD_CAN_Socket, CAN_CMD_PCBA_TEST, 0, 2, can_tx_payload);

        sleep(1);

        //Step 3: Rx CAN Message
        memset(&frame, 0, sizeof(struct can_frame));
        read(FD_CAN_Socket, &frame, sizeof(struct can_frame));

        //PRINT_CAN_FRAME(&frame);

        if( (frame.can_id == 0) ||          //all zero
            (frame.can_id & 0x08000000) ||  //to avoid incoreect DIRECTION bit
            ((frame.can_id & 0x0000FF00) != CAN_CMD_PCBA_TEST) ||  //the 1st command ID from Master should be 0x02
            (frame.can_dlc != 2))           //payload length should be only 5 bytes.
        {
            if (err_cnt++ >= 10)
            {
                SAVE_SYS_LOG_MSG_PCBATESTER("[PCBATstr_canbus]Rx err_cnt >= 5");
                result = FAIL;
                break;
            }
            continue;
        }

        if ((frame.data[0] == 0x55) && (frame.data[1] == 0xAA))
        {
            sprintf(buf_log_pcbatstr,
                    "[PCBATstr_canbus]rx: %02X %02X (PASS)",
                    frame.data[0],
                    frame.data[1]);
            SAVE_SYS_LOG_MSG_PCBATESTER(buf_log_pcbatstr);
            break;
        }
        else
        {
            sprintf(buf_log_pcbatstr,
                    "[PCBATstr_canbus]rx: %02X %02X (FAIL)",
                    frame.data[0],
                    frame.data[1]);
            SAVE_SYS_LOG_MSG_PCBATESTER(buf_log_pcbatstr);
            result = FAIL;
            break;
        }
    }
    SAVE_SYS_LOG_MSG_PCBATESTER("-------- [canbus] end --------");
    return result;
}

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

    SAVE_SYS_LOG_MSG_PCBATESTER("-------- [ethernet] --------");
    system("/sbin/ifconfig eth0 192.168.1.10 netmask 255.255.255.0 up");
    system("/sbin/route add default gw 192.168.1.1");
    //system("ping -I eth0 -c 3 192.168.1.1");
    sleep(1);

    return result;
}

/*===========================================================================
FUNCTION: PCBATstr_gpio_ntc
DESCRIPTION:
    1. AM_IO_1 <<==>> NTC1 & NTC2

PRE-CONDITION:
INPUT:
OUTPUT:
GLOBAL VARIABLES:
=============================================================================*/
int PCBATstr_gpio_ntc()
{
    int result = PASS;
    float ntc1 = 0;
    float ntc2 = 0;

    SAVE_SYS_LOG_MSG_PCBATESTER("-------- [ntc] --------");

    //Step 1: AM_IO_1
    system("echo 87 > /sys/class/gpio/export");
    system("echo \"out\" > /sys/class/gpio/gpio87/direction");

    SAVE_SYS_LOG_MSG_PCBATESTER("[PCBATstr_gpio_ntc]AM_IO_1(ON): read NTC1, NTC2");
    system("echo 1 > /sys/class/gpio/gpio87/value"); //[AM_IO_1](gpio2_25: output)
    usleep(100000);
    ntc1 = ReadAdcVolt(ADC_CHANNEL_AIN0_NTC1);
    ntc2 = ReadAdcVolt(ADC_CHANNEL_AIN1_NTC2);
    if (ntc1 > 3.0)
    {
        sprintf(buf_log_pcbatstr, "[PCBATstr_gpio_ntc]ntc1 = %.02fV (PASS, > 3.0)", ntc1);
        SAVE_SYS_LOG_MSG_PCBATESTER(buf_log_pcbatstr);
        if (ntc2 > 3.0)
        {
            sprintf(buf_log_pcbatstr, "[PCBATstr_gpio_ntc]ntc2 = %.02fV (PASS, > 3.0)", ntc2);
            SAVE_SYS_LOG_MSG_PCBATESTER(buf_log_pcbatstr);
        }
        else
        {
            sprintf(buf_log_pcbatstr, "[PCBATstr_gpio_ntc]ntc2 = %.02fV (FAIL, > 3.0)", ntc2);
            SAVE_SYS_LOG_MSG_PCBATESTER(buf_log_pcbatstr);
            result = FAIL;
        }
    }
    else
    {
        sprintf(buf_log_pcbatstr, "[PCBATstr_gpio_ntc]ntc1 = %.02fV (FAIL, > 3.0)", ntc1);
        SAVE_SYS_LOG_MSG_PCBATESTER(buf_log_pcbatstr);
        result = FAIL;
    }

    //=========================================================================
    SAVE_SYS_LOG_MSG_PCBATESTER("\n");
    SAVE_SYS_LOG_MSG_PCBATESTER("[PCBATstr_gpio_ntc]AM_IO_1(OFF): read NTC1, NTC2");
    system("echo 0 > /sys/class/gpio/gpio87/value"); //[AM_IO_1](gpio2_25: output)
    usleep(100000);
    ntc1 = ReadAdcVolt(ADC_CHANNEL_AIN0_NTC1);
    ntc2 = ReadAdcVolt(ADC_CHANNEL_AIN1_NTC2);
    if (ntc1 < 0.5)
    {
        sprintf(buf_log_pcbatstr, "[PCBATstr_gpio_ntc]ntc1 = %.02fV (PASS, < 0.5)", ntc1);
        SAVE_SYS_LOG_MSG_PCBATESTER(buf_log_pcbatstr);
        if (ntc2 < 0.5)
        {
            sprintf(buf_log_pcbatstr, "[PCBATstr_gpio_ntc]ntc2 = %.02fV (PASS, < 0.5)", ntc2);
            SAVE_SYS_LOG_MSG_PCBATESTER(buf_log_pcbatstr);
        }
        else
        {
            sprintf(buf_log_pcbatstr, "[PCBATstr_gpio_ntc]ntc2 = %.02fV (FAIL, < 0.5)", ntc2);
            SAVE_SYS_LOG_MSG_PCBATESTER(buf_log_pcbatstr);
            result = FAIL;
        }
    }
    else
    {
        sprintf(buf_log_pcbatstr, "[PCBATstr_gpio_ntc]ntc1 = %.02fV (FAIL, < 0.5)", ntc1);
        SAVE_SYS_LOG_MSG_PCBATESTER(buf_log_pcbatstr);
        result = FAIL;
    }

    return result;
}

/*===========================================================================
FUNCTION: PCBATstr_gpio_pp
DESCRIPTION:
    1. AM_IO_2 <<==>> PP
PRE-CONDITION:
INPUT:
OUTPUT:
GLOBAL VARIABLES:
=============================================================================*/
int PCBATstr_gpio_pp()
{
    int result = PASS;
    float pp = 0;

    SAVE_SYS_LOG_MSG_PCBATESTER("-------- [pp] --------");

    // initial gpio
    system("echo 89 > /sys/class/gpio/export");
    system("echo \"out\" > /sys/class/gpio/gpio89/direction");

    SAVE_SYS_LOG_MSG_PCBATESTER("[PCBATstr_gpio_ntc]PP(ON): read PP");
    system("echo 1 > /sys/class/gpio/gpio89/value"); //[AM_IO_2](gpio2_25: output)
    usleep(100000);
    pp = ReadAdcVolt(ADC_CHANNEL_AIN2_PP);
    if (pp > 2.0)
    {
        sprintf(buf_log_pcbatstr, "[PCBATstr_gpio_ntc]PP = %.02fV (PASS, > 2.0)", pp);
        SAVE_SYS_LOG_MSG_PCBATESTER(buf_log_pcbatstr);
    }
    else
    {
        sprintf(buf_log_pcbatstr, "[PCBATstr_gpio_ntc]PP = %.02fV (FAIL, > 2.0)", pp);
        SAVE_SYS_LOG_MSG_PCBATESTER(buf_log_pcbatstr);
        result = FAIL;
    }

    SAVE_SYS_LOG_MSG_PCBATESTER("[PCBATstr_gpio_ntc]PP(OFF): read PP...");
    system("echo 0 > /sys/class/gpio/gpio89/value"); //[AM_IO_2](gpio2_25: output)
    usleep(100000);
    pp = ReadAdcVolt(ADC_CHANNEL_AIN2_PP);
    if (pp < 0.5)
    {
        sprintf(buf_log_pcbatstr, "[PCBATstr_gpio_ntc]PP = %.02fV (PASS, < 0.5)", pp);
        SAVE_SYS_LOG_MSG_PCBATESTER(buf_log_pcbatstr);
    }
    else
    {
        sprintf(buf_log_pcbatstr, "[PCBATstr_gpio_ntc]PP = %.02fV (FAIL, < 0.5)", pp);
        SAVE_SYS_LOG_MSG_PCBATESTER(buf_log_pcbatstr);
        result = FAIL;
    }

    return result;
}

/*===========================================================================
FUNCTION: PCBATstr_cp
DESCRIPTION:
PRE-CONDITION:
INPUT:
OUTPUT:
GLOBAL VARIABLES:
=============================================================================*/
int PCBATstr_cp()
{
    int result = PASS;
    float cp = 0;

    SAVE_SYS_LOG_MSG_PCBATESTER("-------- [cp] --------");

    // initial pwm
    system("echo 0 > /sys/class/pwm/pwmchip0/export");
    system("echo 1000000 > /sys/class/pwm/pwmchip0/pwm0/period");// nano seconds =>1k Hz
    system("echo 1000000 > /sys/class/pwm/pwmchip0/pwm0/duty_cycle"); //default 100%
    system("echo 1 > /sys/class/pwm/pwmchip0/pwm0/enable");

    system("echo 89 > /sys/class/gpio/export");
    system("echo \"out\" > /sys/class/gpio/gpio89/direction");

    system("echo 86 > /sys/class/gpio/export");
    system("echo \"out\" > /sys/class/gpio/gpio86/direction");

    SAVE_SYS_LOG_MSG_PCBATESTER("[PCBATstr_gpio_ntc]CP(PWM:100%,SetStateE:OFF): read CP");
    system("echo 1000000 > /sys/class/pwm/pwmchip0/pwm0/duty_cycle");
    system("echo 0 > /sys/class/gpio/gpio86/value"); //state E
    usleep(100000);

    cp = ReadAdcVolt(ADC_CHANNEL_AIN3_CP);
    if (cp > 11.0)
    {
        sprintf(buf_log_pcbatstr, "[PCBATstr_gpio_ntc]CP = %.02fV (PASS, > 11.0)", cp);
        SAVE_SYS_LOG_MSG_PCBATESTER(buf_log_pcbatstr);
    }
    else
    {
        sprintf(buf_log_pcbatstr, "[PCBATstr_gpio_ntc]CP = %.02fV (FAIL, > 11.0)", cp);
        SAVE_SYS_LOG_MSG_PCBATESTER(buf_log_pcbatstr);
        result = FAIL;
    }

    SAVE_SYS_LOG_MSG_PCBATESTER("\n");
    SAVE_SYS_LOG_MSG_PCBATESTER("[PCBATstr_gpio_ntc]CP(PWM:100%,SetStateE:ON): read CP");
    system("echo 1000000 > /sys/class/pwm/pwmchip0/pwm0/duty_cycle");
    system("echo 1 > /sys/class/gpio/gpio86/value"); //state E
    usleep(100000);
    cp = ReadAdcVolt(ADC_CHANNEL_AIN3_CP);
    if (cp < 1.0)
    {
        sprintf(buf_log_pcbatstr, "[PCBATstr_gpio_ntc]CP = %.02fV (PASS, < 1.0V)", cp);
        SAVE_SYS_LOG_MSG_PCBATESTER(buf_log_pcbatstr);
    }
    else
    {
        sprintf(buf_log_pcbatstr, "[PCBATstr_gpio_ntc]CP = %.02fV (FAIL, < 1.0V)", cp);
        SAVE_SYS_LOG_MSG_PCBATESTER(buf_log_pcbatstr);
        result = FAIL;
    }

    return result;
}

/*===========================================================================
FUNCTION: PCBATstr_reboot
DESCRIPTION:
PRE-CONDITION:
INPUT:
OUTPUT:
GLOBAL VARIABLES:
=============================================================================*/
int PCBATstr_reboot()
{
    SAVE_SYS_LOG_MSG_PCBATESTER("-------- [reboot] --------");
    system("reboot -f");
    sleep(2);
    system("reboot -f");
    return PASS;
}

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

    SAVE_SYS_LOG_MSG_PCBATESTER("-------- [killtasks] --------");

    system("../root/stop.sh");

    return result;
}


/*===========================================================================
FUNCTION: PCBATstr_qca7k
DESCRIPTION:
PRE-CONDITION:
INPUT:
OUTPUT:
GLOBAL VARIABLES:
=============================================================================*/
int PCBATstr_qca7k()
{
    SAVE_SYS_LOG_MSG_PCBATESTER("-------- [qca7000] --------");

    if(isKernelSupportNAT() == 1)
		system("/sbin/insmod /lib/qcaspi_nat.ko");
	else
		system("/sbin/insmod /lib/qcaspi.ko");
   // system("/sbin/insmod /lib/qcaspi.ko");
    sleep(2);
    system("/sbin/ifconfig eth1 192.168.0.11 netmask 255.255.255.0 up");
    sleep(1);

    int state = IDLE;
    int result = PASS;
    int packet_size = 0;
    double t_diff = 0;

    //Step1: Init
    if(RawSock > 0)
    {
        close(RawSock);
    }
    RawSock = -1;

    //Init V2G TCP/IPv6 packets buffer
    V2gtpMsgRxBuf = (unsigned char *)malloc(V2GTP_MSG_RX_BUFFER_SIZE);
    memset(V2gtpMsgRxBuf, 0, V2GTP_MSG_RX_BUFFER_SIZE);

    V2gtpMsgTxBuf = (unsigned char *)malloc(V2GTP_MSG_TX_BUFFER_SIZE);
    memset(V2gtpMsgTxBuf, 0, V2GTP_MSG_TX_BUFFER_SIZE);

    while (1)
    {
        if(RawSock >= 0)
        {
            memset(V2gtpMsgRxBuf, 0, V2GTP_MSG_RX_BUFFER_SIZE);
            packet_size = recvfrom(RawSock, V2gtpMsgRxBuf, V2GTP_MSG_RX_BUFFER_SIZE, 0, NULL, NULL);
            if(packet_size > 0)
            {
                struct MmeHeader *MmePacket;
                MmePacket = (struct MmeHeader *)V2gtpMsgRxBuf;
                switch(MmePacket->MMTYPE)
                {
                    case MMTYPE_VENDOR_VS_NW_INFO_CNF:
                    {
                        memcpy(QcaMac, MmePacket->OSA, 6);

                        sprintf(buf_log_pcbatstr,
                                "[QCA7K][Rx][VENDOR_VS_NW_INFO_CNF]Got QCA7K MacAddr:%02X:%02X:%02X:%02X:%02X:%02X (comm:OK)",
                                 QcaMac[0],  QcaMac[1],  QcaMac[2],  QcaMac[3],  QcaMac[4],  QcaMac[5]);
                        SAVE_SYS_LOG_MSG_PCBATESTER(buf_log_pcbatstr);
                        return PASS;
                    }

                    default:
                    {
                        break;
                    }

                }
            }
        }

        switch(state)
        {
            case IDLE:
            {
                if(RawSock < 0)
                {
                    RawSock = socket(PF_PACKET, SOCK_RAW, htons(EtherType_HomePlug));

                    sprintf(buf_log_pcbatstr, "[RawSock]opened(%d)", RawSock);
                    SAVE_SYS_LOG_MSG_PCBATESTER(buf_log_pcbatstr);

                    if(RawSock == -1)
                    {
                        SAVE_SYS_LOG_MSG_PCBATESTER("SlacComm:Failed to create socket");
                        return FAIL;
                    }

                    if (setsockopt(RawSock, SOL_SOCKET, SO_BINDTODEVICE, QcaInterface, 4) < 0)
                    {
                        SAVE_SYS_LOG_MSG_PCBATESTER("SlacComm:Set SO_BINDTODEVICE NG");
                        return FAIL;
                    }

                    struct timeval tv;
                    tv.tv_sec = 0;
                    tv.tv_usec = 100000;  //100ms (Rx timeout)
                    if (setsockopt(RawSock, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(struct	timeval)) < 0)
                    {
                        SAVE_SYS_LOG_MSG_PCBATESTER("SlacComm:Set SO_RCVTIMEO NG");
                        return FAIL;
                    }

                    tv.tv_usec = 100000;  //100ms (Tx timeout)
                    if (setsockopt(RawSock, SOL_SOCKET, SO_SNDTIMEO, (char *)&tv, sizeof(struct	timeval)) < 0)
                    {
                        SAVE_SYS_LOG_MSG_PCBATESTER("SlacComm:Set SO_SNDTIMEO NG");
                        return FAIL;
                    }

                    memset(&Req, 0, sizeof(struct ifreq));
                    strcpy( (char*)Req.ifr_name, QcaInterface);

                    if (ioctl(RawSock, SIOCGIFINDEX, &Req) < 0)
                    {
                        SAVE_SYS_LOG_MSG_PCBATESTER("SlacComm: ioctl NG");
                        return FAIL;
                    }

                    memset(&DestSocketAddress, 0, sizeof(struct sockaddr_ll));
                    DestSocketAddress.sll_ifindex = Req.ifr_ifindex;
                    DestSocketAddress.sll_halen = ETH_ALEN;

                    SAVE_SYS_LOG_MSG_PCBATESTER("[QCA7K]connecting...");
                    //Get QCA7K MAC address
                    GetQca7kMac();
                    ftime(&SeqStartTime);
                    break;
                }
                else   //RawSock: opened
                {
                    ftime(&SeqEndTime);
                    t_diff = DiffTimeb(SeqStartTime, SeqEndTime);
                    if (t_diff > V2G_SECC_QCA7000_COMM_TIMEOUT)  //10 secs
                    {
                        sprintf(buf_log_pcbatstr,
                                "[QCA7K][Error]comm: fail (%.02lf/%dms)",
                                t_diff,
                                V2G_SECC_QCA7000_COMM_TIMEOUT);
                        SAVE_SYS_LOG_MSG_PCBATESTER(buf_log_pcbatstr);
                        return FAIL;
                    }
                    else if (t_diff > V2G_SECC_QCA7000_GET_MAC_ADDR_REQ_RETRY_PERIOD)   //3 secs
                    {
                        sprintf(buf_log_pcbatstr,
                                "[QCA7K]re-try connecting...(%.02lf/%dms)",
                                t_diff,
                                V2G_SECC_QCA7000_GET_MAC_ADDR_REQ_RETRY_PERIOD);
                        SAVE_SYS_LOG_MSG_PCBATESTER(buf_log_pcbatstr);

                        GetQca7kMac(); //re-send req
                        break;
                    }
                    else
                    {
                        //null
                    }
                }
                break;
            }

            default:
            {
                break;
            }
        }
    }
}

/*===========================================================================
FUNCTION: PCBATstr_sdcard
DESCRIPTION:
PRE-CONDITION:
INPUT:
OUTPUT:
GLOBAL VARIABLES:
=============================================================================*/
int PCBATstr_sdcard()
{
    int result = FAIL;
    SAVE_SYS_LOG_MSG_PCBATESTER("-------- [sdcard] --------");

    SAVE_SYS_LOG_MSG_PCBATESTER("[PCBATstr_sdcard]mount SDCard on mnt");
    system("mount /dev/mmcblk0p1 /mnt");

    SAVE_SYS_LOG_MSG_PCBATESTER("[PCBATstr_sdcard]delete file.txt");
    system("rm -rfv /mnt/file.txt");

    SAVE_SYS_LOG_MSG_PCBATESTER("[PCBATstr_sdcard]creat file.txt in SDCard");
    system("touch /mnt/file.txt");

    SAVE_SYS_LOG_MSG_PCBATESTER("[PCBATstr_sdcard]unmount SDCard from mnt");
    system("umount /mnt");
    sleep(1);

    SAVE_SYS_LOG_MSG_PCBATESTER("[PCBATstr_sdcard]mount SDCard on UsbFlash");
    system("mount /dev/mmcblk0p1 /UsbFlash");
    FILE *fp;
    fp = fopen("/UsbFlash/file.txt", "r");
    if (fp == NULL)
    {
        SAVE_SYS_LOG_MSG_PCBATESTER("[PCBATstr_sdcard]file.txt is not found");
    }
    else
    {
        SAVE_SYS_LOG_MSG_PCBATESTER("[PCBATstr_sdcard]file.txt is found");
        SAVE_SYS_LOG_MSG_PCBATESTER("[PCBATstr_sdcard]delete file.txt");
        system("rm -rfv /mnt/file.txt");
        result = PASS;
    }
    fclose(fp);

    SAVE_SYS_LOG_MSG_PCBATESTER("[PCBATstr_sdcard]unmount SDCard from UsbFlash");
    system("umount /UsbFlash");
    return result;
}


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

    SAVE_SYS_LOG_MSG_PCBATESTER("-------- [all] --------");

    //case 'k':   //Kill other tasks
    if (PCBATstr_killtasks() == PASS)
    {
        printf("[kill]PASS\n");
    }
    else
    {
        printf("[kill]FAIL\n");
        result = FAIL;
    }

    //case 'b':   //CAN Bus Test
    if (PCBATstr_canbus() == PASS)
    {
        printf("[can]PASS\n");
    }
    else
    {
        printf("[can]FAIL\n");
        result = FAIL;
    }


    //case 'e':   //Ethernet(eth1) Test
    if (PCBATstr_ethernet() == PASS)
    {
        printf("[eth]READY\n");
    }
    else
    {
        printf("[eth]FAIL\n");
        result = FAIL;
    }


    //case 'n':   //GPIO(AM_IO_1) and ADC(NTC1,NTC2) Test
    if (PCBATstr_gpio_ntc() == PASS)
    {
        printf("[ntc]PASS\n");
    }
    else
    {
        printf("[ntc]FAIL\n");
        result = FAIL;
    }


    //case 'p':   //GPIO(AM_IO_2) and ADC(PP) Test
    if (PCBATstr_gpio_pp() == PASS)
    {
        printf("[pp]PASS\n");
    }
    else
    {
        printf("[pp]FAIL\n");
        result = FAIL;
    }

    //case 'c':   //CP Test
    if (PCBATstr_cp() == PASS)
    {
        printf("[cp]PASS\n");
    }
    else
    {
        printf("[cp]FAIL\n");
        result = FAIL;
    }


    //case 'q':   //QCA7000 Test
    if (PCBATstr_qca7k() == PASS)
    {
        printf("[qca]PASS\n");
    }
    else
    {
        printf("[qca]FAIL\n");
        result = FAIL;
    }

    //case 's':   //SD Card Test
    if (PCBATstr_sdcard() == PASS)
    {
        printf("[sd]PASS\n");
    }
    else
    {
        printf("[sd]FAIL\n");
        result = FAIL;
    }

    return result;
}


/*===========================================================================
FUNCTION: main
DESCRIPTION:
PRE-CONDITION:
INPUT:
OUTPUT:
GLOBAL VARIABLES:
=============================================================================*/
int main(int argc, char *argv[])
{
    SAVE_SYS_LOG_MSG_PCBATESTER("PCBATester: on");
    SAVE_SYS_LOG_MSG_PCBATESTER("---------------------------------------------");
    SAVE_SYS_LOG_MSG_PCBATESTER("--                PCBATester               --");
    SAVE_SYS_LOG_MSG_PCBATESTER("---------------------------------------------\n");

    //Initialization
    //System_Init();

    //Arguments Parsing for Commands
    if (argv[1][0] == '-' )
    {
        switch (argv[1][1])
        {
            case 'h':   //help
            {
                printf(" ================= [HELP] PCBATester ==============\n");
                printf(" -h \t :help\n");
                printf(" -a \t :Auto Run All Test Cases\n");
                printf(" -b \t :CAN Bus Test\n");
                printf(" -c \t :CP Test\n");
                printf(" -e \t :Ethernet(eth1) Test\n");
                printf(" -k \t :Killall other tasks\n");
                printf(" -p \t :GPIO(AM_IO_2) and ADC(PP) Test\n");
                printf(" -n \t :GPIO(AM_IO_1) and ADC(NTC1,NTC2) Test\n");
                printf(" -q \t :QCA7000 Test\n");
                printf(" -r \t :Reboot\n");
                printf(" -s \t :SD Card Test\n");
                break;
            }
            case 'a':   //Auto Run All Test Cases
            {
                if (PCBATstr_all() == PASS)
                {
                    printf("[all]PASS\n");
                }
                else
                {
                    printf("[all]FAIL\n");
                }
                break;
            }
            case 'b':   //CAN Bus Test
            {
                if (PCBATstr_canbus() == PASS)
                {
                    printf("[can]PASS\n");
                }
                else
                {
                    printf("[can]FAIL\n");
                }
                break;
            }
            case 'e':   //Ethernet(eth1) Test
            {
                if (PCBATstr_ethernet() == PASS)
                {
                    printf("[eth]READY\n");
                }
                else
                {
                    printf("[eth]FAIL\n");
                }
                break;
            }
            case 'n':   //GPIO(AM_IO_1) and ADC(NTC1,NTC2) Test
            {
                if (PCBATstr_gpio_ntc() == PASS)
                {
                    printf("[ntc]PASS\n");
                }
                else
                {
                    printf("[ntc]FAIL\n");
                }
                break;
            }
            case 'p':   //GPIO(AM_IO_2) and ADC(PP) Test
            {
                if (PCBATstr_gpio_pp() == PASS)
                {
                    printf("[pp]PASS\n");
                }
                else
                {
                    printf("[pp]FAIL\n");
                }
                break;
            }
            case 'k':   //Kill other tasks
            {
                if (PCBATstr_killtasks() == PASS)
                {
                    printf("[kill]PASS\n");
                }
                else
                {
                    printf("[kill]FAIL\n");
                }
                break;
            }
            case 'c':   //CP Test
            {
                if (PCBATstr_cp() == PASS)
                {
                    printf("[cp]PASS\n");
                }
                else
                {
                    printf("[cp]FAIL\n");
                }
                break;
            }
            case 'q':   //QCA7000 Test
            {
                if (PCBATstr_qca7k() == PASS)
                {
                    printf("[qca]PASS\n");
                }
                else
                {
                    printf("[qca]FAIL\n");
                }
                break;
            }
            case 'r':   //Reboot
            {
                if (PCBATstr_reboot() == PASS)
                {
                    printf("[reboot]PASS\n");
                }
                else
                {
                    printf("[reboot]FAIL\n");
                }
                break;
            }
            case 's':   //SD Card Test
            {
                if (PCBATstr_sdcard() == PASS)
                {
                    printf("[sd]PASS\n");
                }
                else
                {
                    printf("[sd]FAIL\n");
                }
                break;
            }
            default:
            {
                sprintf(buf_log_pcbatstr, "Unknown option -%c\n\n", argv[1][1]);
                SAVE_SYS_LOG_MSG_PCBATESTER(buf_log_pcbatstr);
                printf("[others]UNSUPPORTED\n");
                break;
            }
        }
    }
}