/** @file testapci2016.c
*
* @author Sylvain Nahas
*
* This software 
* - scans the /proc/sys/apci2016/ directory 
* - run a serie of test on each detected boards
* 
* IMPORTANT: THIS SOFTWARE TEST ALL AVAILABLE FUNCTIONS OF THE DRIVER.
* 			 IT IS POSSIBLE THAT BY RUNNING SOME ERRORS OCCURED.
* 			 IN ORDER TO TEST THE ERROR MANAGEMENT
* 			 SOME FUNCTIONS ARE CALLED WITH BAD PARAMTERS VALUE.
* 			 THIS TEST PROGRAM IS A DEVELOPER TOOL.
*			 SAMPLEAPCIXXXX DEMONSTRATE HOW TO USE THE DRIVER.
*/

/** @par LICENCE
* @verbatim
 Copyright (C) 2004,2005  ADDI-DATA GmbH for the source code and the documentation.
        
        ADDI-DATA GmbH
        Dieselstrasse 3 
        D-77833 Ottersweier
        Tel: +19(0)7223/9493-0
        Fax: +49(0)7223/9493-92
        http://www.addi-data-com
        info@addi-data.com

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details. 
        
    You shoud find the complete GPL in the COPYING file accompanying 
    this source code. 
* @endverbatim
*/


#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <dirent.h>
#include <string.h>
#include <sys/sysmacros.h> /* major() minor() */

#include <apci2016.h>


//------------------------------------------------------------------------------
/** Filter the "." and ".." entries in the struct dirent ** area set up by the scandir() function (utility function).*/
int
get_real_dir_entry (const struct dirent * ent)
{
        if (! strcmp(ent->d_name,"."))
                return 0;
        if (! strcmp(ent->d_name,".."))
                return 0;
                                
        return 1;
}

//--------------------------------------------------------------------------------
/** this function look up for cards in the /proc/sys/apci2016/ directory 
* @param cards A pointer to the card area to fill
* @return The number of cards detected (may be zero if nor cards found).
*/
int apci2016_find_cards(int ** cards)
{
	
	struct dirent **eps;
	int cnt = 0; // number of card found
    char APCI_SPECIAL_FILE_DIR[255]={0};
    char proc_path_template[255]={0};

    /* search which node is available */
    {
            struct stat file_stat;
            const char PROC_PATH[]="/proc/sys/apci2016";
            const char DEV_PATH[]="/dev/apci2016/";

            if (stat(PROC_PATH,&file_stat) == 0)
            {
                    /* Kernel 2.4 */
                    strncpy(APCI_SPECIAL_FILE_DIR, PROC_PATH, strlen(PROC_PATH));
                    strncpy(proc_path_template, "%s%s/ctrl", 9);
            }
            else if (stat(DEV_PATH,&file_stat) == 0)
            {
                    /* Kernel 2.6 */
                    strncpy(APCI_SPECIAL_FILE_DIR, DEV_PATH, strlen(DEV_PATH));
                    strncpy(proc_path_template, "%s%s", 4);
            }
            else
            {
                    perror("fstat");
                    exit(1);
            }
    }

	/* scan directory for card entry */
	if ( (cnt = scandir (APCI_SPECIAL_FILE_DIR, &eps, get_real_dir_entry , alphasort)) == -1 )
	{
		perror("scandir");
		return 0;
	}	
	
	if (cnt == 0) return 0;
	
	/* allocate cards area */
	if ( ! ( (*cards) = malloc( cnt * sizeof(int) ) ) )
	{
		perror("malloc");
		free(eps);
		return 0;
	}
	
	/* for each card entry, open it and store file descriptor in cards[] */
	{
		int i;
		for (i=0; i< cnt; i++)
		{
			int fd;
			char name[100];
			
			snprintf(name,sizeof(name),"%s%s/ctrl",APCI_SPECIAL_FILE_DIR,eps[i]->d_name);
			
			if ( ( fd = open(name, O_RDWR)) == -1 )
			{
				perror("open");
				exit(1);
			}			
									
			{
				struct stat file_stat;
				if ( fstat(fd,&file_stat) )
				{
					perror("fstat");
					exit(1);
				}			
				if (!S_ISCHR(file_stat.st_mode)) 
				{
					/* should never happen !*/
					printf("file %d is not a character device\n",i);
					exit(1);
				}
				printf("%s, major:%d minor:%d \n",name, major(file_stat.st_rdev),minor(file_stat.st_rdev));
			
				(*cards)[minor(file_stat.st_rdev)] = fd;
			} 
			/* index device file descriptor by the minor number */
			
		}	
		
		
		
	}
		
	return cnt;
	
	
}

//--------------------------------------------------------------------------------
int do_chenillard(int fd, int CMD)
{
	int i = 0;
	for(i=1; i<17; i++ )
	{
		unsigned int status = 0;
		if ( ioctl (fd,CMD,i) == -1)
		{
			
			perror("ioctl");
			return 1;
		}

		status = i;
		if ( ioctl (fd,CMD_APCI2016_Get1DigitalOutputStatus,&status) == -1)
		{
			
			perror("ioctl");
			return 1;
		}		
		printf("%d %d (%s)\n",i,status, (status ? "on" : "off") );
		usleep(200000);
	}	
	printf("\n");
	return 0;
}
//--------------------------------------------------------------------------------

void apci2016_test_watchdog(int fd)
{
	printf("\n Test WATCHDOG\n\n");

	printf("=> CMD_APCI2016_InitWatchdog (1 second) \n");
	{	
		if ( ioctl(fd,CMD_APCI2016_InitWatchdog,1000))
		{
			perror("ioctl");
			exit(1);
		}
	}	
	printf("=> CMD_APCI2016_StartWatchdog\n");
	{	
		if ( ioctl(fd,CMD_APCI2016_StartWatchdog,NULL))
		{
			perror("ioctl");
			exit(1);
		}
	}
	
	printf("=> CMD_APCI2016_TriggerWatchdog\n");
	{	
		if ( ioctl(fd,CMD_APCI2016_TriggerWatchdog,NULL))
		{
			perror("ioctl");
			exit(1);
		}
	}	
	printf("=> sleeping 2 seconds\n");
	sleep(2);
	
	printf("=> CMD_APCI2016_ReadWatchdogStatus\n");
	{	
		unsigned int stat = 0;
		
		if ( ioctl(fd,CMD_APCI2016_ReadWatchdogStatus,&stat))
		{
			perror("ioctl");
			exit(1);
		}
		
		printf("watchdog status : %d (%s,%s)\n", stat, (stat & APCI2016_WATCHDOG_TIMEDOUT ? "timed out" : "not timed out" ), (stat & APCI2016_WATCHDOG_TRIGGERED ? "triggered" : "not triggered" ));
		if ( !(stat &APCI2016_WATCHDOG_TIMEDOUT) )
		{
			printf("watchdog should have run out\n");
			exit(1);
		}
		
	}	

	printf("=> Now I do the test with 5 seconds\n");
	{
		printf("=> CMD_APCI2016_InitWatchdog\n");
		{	
			if ( ioctl(fd,CMD_APCI2016_InitWatchdog,5000))
			{
				perror("ioctl");
				exit(1);
			}
		}	
		printf("=> CMD_APCI2016_StartWatchdog\n");
		{	
			if ( ioctl(fd,CMD_APCI2016_StartWatchdog,NULL))
			{
				perror("ioctl");
				exit(1);
			}
		}
		printf("=> CMD_APCI2016_Set16DigitalOutputOn\n");
		{	
			if ( ioctl(fd,CMD_APCI2016_Set16DigitalOutputsOn,0xFFFF))
			{
				perror("ioctl");
				exit(1);
			}
		}			
		
		printf("=> CMD_APCI2016_TriggerWatchdog\n");
		{	
			if ( ioctl(fd,CMD_APCI2016_TriggerWatchdog,NULL))
			{
				perror("ioctl");
				exit(1);
			}
		}			
		
		printf("=> sleeping 4,5 seconds\n");
		usleep(4500000);
		{
			unsigned int stat = 0;		
			printf("=> CMD_APCI2016_ReadWatchdogStatus : ");			
			if ( ioctl(fd,CMD_APCI2016_ReadWatchdogStatus,&stat))
			{
				perror("ioctl");
				exit(1);
			}					
			printf("watchdog status : %d (%s,%s)\n", stat, (stat & APCI2016_WATCHDOG_TIMEDOUT ? "timed out" : "running" ), (stat & APCI2016_WATCHDOG_TRIGGERED ? "triggered" : "not triggered" ));
			if ( (stat &APCI2016_WATCHDOG_TIMEDOUT) )
			{
				printf("watchdog should have not run out\n");
				exit(1);
			}		
		}			
		printf("=> sleeping 2 seconds\n");
		sleep(2);		
		
		{
			unsigned int stat = 0;		
			printf("=> CMD_APCI2016_ReadWatchdogStatus : ");			
			if ( ioctl(fd,CMD_APCI2016_ReadWatchdogStatus,&stat))
			{
				perror("ioctl");
				exit(1);
			}					
			printf("watchdog status : %d (%s,%s)\n", stat, (stat & APCI2016_WATCHDOG_TIMEDOUT ? "timed out" : "running" ), (stat & APCI2016_WATCHDOG_TRIGGERED ? "triggered" : "not triggered" ));
			if ( !(stat &APCI2016_WATCHDOG_TIMEDOUT) )
			{
				printf("watchdog should have run out\n");
				exit(1);
			}		
		}				
	}
}
//--------------------------------------------------------------------------------
void apci2016_test_watchdogtriggers(int fd)
{
	printf("\n Test WATCHDOG TRIGGER\n\n");
	
	printf("=> Now I configure the watchdog for 1 second and trigger each  500 ms during 10 second : LEDS should never go off until the end\n");

	printf("=> CMD_APCI2016_InitWatchdog\n");
	{	
		if ( ioctl(fd,CMD_APCI2016_InitWatchdog,1000))
		{
			perror("ioctl");
			exit(1);
		}
	}	
	printf("=> CMD_APCI2016_StartWatchdog\n");
	{	
		if ( ioctl(fd,CMD_APCI2016_StartWatchdog,NULL))
		{
			perror("ioctl");
			exit(1);
		}
	}
	printf("=> CMD_APCI2016_Set16DigitalOutputOn\n");
	{	
		if ( ioctl(fd,CMD_APCI2016_Set16DigitalOutputsOn,0xFFFF))
		{
			perror("ioctl");
			exit(1);
		}
	}			

	/*loop during an estimated 5 seconds */		
	{
		int i;
		
		for( i = 0; i < 40; i++)
		{
			printf("=> CMD_APCI2016_TriggerWatchdog\n");
			{	
				if ( ioctl(fd,CMD_APCI2016_TriggerWatchdog,NULL))
				{
					perror("ioctl");
					exit(1);
				}
			}			
			usleep(500000);		
		}		
	}
	{
		printf("=> CMD_APCI2016_TriggerWatchdog\n");
		{	
			if ( ioctl(fd,CMD_APCI2016_TriggerWatchdog,NULL))
			{
				perror("ioctl");
				exit(1);
			}
		}			
	}

	/* check that watchdog timeout hasn't occured */
	{
		unsigned int stat = 0;		
		printf("=> CMD_APCI2016_ReadWatchdogStatus : ");			
		if ( ioctl(fd,CMD_APCI2016_ReadWatchdogStatus,&stat))
		{
			perror("ioctl");
			exit(1);
		}					
		printf("watchdog status : %d (%s,%s)\n", stat, (stat & APCI2016_WATCHDOG_TIMEDOUT ? "timed out" : "running" ), (stat & APCI2016_WATCHDOG_TRIGGERED ? "triggered" : "not triggered" ));
		if ( (stat &APCI2016_WATCHDOG_TIMEDOUT) )
		{
			printf("watchdog should not have run out\n");
			exit(1);
		}		
	}
	{
		printf("=> sleep 1 second\n");			
		sleep(1);
	}
	/* check that watchdog timeout did occur */
	{
		unsigned int stat = 0;		
		printf("=> CMD_APCI2016_ReadWatchdogStatus : ");			
		if ( ioctl(fd,CMD_APCI2016_ReadWatchdogStatus,&stat))
		{
			perror("ioctl");
			exit(1);
		}					
		printf("watchdog status : %d (%s,%s)\n", stat, (stat & APCI2016_WATCHDOG_TIMEDOUT ? "timed out" : "running" ), (stat & APCI2016_WATCHDOG_TRIGGERED ? "triggered" : "not triggered" ));
		if ( !(stat &APCI2016_WATCHDOG_TIMEDOUT) )
		{
			printf("watchdog should not have run out\n");
			exit(1);
		}		
	}

}

//--------------------------------------------------------------------------------

void apci2016_test_outputs(int fd)
{
	/* memory off */
	printf("=> CMD_APCI2016_SetDigitalOutputMemoryOff\n");
	{	
		if ( ioctl(fd,CMD_APCI2016_SetDigitalOutputMemoryOff,NULL))
		{
			perror("ioctl");
			exit(1);
		}
	}
	
	printf("=> Now I do a \"chenillard\" that light ON\n");
	do_chenillard(fd, CMD_APCI2016_Set1DigitalOutputOn );
	
	printf("=> Now I do a \"chenillard\" that light OFF (should fail)\n");	
	do_chenillard(fd, CMD_APCI2016_Set1DigitalOutputOff );
	

	printf("=> CMD_APCI2016_SetDigitalOutputMemoryOn\n");
	{	
		if ( ioctl(fd,CMD_APCI2016_SetDigitalOutputMemoryOn,NULL))
		{
			perror("ioctl");
			exit(1);
		}
	}	
		
	printf("=> Now I do a \"chenillard\" that light on LEDS\n");
	do_chenillard(fd, CMD_APCI2016_Set1DigitalOutputOn );	
	printf("=> Now I do a \"chenillard\" that light OFF\n");	
	do_chenillard(fd, CMD_APCI2016_Set1DigitalOutputOff );	
	{
		uint16_t val = 0;
	
		printf("=> CMD_APCI2016_Get16DigitalOutputStatus = ");
		if ( ioctl(fd,CMD_APCI2016_Get16DigitalOutputStatus,&val))
		{
		perror("ioctl");
		exit(1);			
		}
		printf("%X\n",val);
	}
	printf("=> CMD_APCI2016_SetDigitalOutputMemoryOff\n");
	{	
		if ( ioctl(fd,CMD_APCI2016_SetDigitalOutputMemoryOn,NULL))
		{
			perror("ioctl");
			exit(1);
		}
	}		
	printf("=> CMD_APCI2016_Set16DigitalOutputOn\n");
	printf("=> Now I switch one LED on two ON\n");	
	{	
		if ( ioctl(fd,CMD_APCI2016_Set16DigitalOutputsOn,0xAAAA))
		{
			perror("ioctl");
			exit(1);
		}
	}			
	{
		uint16_t val;
		printf("=> CMD_APCI2016_Get16DigitalOutputStatus = ");
		if ( ioctl(fd,CMD_APCI2016_Get16DigitalOutputStatus,&val))
		{
			perror("ioctl");
			exit(1);			
		}
		printf("0x%X\n",val);
		if (val != 0xAAAA)
		{
			printf("should be 0xAAAA\n");
			exit(1);
		} 
	}
	printf("=> test of CMD_APCI2016_Get1DigitalOutputStatus\n");	
	{
		uint16_t val = 0;

		/* get status for each output, result is coded in val*/
		{
			int i;
			for ( i= 1; i<17; i++)
			{ 
				unsigned int outpstatus = i;
				if ( ioctl(fd,CMD_APCI2016_Get1DigitalOutputStatus,&outpstatus))
				{
					perror("ioctl");
					exit(1);			
				}
				val |= (outpstatus << (i-1));
			}
		}
		printf("status = 0x%X ",val);
		if (val != 0xAAAA)
		{
			printf("(should be 0xAAAA and is not)\n");
			exit(1);
		} 
		else
		{
			printf("(OK)\n");
		}
	}	
}
//--------------------------------------------------------------------------------
void apci2016_test_basic(int fd)
{
	printf("=> CMD_APCI2016_CheckAndGetPCISlotNumber\n");
	{	
		// deprecated (the number of boards is now unlimited and the OS gives actually these information)
		int nb = 0;
		char b_SlotArray[CONFIG_APCI2016_MAX_BOARD_NBR+1];
		if ( (nb = ioctl(fd,CMD_APCI2016_CheckAndGetPCISlotNumber,b_SlotArray)) == 0 )
		{
			printf("no boards found\n"); 
			exit(1);
		}	

		printf("The driver advertise %d board\n", nb);
		
		{
			int i;
			for (i=0; i< nb; i++)
	   			printf ("\t%d: Slot Number                       : %d\n", i, b_SlotArray[i]);		
		}
		
	}		

	printf("=> CMD_APCI2016_CheckAndGetPCISlotNumber with bad address\n");   
	{   
		if ( (ioctl(fd,CMD_APCI2016_CheckAndGetPCISlotNumber,NULL)) == 0 )   
		{   
		printf("CMD_APCI2016_CheckAndGetPCISlotNumber failed\n");   
		}   
      
	}

	
	printf("=> CMD_APCI2016_SetBoardInformation\n");
	{
		//this command do nothing
		if ( ioctl (fd,CMD_APCI2016_SetBoardInformation,0) == -1)
		{
			perror("ioctl");
			exit(1);
		}	
	}
	printf("=> CMD_APCI2016_GetHardwareInformation\n");
	{
		// deprecated (the OS gives actually these information)
		str_BaseInformations s_BaseInformations;
		if ( ioctl (fd,CMD_APCI2016_GetHardwareInformation,&s_BaseInformations) == -1)
		{
			perror("ioctl");
			exit(1);
		}			

   		printf("\tBAR 0 : %d\n",  s_BaseInformations.ui_BaseAddress[0] ); 
		printf("\tBAR 1 : %d\n",  s_BaseInformations.ui_BaseAddress[1] ); 
		printf("\tBAR 2 : %d\n",  s_BaseInformations.ui_BaseAddress[2] ); 
		printf("\tBAR 3 : %d\n",  s_BaseInformations.ui_BaseAddress[3] ); 
		printf("\tBAR 5 : %d\n",  s_BaseInformations.ui_BaseAddress[4] ); 
   		printf("\tinterrupt nb: %d\n", s_BaseInformations.b_Interrupt);
   		printf("\tslot number: %d\n", s_BaseInformations.b_SlotNumber);
		
		
	}
	
}

void apci2016_test_all(int fd)
{
	apci2016_test_basic(fd);
	apci2016_test_outputs(fd);
	apci2016_test_watchdog(fd);
	apci2016_test_watchdogtriggers(fd);
}

//--------------------------------------------------------------------------------
static void usage(void)
{
	printf("testapci2016 COMMAND1 [COMMAND2] ... [COMMANDN] \n");
	
}


static struct {char* name; char * desc; void (*fc) (int) ;} commandtab[] =
{
	{	
		"basic",
		"(CMD_APCI2016_CheckAndGetPCISlotNumber, CMD_APCI2016_SetBoardInformation, CMD_APCI2016_GetHardwareInformation)",
		apci2016_test_basic
	},
	{
		"output",
		"test output",
		apci2016_test_outputs
	},
	{
		"watchdog",
		"test watchdog functionality",
		apci2016_test_watchdog
	},
	{
		"triggers",
		"specifically tests watchdog triggering",
		apci2016_test_watchdogtriggers
	},	
	{
		"all",
		"basic output watchdog",
		apci2016_test_all
	},
	{NULL,NULL,NULL}
};

//--------------------------------------------------------------------------------
void apci2016_test_help(int fd)
{
	int i = 0;
	printf("\navailable commands:\n");
	while (commandtab[i].name != NULL)
	{
		
		printf("%s\t%s\n",commandtab[i].name,commandtab[i].desc);
		i++;
	}
	printf("\n");
}
//--------------------------------------------------------------------------------
void apci2016_run_test(int fd, char* command)
{
	
	int i = 0;
	while (commandtab[i].name != NULL)
	{
		if (strcmp(command, commandtab[i].name) == 0 )
		{
			commandtab[i].fc (fd);
			return;
		}
		i++;
	}
	printf("unknown command %s\n",command);
	apci2016_run_test(fd, "help");
	exit(1);

}


//--------------------------------------------------------------------------------
void about_test (void)
{
	printf ("IMPORTANT:\n");
	printf ("\tTHIS SOFTWARE TEST ALL AVAILABLE FUNCTIONS OF THE DRIVER.\n");
	printf ("\tIT IS POSSIBLE THAT BY RUNNING SOME ERRORS OCCURED.\n");
	printf ("\tIN ORDER TO TEST THE ERROR MANAGEMENT\n");
	printf ("\tSOME FUNCTIONS ARE CALLED WITH BAD PARAMTERS VALUE.\n");
	printf ("\tTHIS TEST PROGRAM IS A DEVELOPER TOOL.\n");
	printf ("\tSAMPLEAPCIXXXX DEMONSTRATE HOW TO USE THE DRIVER.\n");
}

//--------------------------------------------------------------------------------
int main(int argc, char** argv)
{
	int * apci2016_card_fd;
	unsigned int apci2016_card_number = 0;
	
	about_test ();
	
	if (argc < 2 )
	{
		printf("not enough arguments\n");
		usage();
		apci2016_test_help(0);
		exit(1);
		
	}

	{
		apci2016_card_number = apci2016_find_cards(&apci2016_card_fd);
		
		printf("total: %d cards\n",apci2016_card_number);
	}	
	
	{
		int i;
		for (i=0;i< apci2016_card_number; i++)
		{
			printf("\n+++++++++++++++++++++++++++++++++++++\nrunning test on device of minor number %d\n+++++++++++++++++++++++++++++++++++++\n\n\n",i);
			{
				int j = 0;
				for (j=1; j< argc; j++)
				{
					apci2016_run_test(apci2016_card_fd[i],argv[j]);		
				}
			}
		}
	}

	

	return 0;
}
//--------------------------------------------------------------------------------
