/** @file testapci1016.c
*
* @author Sylvain Nahas
*
* This software 
* - scans the /proc/sys/apci1016/ 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.
*			 SAMPLEAPCI1016 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 <apci1016.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/apci1016/ 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 apci1016_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/apci1016";
            const char DEV_PATH[]="/dev/apci1016/";

            if (stat(PROC_PATH,&file_stat) == 0)
            {
                    /* Kernel 2.4 */
                    strcpy(APCI_SPECIAL_FILE_DIR, PROC_PATH);
                    strcpy(proc_path_template, "%s%s/ctrl");
            }
            else if (stat(DEV_PATH,&file_stat) == 0)
            {
                    /* Kernel 2.6 */
                    strcpy(APCI_SPECIAL_FILE_DIR, DEV_PATH);
                    strcpy(proc_path_template, "%s%s");
            }
            else
            {
                    perror("fstat");
					printf("Possible errors are: driver not loaded and/or board not found.\nAbort.\n");
					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),proc_path_template,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;
}
//--------------------------------------------------------------------------------
/*  loop reading digital inputs */
static void apci1016_read_input(int fd) {
	printf ("CTRL+C to stop\n");
	for(;;) {
		uint16_t DigitalInputs;
		int ret = ioctl(fd, CMD_APCI1016_Read16DigitalInput, &DigitalInputs);
		if(ret) {
			perror("ioctl");
			exit(1);
		}
		printf("0x%hX\n",DigitalInputs);
		sleep(1);
	}
}

//--------------------------------------------------------------------------------
void apci1016_run_test(int fd)
{
	printf("=> CMD_APCI1016_CheckAndGetPCISlotNumber\n");
	{	
		// deprecated (the number of boards is now unlimited and the OS gives actually these information)
		int nb = 0;
		char b_SlotArray[CONFIG_APCI1016_MAX_BOARD_NBR+1];
		if ( (nb = ioctl(fd,CMD_APCI1016_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_APCI1016_SetBoardInformation\n");
	{
		//this command do nothing
		if ( ioctl (fd,CMD_APCI1016_SetBoardInformation,0) == -1)
		{
			perror("ioctl");
			exit(1);
		}	
	}
	printf("=> CMD_APCI1016_GetHardwareInformation\n");
	{
		// deprecated (the OS gives actually these information)
		str_BaseInformations s_BaseInformations;
		if ( ioctl (fd,CMD_APCI1016_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);
		
		
	}
	apci1016_read_input(fd);
}

//--------------------------------------------------------------------------------
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 ("\tSAMPLEAPCI1016 DEMONSTRATE HOW TO USE THE DRIVER.\n");
}

//--------------------------------------------------------------------------------
int main(int argc, char** argv)
{

	int * apci1016_card_fd;
	unsigned int apci1016_card_number = 0;
	
	about_test ();
	
	{
		apci1016_card_number = apci1016_find_cards(&apci1016_card_fd);
		
		printf("total: %d cards\n",apci1016_card_number);
	}	
	
	{
		int i;
		for (i=0;i< apci1016_card_number; i++)
		{
			printf("\n+++++++++++++++++++++++++++++++++++++\nrunning test on device of minor number %d\n+++++++++++++++++++++++++++++++++++++\n\n\n",i);
			apci1016_run_test(apci1016_card_fd[i]);		
		}
	}

	

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