/** @file samlple_apci1648.c
 
   This software 
   - scans the /proc/sys/apci1648/ directory 
   - run a serie of test on each detected boards
  
   @par CREATION  
   @author Krauth Julien
   @date   13.06.05
   
   @par VERSION
   @verbatim
   $LastChangedRevision:$
   $LastChangedDate:$
   @endverbatim   
   
   @par COMPILER
   @verbatim
   Compiler	: gcc
   Version	: gcc (GCC) 3.3.6 (Debian 1:3.3.6-5)   
   kernel	: 2.4.26
   @endverbatim  
   
   @par UPDATES
   @verbatim
   Date			:
   Author		: 
   Description	: 
   @endverbatim    
    
   @par LICENCE
   @verbatim
   Copyright (C) 2004,2005  ADDI-DATA GmbH for the source code of this module.
        
        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 should have received a copy of the GNU General Public License along with 
   this program; if not, write to the Free Software Foundation, 
   Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

   You shoud also 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 <signal.h> /* For signal */

#include <apci1648.h>


/* This global variable is used to stop each single samples */
unsigned char stop;

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

/** Move the consol cursor on the specified point.
 * 
 * @param x	Horizontal position of the cursor.
 * @param y	Vertical position of the cursor.
 * 
 * retval 0 No error.
 */
int     gotoxy(int x, int y) 
        {
        char essq[100];       // String variable to hold the escape sequence
        char xstr[100];       // Strings to hold the x and y coordinates
        char ystr[100];       // Escape sequences must be built with characters
        
        // Convert the screen coordinates to strings
        sprintf(xstr, "%d", x);
        sprintf(ystr, "%d", y);
        
        // Build the escape sequence (vertical move)
        essq[0] = '\0';
        strcat(essq, "\033[");
        strcat(essq, ystr);

        // Described in man terminfo as vpa=\\E[%p1%dd
        // Vertical position absolute
        strcat(essq, "d");

        // Horizontal move
        // Horizontal position absolute
        strcat(essq, "\033[");
        strcat(essq, xstr);

        // Described in man terminfo as hpa=\\E[%p1%dG
        strcat(essq, "G");

        // Execute the escape sequence
        // This will move the cursor to x, y
        printf("%s", essq);

        return 0;
        }



//------------------------------------------------------------------------------
/** 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/apci1648/ 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 apci1648_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/apci1648";
                const char DEV_PATH[]="/dev/apci1648/";

                if (stat(PROC_PATH,&file_stat) == 0)
                {
                        /* Kernel 2.4 */
                        strncpy(APCI_SPECIAL_FILE_DIR, PROC_PATH, strlen(PROC_PATH));
                        strcpy(proc_path_template, "%s%s/ctrl");
                }
                else if (stat(DEV_PATH,&file_stat) == 0)
                {
                        /* Kernel 2.6 */
                        strncpy(APCI_SPECIAL_FILE_DIR, DEV_PATH, strlen(DEV_PATH));
                        strcpy(proc_path_template, "%s%s");
                }
                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),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;
	
	
}
//--------------------------------------------------------------------------------
void apci1648_run_test(int fd)
{
	printf("=> CMD_APCI1648_CheckAndGetPCISlotNumber\n");
	{	
		// deprecated (the number of boards is now unlimited and the OS gives actually these information)
		int nb = 0;
		char b_SlotArray[CONFIG_APCI1648_MAX_BOARD_NBR+1];
		if ( (nb = ioctl(fd,CMD_APCI1648_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_APCI1648_SetBoardInformation\n");
	{
		//this command do nothing
		if ( ioctl (fd,CMD_APCI1648_SetBoardInformation,0) == -1)
		{
			perror("ioctl");
			exit(1);
		}	
	}
	printf("=> CMD_APCI1648_GetHardwareInformation\n");
	{
		// deprecated (the OS gives actually these information)
		str_BaseInformations s_BaseInformations;
		if ( ioctl (fd,CMD_APCI1648_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);
		
		
	}

}

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

/** Test APCI-1648 initialization.
 * 
 * @param [in]	fd	File descriptor of the borad to use.
 * 
 * @retval 0	No error.
 * @retval 1	Error.
 */
int apci1648_test_initialization (int fd)
{
   unsigned char PortsSetting[6]; /**< Ports initialization type:<br>
                                       - 0: Input direction<br>
                                       - 1: Output direction*/
   int i=0;
	int choice = 0;
   
   
   struct DigitalPortsArg_t DigitalPort;
   
      /* Set the port setting variable to 0 */
      memset (PortsSetting, 0, (sizeof (unsigned char) * 6));   


   
      /* Set the configuration of all ports */
      for (i=0; i<APCI1648_PORT_NUMBER; i++)      
         {
         do
            {
            printf ("Select the configuration for port %i (0:Input / 1:Output):\n", (APCI1648_PORT0+i));
            scanf ("%i", &choice);
            }
         while ( (PortsSetting [APCI1648_PORT0+i] != 0) && (PortsSetting [APCI1648_PORT0+i] != 1) );
			PortsSetting [APCI1648_PORT0+i] = choice;
         }
       
      if ( ioctl (fd,CMD_APCI1648_InitAllDigitalPorts,PortsSetting) < 0 )
         {
         perror("ioctl");
         exit(1);
         }	
      
      printf ("CMD_APCI1648_InitAllDigitalPorts: OK\n");
      printf ("\n");
            
      /* Set the port setting variable to 0 */
      memset (PortsSetting, 0, (sizeof (unsigned char) * 6));
      
      /* Get all ports actual setting */
      if ( ioctl (fd,CMD_APCI1648_GetAllDigitalPortsSetting,PortsSetting) < 0 )
         {
         perror("ioctl");
         exit(1);
         }	
         
      for (i=0; i<APCI1648_PORT_NUMBER; i++)
         printf ("Port %i is as digital %s\n", (APCI1648_PORT0+i), (PortsSetting [APCI1648_PORT0+i] == APCI1648_PORT_INPUT_DIRECTION)?"input": "output");     
      
      printf ("CMD_APCI1648_GetAllDigitalPortsSetting: OK\n"); 
      printf ("\n");
            
      /* Change the setting of one port */      
      do
         {         	
         printf ("Select the port to configure (0 - 5, this function initialize 1 port):\n");
         scanf ("%i", (int*)&DigitalPort.Port);
         }
      while ( (DigitalPort.Port > 5) );      
                 
      do
         {         	
         printf ("Select the configuration for port %i (0:Input / 1:Output):\n", DigitalPort.Port);
         scanf ("%i", (int*)&DigitalPort.Value);
         }
      while ( (DigitalPort.Value != 0) && (DigitalPort.Value != 1) );         
      
      if ( ioctl (fd,CMD_APCI1648_InitDigitalPort,&DigitalPort) < 0 )
         {
         perror("ioctl");
         exit(1);
         }	
         
      printf ("CMD_APCI1648_InitDigitalPort: OK\n");               
      printf ("\n");

      /* Get all ports actual setting */
      for (i=0; i<APCI1648_PORT_NUMBER; i++)
         {
         DigitalPort.Port = (APCI1648_PORT0+i);
         DigitalPort.Value = 0;
         	
         if ( ioctl (fd,CMD_APCI1648_GetDigitalPortSetting,&DigitalPort) < 0 )
            {
            perror("ioctl");
            exit(1);
            }	
          
         printf ("Port %i is as digital %s\n", (APCI1648_PORT0+i), (DigitalPort.Value == APCI1648_PORT_INPUT_DIRECTION)?"input": "output");     
         }
      printf ("CMD_APCI1648_GetDigitalPortSetting: OK\n"); 
      printf ("\n");      

      return 0;
}

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

/** Test APCI-1648 digital input.
 * 
 * @param [in]	fd	File descriptor of the borad to use.
 * 
 * @retval 0	No error.
 * @retval 1	Error.
 */
int apci1648_test_digital_input (int fd)
{
   unsigned char PortsSetting[6]; /**< Ports initialization type:<br>
                                       - 0: Input direction<br>
                                       - 1: Output direction*/
   unsigned char PortsStates[6];
   int i=0;
            
      /* Set port variable to 0 */
      memset (PortsSetting, APCI1648_PORT_INPUT_DIRECTION, (sizeof (unsigned char) * 6));
      memset (PortsStates, 0, (sizeof (unsigned char) * 6));
      
      
      /* Set all ports as input */
      if ( ioctl (fd,CMD_APCI1648_InitAllDigitalPorts,PortsSetting) < 0 )
         {
         perror("ioctl");
         exit(1);
         }      
      
      /* Clean the screen before display input status */
      system("clear"); 
               
      while (!stop)
         {
         /* Read all digital input state */
         if ( ioctl (fd,CMD_APCI1648_ReadAllDigitalInputs,PortsStates) < 0 )
            {
            perror("ioctl");
            exit(1);
            }	      
                    
         /* Set the cursor on screen top */  
         gotoxy (0,0);
         printf ("\n        +-------------------------------+");
         printf ("\n        | DIGITAL INPUTS STATES         |");
         printf ("\n+-------+---+---+---+---+---+---+---+---+-----+");
   	     printf ("\n| PORT  | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | Hex |");
				
         for (i=0; i<APCI1648_PORT_NUMBER; i++)
            {
			printf ("\n+-------+---+---+---+---+---+---+---+---+-----+");              
		    printf ("\n| %3d   |%3s|%3s|%3s|%3s|%3s|%3s|%3s|%3s| %3x |", (APCI1648_PORT0+i),
				     (PortsStates [(APCI1648_PORT0+i)] & 128)?"ON":"OFF",
				     (PortsStates [(APCI1648_PORT0+i)] & 64)?"ON":"OFF",
				     (PortsStates [(APCI1648_PORT0+i)] & 32)?"ON":"OFF",
				     (PortsStates [(APCI1648_PORT0+i)] & 16)?"ON":"OFF",
				     (PortsStates [(APCI1648_PORT0+i)] & 8)?"ON":"OFF",
				     (PortsStates [(APCI1648_PORT0+i)] & 4)?"ON":"OFF",
				     (PortsStates [(APCI1648_PORT0+i)] & 2)?"ON":"OFF",
				     (PortsStates [(APCI1648_PORT0+i)] & 1)?"ON":"OFF",
				     PortsStates [(APCI1648_PORT0+i)]);             	 
            }
         printf ("\n+---------------------------------------------+"); 
         printf ("\n");            
         printf ("CMD_APCI1648_ReadAllDigitalInputs: OK\n"); 
         printf ("ctrl + C for the next test\n"); 
         printf ("\n");      
         }
         
      return 0;
}

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

/** Test APCI-1648 digital output.
 * 
 * @param [in]	fd	File descriptor of the borad to use.
 * 
 * @retval 0	No error.
 * @retval 1	Error.
 */
int apci1648_test_digital_output (int fd)
{
   unsigned char PortsSetting[6]; /**< Ports initialization type:<br>
                                       - 0: Input direction<br>
                                       - 1: Output direction*/
   unsigned char PortsStates[6];
   int i=0;
            
      /* Set port variable to 0 */
      memset (PortsSetting, APCI1648_PORT_OUTPUT_DIRECTION, (sizeof (unsigned char) * 6));
      memset (PortsStates, 0, (sizeof (unsigned char) * 6));
      
      /* Set all ports as output */
      if ( ioctl (fd,CMD_APCI1648_InitAllDigitalPorts,PortsSetting) < 0 )
         {
         perror("ioctl");
         exit(1);
         }            
      
     /* Set the digital output memory on.<br> 
      * When digital output memory is activated,<br>
      * the previous output state is hold when one<br>
      * or more output states are set. 
      */      
      if ( ioctl (fd,CMD_APCI1648_SetDigitalOutputMemoryOn,NULL) < 0 )
         {
         perror("ioctl");
         exit(1);
         }	     
         
      printf ("CMD_APCI1648_SetDigitalOutputMemoryOn: OK\n"); 
      printf ("\n");         
      printf ("\n");         

      /* Set all digital output state on */
      for (i=0; i<APCI1648_PORT_NUMBER; i++)
         {      
         printf ("Type the value (0 - 255) to write on port %i:\n", (APCI1648_PORT0+i));
         scanf ("%i", (int*)&PortsStates [(APCI1648_PORT0+i)]);
         }
      			
      if ( ioctl (fd,CMD_APCI1648_SetAllDigitalOutputsOn,PortsStates) < 0 )
         {
         perror("ioctl");
         exit(1);
         }	 
      
      printf ("CMD_APCI1648_SetAllDigitalOutputsOn: OK\n"); 
      printf ("\n");   
      
      if ( ioctl (fd,CMD_APCI1648_GetAllDigitalOutputsStates,PortsStates) < 0 )
         {
         perror("ioctl");
         exit(1);
         }	                

      printf ("CMD_APCI1648_GetAllDigitalOutputsStates: OK\n"); 
      printf ("\n");   
              
      printf ("\n        +-------------------------------+");
      printf ("\n        | DIGITAL OUTPUTS STATES        |");
      printf ("\n+-------+---+---+---+---+---+---+---+---+-----+");
   	  printf ("\n| PORT  | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | Hex |");
				
      for (i=0; i<APCI1648_PORT_NUMBER; i++)
         {
	     printf ("\n+-------+---+---+---+---+---+---+---+---+-----+");              
		 printf ("\n| %3d   |%3s|%3s|%3s|%3s|%3s|%3s|%3s|%3s| %3x |", (APCI1648_PORT0+i),
				     (PortsStates [(APCI1648_PORT0+i)] & 128)?"ON":"OFF",
				     (PortsStates [(APCI1648_PORT0+i)] & 64)?"ON":"OFF",
				     (PortsStates [(APCI1648_PORT0+i)] & 32)?"ON":"OFF",
				     (PortsStates [(APCI1648_PORT0+i)] & 16)?"ON":"OFF",
				     (PortsStates [(APCI1648_PORT0+i)] & 8)?"ON":"OFF",
				     (PortsStates [(APCI1648_PORT0+i)] & 4)?"ON":"OFF",
				     (PortsStates [(APCI1648_PORT0+i)] & 2)?"ON":"OFF",
				     (PortsStates [(APCI1648_PORT0+i)] & 1)?"ON":"OFF",
				     PortsStates [(APCI1648_PORT0+i)]);             	 
         }
      printf ("\n+---------------------------------------------+"); 

      printf ("\n");   
      printf ("\n");            
      
      /* Set all digital output state off */
      for (i=0; i<APCI1648_PORT_NUMBER; i++)
         {      
         printf ("Type the value (0 - 255) to write on port %i:\n", (APCI1648_PORT0+i));
         scanf ("%i", (int*)&PortsStates [(APCI1648_PORT0+i)]);
         }
      			
      if ( ioctl (fd,CMD_APCI1648_SetAllDigitalOutputsOff,PortsStates) < 0 )
         {
         perror("ioctl");
         exit(1);
         }	 

      printf ("CMD_APCI1648_SetAllDigitalOutputsOff: OK\n"); 
      printf ("\n");            
              
      if ( ioctl (fd,CMD_APCI1648_GetAllDigitalOutputsStates,PortsStates) < 0 )
         {
         perror("ioctl");
         exit(1);
         }	                

      printf ("CMD_APCI1648_GetAllDigitalOutputsStates: OK\n"); 
      printf ("\n");   
              
      printf ("\n        +-------------------------------+");
      printf ("\n        | DIGITAL OUTPUTS STATES        |");
      printf ("\n+-------+---+---+---+---+---+---+---+---+-----+");
   	  printf ("\n| PORT  | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | Hex |");
				
      for (i=0; i<APCI1648_PORT_NUMBER; i++)
         {
	     printf ("\n+-------+---+---+---+---+---+---+---+---+-----+");              
		 printf ("\n| %3d   |%3s|%3s|%3s|%3s|%3s|%3s|%3s|%3s| %3x |", (APCI1648_PORT0+i),
				     (PortsStates [(APCI1648_PORT0+i)] & 128)?"ON":"OFF",
				     (PortsStates [(APCI1648_PORT0+i)] & 64)?"ON":"OFF",
				     (PortsStates [(APCI1648_PORT0+i)] & 32)?"ON":"OFF",
				     (PortsStates [(APCI1648_PORT0+i)] & 16)?"ON":"OFF",
				     (PortsStates [(APCI1648_PORT0+i)] & 8)?"ON":"OFF",
				     (PortsStates [(APCI1648_PORT0+i)] & 4)?"ON":"OFF",
				     (PortsStates [(APCI1648_PORT0+i)] & 2)?"ON":"OFF",
				     (PortsStates [(APCI1648_PORT0+i)] & 1)?"ON":"OFF",
				     PortsStates [(APCI1648_PORT0+i)]);             	 
         }
      printf ("\n+---------------------------------------------+"); 

      printf ("\n");   
      printf ("\n");  

      printf ("\n");       
      
      /* Set the digital output memory off */
      if ( ioctl (fd,CMD_APCI1648_SetDigitalOutputMemoryOff,NULL) < 0 )
         {
         perror("ioctl");
         exit(1);
         }	     
         
      printf ("CMD_APCI1648_SetDigitalOutputMemoryOff: OK\n"); 
      printf ("\n");         

      return 0;
}

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

void v_ControlExit (int i)
{
	stop=1;
}

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

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

	int * apci1648_card_fd;
	unsigned int apci1648_card_number = 0;
	{
		apci1648_card_number = apci1648_find_cards(&apci1648_card_fd);
		
		printf("total: %d cards\n",apci1648_card_number);
	}
	
    signal(SIGINT,v_ControlExit);  
    stop = 0;
    system("clear");	
			
	{
		int i;
		for (i=0;i< apci1648_card_number; i++)
		{
			printf("\n\nRunning test on device of minor number %d\n\n\n\n",i);
			apci1648_run_test(apci1648_card_fd[i]);		

			printf("\n\nRunning initialization test on device of minor number %d\n\n\n\n",i);
			apci1648_test_initialization (apci1648_card_fd[i]);
			system("clear");
            stop = 0;
            
			printf("\n\nRunning digital input test on device of minor number %d\n\n\n\n",i);
			apci1648_test_digital_input (apci1648_card_fd[i]);
			system("clear");
            stop = 0;
            
			printf("\n\nRunning digital output test on device of minor number %d\n\n\n\n",i);			
			apci1648_test_digital_output (apci1648_card_fd[i]);
		}
	}


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