#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <linux/if.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <time.h>
#include <signal.h>
#include <assert.h>

#include "xvdrv_if.h"

FILE * File =  NULL;
int fd=0;
int channel = 0;

typedef void Sigfunc(int);
typedef Sigfunc *SigfuncPtr;

static SigfuncPtr setsignal( int inSigno, SigfuncPtr inFunc ) 
{   
    struct sigaction theNewAction, theOldAction;    
    assert( inFunc != NULL );   
    theNewAction.sa_handler = inFunc;   
    sigemptyset( &theNewAction.sa_mask );   
    theNewAction.sa_flags = 0;  
    if ( inSigno == SIGALRM )   
    {
        #ifdef SA_INTERRUPT        
        theNewAction.sa_flags |= SA_INTERRUPT;  /* SunOS 4.x */
        #endif   
    }   
    else    
    {
        #ifdef SA_RESTART        
        theNewAction.sa_flags |= SA_RESTART;    /* SVR4, 4.4BSD */
        #endif  

    }   

    if ( sigaction( inSigno, &theNewAction, &theOldAction ) < 0 )   
    {       
        return SIG_ERR; 
    }   
    else    
    {       
        return theOldAction.sa_handler; 
    }
}

static void SigProc( int inSigno ) 
{   
    struct para_open_req cmd_close;
    struct ifreq ifr;
    
    cmd_close.struct_size = sizeof(struct para_close_req);
    cmd_close.handle = 0;

    strcpy(ifr.ifr_name, "veth");
    ifr.ifr_data = &cmd_close;

    if(0!=ioctl(fd,XVETH_CMD_CLOSE, &ifr))
    {
        printf("xveth close error\n");
            
    }
    
    if(File)
    {
       fclose(File);
       File = NULL;
    }
    
    if(fd)
    {
        close(fd);
        fd = 0;
    }

    exit(-1);
}

static void usage(void * pappname)
{
    printf("Usage: %s [tx|rx] [filename]\n",pappname);
    printf("Option:rx for receive,tx for send\n");
}
int main(int argc,char * argv[])
{
    struct ifreq ifr;
    char *p;
    int i;
    int tx_flag = 0;
    int res;
    struct timeval  tvstart, tvend;
    double time_used;
    int show=0;

    
    unsigned int file_size;
    unsigned int *pint;
    
    if((argc!=3)&&(argc!=4))
    {
        usage(argv[0]);
        return -1;
    }

    if(!strcmp(argv[1],"tx"))
    {
        printf("Veth ioctl send demo start.\n");
        tx_flag = 1;
    }
    else if(!strcmp(argv[1],"rx"))
    {
        
        printf("Veth ioctl recv demo start.\n");
        tx_flag = 0;
    }
    else
    {
        printf("Incorrect option\n");
        usage(argv[0]);
        return -2;
    }

#if 0
    if(4==argc)
    {
        if(!strcmp(argv[3],"show"))
            show=1;
        else
        {
            printf("Incorrect option\n");
            usage(argv[0]);
            return -2;
        }
    }
#endif
	if(4==argc)
	{
		char *tmp;
		
		channel = atoi(argv[3]);
		if((channel<0) || (channel>=8))
		{
			printf("Channel number must be 0-7.\n");
			return -3;
		}

		tmp = getenv("VETH_IOCTL_TEST");
		if(NULL != tmp)
		{
			if(atoi(tmp) == channel)
				show=1;
		}
	}
    
    fd = socket(AF_INET,SOCK_DGRAM,0);
    strcpy(ifr.ifr_name, "veth");

    struct para_open_req cmd_open;
    cmd_open.struct_size = sizeof(struct para_open_req);
    cmd_open.input_buf = 0xdeadbeaf;
    cmd_open.handle = channel; //0;
    
    ifr.ifr_data = &cmd_open;
    if(0!=ioctl(fd,XVETH_CMD_OPEN, &ifr))
    {
        printf("xveth open error\n");
        goto test_exit;
    }
    
    printf("handle:0x%x\n",cmd_open.handle);
    printf("send buff addr:0x%x\n",cmd_open.input_buf);
    printf("send buff size:0x%x\n",cmd_open.input_maxsize);

    setsignal( SIGTERM, SigProc );
    setsignal( SIGINT,  SigProc );

    if(tx_flag) //sendfile
    {
        struct para_send_req cmd_send;
        struct stat finfo;
        unsigned int left;	
        unsigned int readbyte;
        struct timespec sleep_time; 
        struct timespec left_time;
        
        File = fopen(argv[2],"rb");
        if(NULL == File)
        {
            printf("Can't open input filename.");
            goto test_exit;
        }	

        if(-1==fstat(fileno(File),&finfo))
        {
            printf("Can't get file size.");
            goto test_exit;
        }

        file_size = finfo.st_size;
        printf("Output file size is %d bytes.\n",file_size);
        
        //send file length
        #ifdef VETH_BIG_ENDIAN
        {
            unsigned char *pchar = (unsigned char *)cmd_open.input_buf;
            *pchar++=0xef;
            *pchar++=0xbe;
            *pchar++=0xad;
            *pchar++=0xde;
            *pchar++=(unsigned char)(file_size& 0xff);
            *pchar++=(unsigned char)((file_size>>8) & 0xff);
            *pchar++=(unsigned char)((file_size>>16) & 0xff);
            *pchar++=(unsigned char)((file_size>>24) & 0xff);
        }
        #else
        pint=(unsigned int *)cmd_open.input_buf;
        *pint++=0xdeadbeef;
        *pint= file_size;
        #endif
        
        cmd_send.handle = cmd_open.handle;
        cmd_send.input_size = 8;
        cmd_send.struct_size = sizeof(struct para_send_req);
        ifr.ifr_data = &cmd_send;
        
        do{
            res = ioctl(fd,XVETH_CMD_SEND, &ifr);
        }while(res!=0);
            
        //send file;
        sleep_time.tv_sec = 0;
        sleep_time.tv_nsec = 50000; //20ms

        gettimeofday(&tvstart,NULL);
        left=file_size;
        
        do
        {
            readbyte = fread((void *)cmd_open.input_buf,1,
                cmd_open.input_maxsize,File);

            if(0==readbyte)
                goto test_exit;
            
            left -= readbyte;
            
            cmd_send.handle = cmd_open.handle;
            cmd_send.input_size = readbyte;
            cmd_send.struct_size = sizeof(struct para_send_req);
            ifr.ifr_data = &cmd_send;
            do{
                res = ioctl(fd,XVETH_CMD_SEND, &ifr);
                if(0==res)
                {
                    if(show)
                        putchar('*');
                }
                else 
                {
                    //printf("cmd_send.return_value:0x%x\n",cmd_send.return_value);
                    //printf("res:0x%x\n",res);
                    if(nanosleep(&sleep_time,&left_time))
                        printf("sleep_error\n");

                    if(show)
                        putchar('#');
                }
            }while(res!=0);	

        }while(left!=0);

        if(show)
            putchar('\n');

        gettimeofday(&tvend,NULL);
        time_used = tvend.tv_sec-tvstart.tv_sec + 
            ((double)(tvend.tv_usec - tvstart.tv_usec+1))/1000000;
        
        printf("Used time(s):%fs\n",time_used);
        printf("Transmit speed is %f MB/bps.\n",((float)(file_size))
            /1000000.0/time_used);
    }
    else //receive file
    {
        struct para_recv_req cmd_recv;
        unsigned int total_rx_byte;
        unsigned int rx_left;
        
        File = fopen(argv[2],"w+");
        if(NULL == File)
        {
            printf("Can't open input filename.");
            goto test_exit;
        }	


        //get file size
        cmd_recv.handle = cmd_open.handle;
        cmd_recv.timeout= 200;
        cmd_recv.struct_size = sizeof(struct para_recv_req);
        cmd_recv.output_buf = 0;
        ifr.ifr_data = &cmd_recv;

        do{
            res=ioctl(fd,XVETH_CMD_RECV, &ifr);
        }while(res!=0);

        pint = (unsigned int *)cmd_recv.output_buf;
        #ifdef VETH_BIG_ENDIAN
        {
            unsigned char *pchar = (unsigned char *)cmd_recv.output_buf;
            unsigned char tmp;
            tmp= pchar[0];
            pchar[0]=pchar[3];
            pchar[3]=tmp;

            tmp = pchar[1];
            pchar[1]=pchar[2];
            pchar[2]=tmp;

            pchar +=4;
            
            tmp= pchar[0];
            pchar[0]=pchar[3];
            pchar[3]=tmp;

            tmp = pchar[1];
            pchar[1]=pchar[2];
            pchar[2]=tmp;
            
        }
        #endif
        if((cmd_recv.output_size!=8)|| (*pint!=0xdeadbeef))
        {
            printf("recv_size:%d,flag:0x%x",cmd_recv.output_size,*pint);
            goto test_exit;
            
        }

        pint++;
        total_rx_byte =*pint;
        printf("Input file size: %d bytes\n",total_rx_byte);
        rx_left = total_rx_byte;

        gettimeofday(&tvstart,NULL);
            
        do
        {
            unsigned int rx_pack_len;
            unsigned int rx_pack_left;
            unsigned int write_ok;
                
            cmd_recv.handle = cmd_open.handle;
            cmd_recv.timeout= 10000; //10000ms
            cmd_recv.struct_size = sizeof(struct para_recv_req);
            ifr.ifr_data = &cmd_recv;
            do
            {
                res=ioctl(fd,XVETH_CMD_RECV, &ifr);
            }while(res);

            rx_pack_len = cmd_recv.output_size;
            rx_pack_left = rx_pack_len;

            do
            {
                write_ok = fwrite((void *)cmd_recv.output_buf,1,rx_pack_left,File);
                rx_pack_left -= write_ok;
            }while(rx_pack_left);

            if(show)
                putchar('*');
            rx_left -= rx_pack_len;
        }while(rx_left);

        //printf("output buf:0x%x\n",cmd_recv.output_buf);
        //printf("output buf size:0x%x\n",cmd_recv.output_size);
        //printf("--->received:%s\n",(char *)cmd_recv.output_buf);

        if(show)
            putchar('\n');

        gettimeofday(&tvend,NULL);
        time_used = tvend.tv_sec-tvstart.tv_sec + 
            ((double)(tvend.tv_usec - tvstart.tv_usec+1))/1000000;
        
        printf("Used time(s):%fs\n",time_used);
        printf("Transmit speed is %f MB/bps.\n",((float)(total_rx_byte))
            /1000000.0/time_used);

    }

    struct para_open_req cmd_close;

test_exit:	
    cmd_close.struct_size = sizeof(struct para_close_req);
    cmd_close.handle = cmd_open.handle;
    ifr.ifr_data = &cmd_close;
    if(0!=ioctl(fd,XVETH_CMD_CLOSE, &ifr))
    {
        printf("xveth close error\n");
        //goto test_exit;
    }

    if(File)
    {
        fclose(File);
        File = NULL;
    }

    if(fd)
    {
        close(fd);
        fd = 0;
    }
    return 0;
}
