/**    
    @file     util.c
    @date     2009/1/15 ~
    @author   오재경 freefrug@falinux.com  FALinux.Co.,Ltd.
    @brief    단순한 유틸리티 함수들을 모아놓았다.
              Ver 0.2.1
    @modify   
    @todo     
    @bug     
    @remark   
    @warning 
*/
//----------------------------------------------------------------------------

#define EMBEDDED_LINUX                                          // 이렇게 처리하지 않으면 EClipse에서 C 영역이 회색 바탕이 됨

#ifdef MS_WIN32
    #undef EMBEDDED_LINUX
#endif

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <ctype.h>
#include <stdarg.h>
#include <time.h>

#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>

#ifdef EMBEDDED_LINUX

    #include <sys/ioctl.h>
    #include <sys/resource.h>
    #include <termios.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <net/if.h>
    #include <linux/sockios.h>
    #include <linux/kdev_t.h>

#else

    #include <windows.h>

#endif

#include <util.h>

#ifdef EMBEDDED_LINUX

//------------------------------------------------------------------------------
/** @brief   드라이버 이름으로 드라이버의 메이져번호를 얻는다.
    @param   driver_name  디바이스드라이버 이름
    @return  메이져번호
*///----------------------------------------------------------------------------
int find_dev_major( char *driver_name )
{
    FILE *fp;
    char  linebuff[128];
    char  tmp_driver_name[32];
    int   major;
    
	major = -1;

    fp = fopen("/proc/devices", "r");
    if(fp == NULL) return major;   

    while(1) 
    {
        if( fgets(linebuff, sizeof( linebuff) , fp ) == NULL ) break;
        if( sscanf(linebuff, "%d %s", &major, tmp_driver_name) == 2 )
        {
            if (strcmp( tmp_driver_name, driver_name ) == 0) break;
        }
    }
    fclose( fp );

    return major;
}

//------------------------------------------------------------------------------
/** @brief   디바이스 드라이버 노드파일을 연다.
    @param   fname  디바이스 노드파일 이름
    @param   major  디바이스 드라이버 메이져번호
    @param   minor  디바이스 드라이버 마이너번호
    @return  노드파일 핸들, 0보다 작으면 에러
    @remakr  노드파일이 없으면 노드파일을 생성한 후 연다.
*///----------------------------------------------------------------------------
int dev_open( char *fname, unsigned char major, unsigned char minor )
{
	int	dev;
	
	dev = open( fname, O_RDWR|O_NDELAY );
	if (dev < 0 )
	{
		if( access( fname, F_OK ) == 0 ) 
		{
			unlink( fname );	
		}
		
		mknod( fname, (S_IRWXU|S_IRWXG|S_IFCHR), MKDEV(major,minor) );
		
		dev = open( fname, O_RDWR|O_NDELAY );
		if (dev < 0 )
		{
			printf( " Device OPEN FAIL %s\n", fname );
			return -1;
		}
	}
        
	return dev;
}
//------------------------------------------------------------------------------
/** @brief   tftp 유틸리티를 사용하여 파일을 다운로드받는다.
    @param   ip    서버주소
    @param   fname 서버에서 받을 파일이름
    @return  성공여부
    @retval  0  성공
    @retval  -1 실패
*///----------------------------------------------------------------------------
int tftp_download_file( char *ip, char *fname )
{
#define TEMP_DOWNLOAD_FILE ".tftpdnfile.tmp"

	char     line[256];
    int      rst;

	// 받기용 temp 파일을 지운다.
	remove( TEMP_DOWNLOAD_FILE );
	
	// tftp 를 이용하여 파일을 받는다.
	sprintf( line, "tftp %s -r %s -l %s -g", ip, fname, TEMP_DOWNLOAD_FILE );
	rst = system( line );
    if ( ( -1 == rst) || ( 127 == rst) ){
        printf( "call system() error: LINE=%d\n", __LINE__);
        return -1;
    }
	
	// 파일이 존재하면 기존파일에 덮어쓴다.
	if ( 0 == exist_file( TEMP_DOWNLOAD_FILE ) ){
		sprintf( line, "cp -a %s %s", TEMP_DOWNLOAD_FILE, fname );
		rst = system( line );
	    if ( ( -1 == rst) || ( 127 == rst) ){
	        printf( "call system() error: LINE=%d\n", __LINE__);
	        return -1;
	    }
		return 0;
	}
	
	return -1;
}
//------------------------------------------------------------------------------
/** @brief   tftp 유틸리티를 사용하여 파일을  업로드한다
    @param   ip    서버주소
    @param   fname 서버에 전송할 파일이름
    @return  성공여부
    @retval  0  성공
    @retval  -1 실패
*///----------------------------------------------------------------------------
int tftp_upload_file( char *ip, char *fname )
{
    char    *ptr_name;
    char    *ptr_slash;
	char     line[256];
    int      rst;


    ptr_name    = fname;
    while( 1){
        ptr_slash   = index( ptr_name, '/');
        if ( NULL == ptr_slash){
            break;
        }
        ptr_name    = ptr_slash+1;
    }


	// tftp 를 이용하여 파일을 전송한다.
	sprintf( line, "tftp %s -r %s -l %s -p", ip, ptr_name, fname );

    rst = system( line );
    if ( ( -1 == rst) || ( 127 == rst) ){
        printf( "call system() error: LINE=%d\n", __LINE__);
    }

	return 0;
}


//------------------------------------------------------------------------------
/** @brief   usec 시간틱을 얻어온다.
    @return  usec 단위의 시간틱
    @remark  하루단위의 시간범위이다.
*///----------------------------------------------------------------------------
unsigned long get_cur_usec(void)
{
	struct timeval mytime;

    // 현재 시간을 얻어온다.
    gettimeofday(&mytime, NULL);

    return ( (mytime.tv_sec%(24*3600))*1000000 + mytime.tv_usec );
}
//------------------------------------------------------------------------------
/** @brief   문자열에서 앞뒤의 특수문자와 공백문자를 제거한다.
    @param   line 문자열 포인터
*///----------------------------------------------------------------------------
void  trimstr( char *line )
{
	int   idx, len;
	
	// 뒷쪽 공백문자와 특수문자 제거
	len = strlen(line);
	for( idx=len-1; idx>=0; idx-- )
	{
		if ( line[idx] > ' ' ) break;
	}
	line[idx+1] = '\0';
	
	// 앞쪽 공백문자와 특수문자 제거
	len = strlen(line);
	for( idx=0; idx<len; idx++ )
	{
		if ( line[idx] > ' ' ) break;
	}
	if ( (0 < idx) && (idx < len) )
	{
		memmove( line, line + idx, len-idx+1 );
	}
}
//------------------------------------------------------------------------------
/** @brief   문자열에서 특정 문자를 분리자로 사용하여 분리한다
    @param   line  분리할 문자열
    @param   delim 델리미터
    @param   item  분리된 문자열중 아이템
    @param   val   분리된 문자열중 값
    @return  성공시 0, 실패시 -1
    @remark  aaa = bbbb 형태를 분리한다.
    @bug     좀더 테스트가 필요하다.
*///----------------------------------------------------------------------------
int item_value_parse( const char *line, char delim, char *item, char *val )
{
	char     buff[1024];
	char     *pch;
	int       pos;
	
	strcpy( buff, line );

	pch = strchr( buff, delim );
	if ( pch )
	{
		pos = pch - buff;
		buff[ pos ] = '\0';
		
		strcpy( item, buff );
		strcpy( val, &buff[ pos+1 ] );
		
		trimstr( item );
		trimstr( val  );
		
		return 0;
	}
	
	return -1;
}
//------------------------------------------------------------------------------
/** @brief   문자열에서 정수형값을 분리하여 배열에 넣는다.
    @param   str        분리할 문자열
    @param   delim      델리미터 문자열
    @param   int_array  정수형 배열
    @param   count      정수형 배열 개수
*///----------------------------------------------------------------------------
int  str_to_int_array( char *str, char *delim, int *int_array, int count )
{
	char  *tok, *item, cp_str[2048];
	int    idx;
	
	strncpy( cp_str, str, 2048 );

	idx = 0;
	for (tok = strtok( cp_str, delim ); tok; tok = strtok(NULL, delim ))
	{
		item = tok;
		trimstr( item );
		
		if ( 1 <= strlen( item ) )
		{
			int_array[idx] = strtol( item, NULL, 0 );
			idx ++;
			
			if ( idx >= count ) break;
		}
	}

	return idx;
}
//------------------------------------------------------------------------------
/** @brief   자기 IP를 가져 온다.
	@param   ip 를 받을 문자열 포인터
    @return  성공시 0, 실패시 -1
    
    @todo    tstrlist 객체에 저장하여야 한다.
    @bug     
*///----------------------------------------------------------------------------
int  get_myip( char *ip ) 
{ 
	const int MAX_NIC = 10; 
	struct ifconf ifc; 
	struct ifreq ifr[MAX_NIC]; 

	int skt_fd; 
	int num_ifs; 
	int i; 
//jks	int count;
	int max=2; 
	int cmd = SIOCGIFCONF; 

	max++; 

	ifc.ifc_len = sizeof ifr; 
	ifc.ifc_ifcu.ifcu_req = ifr; 

	if( (skt_fd = socket(AF_INET,SOCK_STREAM,0)) < 0) 
	{ 
		perror("socket"); 
		return -1;
	} 

	if( ( ioctl(skt_fd, cmd, &ifc) ) < 0) 
	{ 
		perror("ioctl"); 
		close( skt_fd );
		return -1;
	} 

	num_ifs = ifc.ifc_len / sizeof ( struct ifreq ); 
//jks	count = 0;
	strcpy( ip, "127.0.0.1" ); 

	for( i=0; i<num_ifs; i++ ) 
	{ 
		struct in_addr addr; 
		if( ifc.ifc_ifcu.ifcu_req[i].ifr_ifru.ifru_addr.sa_family != AF_INET) 
		{ 
			continue; 
		} 

		addr = ((struct sockaddr_in *) & ifc.ifc_ifcu.ifcu_req[i].ifr_ifru.ifru_addr)->sin_addr; 
		if( addr.s_addr == htonl( 0x7f000001 ) ) 
		{ 
			continue; 
		} 
		strcpy( ip, inet_ntoa( addr ) ); 
		//printf( "%s IP: %s\n", ifc.ifc_ifcu.ifcu_req[i].ifr_ifrn.ifrn_name, ip ); 
	} 
	
	
	close( skt_fd );
	
	if ( 0 < num_ifs ) return 0; 
		
	return -1;
} 
//------------------------------------------------------------------------------
/** @brief   파일이나 디렉토리가 존재하는지 확인한다.
	@param   fname   파일이나 디렉토리이름
	@return  0 이면 정상, 음수이면 존재하지 않음
*///----------------------------------------------------------------------------
int exist_file( char *fname )
{
	return access( fname, F_OK );
}
//------------------------------------------------------------------------------
/** @brief   파일의 크기를 얻는다.
	@param   fname   파일이나 디렉토리이름
	@return  파일의 크기 에러일때 음수
*///----------------------------------------------------------------------------
int get_file_size( char *fname )
{
	struct stat fstat;
	
	fstat.st_size = -1;
	lstat( fname, &fstat);
	
	return fstat.st_size;
}
//------------------------------------------------------------------------------
/** @brief   로그정보를 파일에 써 넣는다.
    @param   fmt 형식문자열
    @param   가변 스택인자.
    @return  파일에 저장한 문자개수
*///----------------------------------------------------------------------------
int log_printf( const char *log_file, const char *fmt, ...  )
{
	FILE 	*fp;
	va_list ap;
	int 	len;

	fp = fopen( log_file, "a" );
	if( fp != NULL )
	{
		va_start(ap, fmt);
		len = vfprintf( fp, fmt, ap);
		va_end(ap);

		fclose( fp );
	
		return len;
	}
	return 0;
}
//------------------------------------------------------------------------------
/** @brief   날짜와 시간을 문자열 형태로 돌려준다.
    @param   fmt 형식문자열
    @param   반환문자열.
*///----------------------------------------------------------------------------
void	format_date_text( char *fmt, char *text )
{
	time_t     cur_time;
	struct tm *tm_data;
	
	time( &cur_time );
	tm_data = localtime( &cur_time );
	
	sprintf( text, fmt, 
                        tm_data->tm_year + 1900, 
                        tm_data->tm_mon +1, 
                        tm_data->tm_mday,
                        tm_data->tm_hour, 
                        tm_data->tm_min, 
                        tm_data->tm_sec );

}

//------------------------------------------------------------------------------
/** @brief   좌표가 사각영역안에 있는지 검사
    @param   x,y 좌표
    @param   sx, sy, wdt, hgt  사각영역
    @return  사각영역에 포함되면 1,  아니면 0
*///----------------------------------------------------------------------------
int	 is_rect_region( int x, int y, int sx, int sy, int wdt, int hgt )
{
	if ( ( sx <= x ) && ( x < (sx+wdt) ) && ( sy <= y ) && ( y < (sy+hgt) ) )
	{
		return 1;	
	}

	return 0;
}

//------------------------------------------------------------------------------
/** @brief   인터럽트가 발생 횟수를 돌려준다.
    @param   irq  irq 번호
    @return  인터럽트 발생회수, irq 번호가 없다면 -1
*///----------------------------------------------------------------------------
int		get_irq_count( int irq )
{
	FILE  *fp;
	char   line[256], cmp[32], *ptr;
	int    int_cnt, cmp_len;
	
	int_cnt = -1;
	cmp_len = sprintf( cmp, "%3d:", irq );

	fp = fopen( "/proc/interrupts", "r" );
	if ( fp )
	{
		while( NULL != fgets( line, sizeof(line), fp) )
		{
		 	if ( 0 == strncmp( line, cmp, cmp_len ) )
		 	{
		 		ptr = line + cmp_len;
		 		trimstr( ptr );
		 		int_cnt = strtoul( ptr, NULL, 10 );
		 		break;
		 	} 
		}
		
		fclose( fp );
	}
		
	return int_cnt;		 		
}
//------------------------------------------------------------------------------
/** @brief   ping 명령을 수행하여 성공한 회수를 돌려준다.
    @param   host   상대방 IP 문자열
    @param   count  핑통신 회수
    @return  핑성공 회수
*///----------------------------------------------------------------------------
int		ping( char *host, int count )
{
	FILE    *fp;
	char     cmd[256], line[1024], *ptr;
	int      ok_cnt = 0;
	int      rst;
	
	sprintf( cmd, "ping -c %d %s > temp.log", count, host );
	rst = system( cmd );
	if ( ( -1 == rst) || ( 127 == rst) ){
	    return 0;
	}
	
	fp = fopen( "temp.log", "r" );
	if ( fp )
	{
		while( NULL != fgets( line, sizeof(line), fp) )
		{
		 	ptr = strstr( line, "packets transmitted," ); 
		 	if ( ptr )
		 	{
		 		ok_cnt = strtoul( ptr+20 , NULL, 10 );
		 		break;
		 	} 
		}
		
		fclose( fp );
	}
	
	return ok_cnt;	 		
}
//------------------------------------------------------------------------------
/** @brief   바이너리 데이타를 헥사 문자열로 변환한다.
    @param   hex  문자열을 담을 버퍼
    @param   bin  바이너리 문자열 버퍼
    @param   bcnt 바이너리 문자열 개수
*///----------------------------------------------------------------------------
void  bin_to_hex( char *hex, char *bin, int bcnt )
{
	int    idx;
	char   tmp[16];
	
	if ( 0 < bcnt )
	{
		sprintf( hex, "%02X", bin[0] );
   	
		for(idx=1; idx<bcnt; idx++)
		{
			sprintf( tmp, " %02X", bin[idx] );
			strcat( hex, tmp );
		}
	}
}   	
//------------------------------------------------------------------------------
/** @brief   mcu 번호를 찾는다.
    @return  mcu 번호  6410, 210, 2440, 270, 255, 420, 430, 1250
*///----------------------------------------------------------------------------
int  get_mcu( void )
{
	FILE *fp;
	char str_item[256], str_val[256];
	int  rtn = -1;
	
	fp = fopen( "/proc/cpuinfo", "r" );
	if ( NULL == fp	) 
	{
		perror("err " );
		return -1;
	}

	while( 0 < fscanf( fp, "%s : %s", str_item, str_val ) )
	{
		if ( 0 == strncmp( str_item, "Hardware", 8 ) )
		{
			//printf( ">>> [%s] = [%s]\n", str_item, str_val );
			if      ( 0 == strncmp( str_val, "EZS3C6410", 9 ) )
			{
				rtn = 6410;
			}
			else if ( 0 == strncmp( str_val, "EZS5PV210", 9 ) )
			{
				rtn = 210;
			}
			break;	
		}
	}
	
	fclose( fp );
	
	return rtn;
}
//------------------------------------------------------------------------------
/** @brief   키보드 입력을 엔터없이 한문자씩 즉시 받는다.
    @return  키보드 입력값
*///----------------------------------------------------------------------------
char  getchar_without_enter( void )
{
	struct termios oldt, newt;
	char ch;

	tcgetattr( STDIN_FILENO, &oldt );
	newt = oldt;
	newt.c_lflag &= ~( ICANON | ECHO );
	tcsetattr( STDIN_FILENO, TCSANOW, &newt );

	ch = getchar();

	tcsetattr( STDIN_FILENO, TCSANOW, &oldt );
	return ch;
}
//------------------------------------------------------------------------------
/** @brief   시스템 시간을 변경한다.
    @param   yyyy, MM, dd, hh, mm, ss
    @return  성공 0, 실패 -1
*///----------------------------------------------------------------------------
int  set_sys_time( int yyyy, int MM, int dd, int hh, int mm, int ss )
{
	struct timeval  newtime;
	struct tm       tm;
	
	memset( (void *)&tm, 0 ,sizeof(tm) );
	tm.tm_year  = yyyy-1900;	 
	tm.tm_mon   = MM;    
	tm.tm_mday  = dd; 	 
	tm.tm_hour  = hh;	
	tm.tm_min   = mm; 	
	tm.tm_sec   = ss; 	

	newtime.tv_sec =  mktime( &tm );
	newtime.tv_usec = 0;
	
	return settimeofday( &newtime, NULL );
}

#endif

//------------------------------------------------------------------------------
/** @brief    현재시간을 초단위로 돌려준다
    @return   현재 시간초
*///----------------------------------------------------------------------------
unsigned long get_cur_sec( void )
{
    struct timeval mytime;

    // 현재 시간을 얻어온다.
    gettimeofday(&mytime, NULL);

    return mytime.tv_sec;
}
//------------------------------------------------------------------------------
/** @brief    현재의 msec 시간틱을 얻어온다.
    @return   현재 msec
    @todo     오버플로우에 대한 고찰이 필요하다.
*///----------------------------------------------------------------------------
msec_t get_cur_msec( void )
{
    struct      timeval mytime;
    long long   tm_sec;

    // 현재 시간을 얻어온다.
    gettimeofday(&mytime, NULL);
    tm_sec = (unsigned long) mytime.tv_sec;

    return ( tm_sec * 1000LL + mytime.tv_usec / 1000);
}
//------------------------------------------------------------------------------
/** @brief   버퍼의 데이타를 헥사 문자열로 보여준다.
    @param   buf   버퍼
    @param   count 버퍼개수
*///----------------------------------------------------------------------------
void  print_buffer( char *buf, int count )
{
	int loop;
	
	for( loop=0; loop<count; loop++ )
	{
		if ( 0 == (loop%16) )
		{
			if ( loop == 0 )
				 printf(   "%08x : ", ( unsigned int)buf + loop );
			else printf( "\n%08x : ", ( unsigned int)buf + loop );
		}
		
		printf( "%02x ", buf[loop] );
	}
	printf( "\n" );
}   	
//------------------------------------------------------------------------------
/** @brief   문자열을 숫자로 변환한다. 숫자뒤의 단위까지 판단한다.
    @param   buf   버퍼
    @return  문자열을 변환한 값
*///----------------------------------------------------------------------------
unsigned int strtosize( char *str )
{
	unsigned int val;
	char *ptr;
	
	val = strtoul( str, &ptr, 0 );

	if ( ptr )
	{
		switch( *ptr )
		{
		case 'M' : case 'm' : val <<= 20; break;
		case 'K' : case 'k' : val <<= 10; break;
		}
	}
	
	return val;
}   	

//------------------------------------------------------------------------------
/** @brief   버퍼의 값을 16비트로 더하여 돌려준다.
    @param   buf   버퍼
    @param   len   버퍼의 길이
    @return  문자열을 변환한 값
*///----------------------------------------------------------------------------
unsigned short checksum(void* buf, int len)
{
	int nleft = len;
	int sum = 0;
	unsigned short* w = (unsigned short*)buf;

	while (nleft > 1) 
	{
		sum += *w++;
		nleft -= 2;
	}
	
	if (nleft == 1)	sum += *(unsigned char*)w;
		
	sum  = (sum >> 16) + (sum & 0xffff);
	sum += (sum >> 16);
	return sum;
}
//------------------------------------------------------------------------------
/** @brief    msec 단위로 기다린다.
    @param    msec
    @remark   스케줄 틱에 의존한다.
*///----------------------------------------------------------------------------
void msleep( int msec )
{
    msec_t  over;
    
    over = get_cur_msec() + msec;
    
	while(1)
	{
		if ( over <= get_cur_msec() ) break;
		sleep(0);
	}
}