/** @file sample_digital_io.c
*
* @author Krauth Julien
*
* This software 
* - scans the /proc/sys/apci3120/ directory 
* - run a serie of test on each detected boards
*/

/** @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 <apci3120.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/apci3120/ 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 apci3120_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};

	/* Test which node is availabe */
	{
		struct stat file_stat;
		const char PROC_PATH[]="/proc/sys/apci3120";
		const char DEV_PATH[]="/dev/apci3120/";

		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 apci3120_run_test(int fd)
{

	/* Test digital inputs */
	printf("=> CMD_APCI3120_Read4DigitalInput\n");
	{
		unsigned char arg = 0;
		int error = 0;

		error = ioctl (fd, CMD_APCI3120_Read4DigitalInput, &arg);

		switch (error)
		{			
			case 0: printf ("Channel0 = %i Channel1 = %i Channel2 = %i Channel3 = %i.\n", (arg&1), ((arg&2)>>1), ((arg&4)>>2), ((arg&8)>>3));
			break;

			case 1: printf ("The handle parameter of the board is wrong.\n");
			break;

			default:perror("ioctl");
			break;
		}
	}

	/* Test digital outputs */
	printf("=> CMD_APCI3120_SetOutputMemoryOn\n");
	{
		int error = 0;

		error = ioctl (fd, CMD_APCI3120_SetOutputMemoryOn);

		switch (error)
		{			
			case 0:
			break;

			case 1: printf ("The handle parameter of the board is wrong.\n");
			break;

			default:perror("ioctl");
			break;
		}
	}

	usleep(250000);
	printf("=> CMD_APCI3120_Set4DigitalOutputOn\n");
	{
		unsigned char arg = 1;
		int error = 0;

		error = ioctl (fd, CMD_APCI3120_Set4DigitalOutputOn, arg);

		switch (error)
		{			
			case 0: printf ("Set the digital output 0 on.\n");
			break;

			case 1: printf ("The handle parameter of the board is wrong.\n");
			break;

			default:perror("ioctl");
			break;
		}
	}

	usleep(250000);
	printf("=> CMD_APCI3120_Set4DigitalOutputOn\n");
	{
		unsigned char arg = 2;
		int error = 0;

		error = ioctl (fd, CMD_APCI3120_Set4DigitalOutputOn, arg);

		switch (error)
		{			
			case 0: printf ("Set the digital output 1 on.\n");
			break;

			case 1: printf ("The handle parameter of the board is wrong.\n");
			break;

			default:perror("ioctl");
			break;
		}
	}

	usleep(250000);
	printf("=> CMD_APCI3120_Set4DigitalOutputOn\n");
	{
		unsigned char arg = 4;
		int error = 0;

		error = ioctl (fd, CMD_APCI3120_Set4DigitalOutputOn, arg);

		switch (error)
		{			
			case 0: printf ("Set the digital output 2 on.\n");
			break;

			case 1: printf ("The handle parameter of the board is wrong.\n");
			break;

			default:perror("ioctl");
			break;
		}
	}

	usleep(250000);
	printf("=> CMD_APCI3120_Set4DigitalOutputOn\n");
	{
		unsigned char arg = 8;
		int error = 0;

		error = ioctl (fd, CMD_APCI3120_Set4DigitalOutputOn, arg);

		switch (error)
		{			
			case 0: printf ("Set the digital output 3 on.\n");
			break;

			case 1: printf ("The handle parameter of the board is wrong.\n");
			break;

			default:perror("ioctl");
			break;
		}
	}

	usleep(250000);
	printf("=> CMD_APCI3120_Set4DigitalOutputOff\n");
	{
		unsigned char arg = 1;
		int error = 0;

		error = ioctl (fd, CMD_APCI3120_Set4DigitalOutputOff, arg);

		switch (error)
		{			
			case 0: printf ("Set the digital output 0 off.\n");
			break;

			case 1: printf ("The handle parameter of the board is wrong.\n");
			break;

			default:perror("ioctl");
			break;
		}
	}

	usleep(250000);
	printf("=> CMD_APCI3120_Set4DigitalOutputOff\n");
	{
		unsigned char arg = 2;
		int error = 0;

		error = ioctl (fd, CMD_APCI3120_Set4DigitalOutputOff, arg);

		switch (error)
		{			
			case 0: printf ("Set the digital output 1 off.\n");
			break;

			case 1: printf ("The handle parameter of the board is wrong.\n");
			break;

			default:perror("ioctl");
			break;
		}
	}

	usleep(250000);
	printf("=> CMD_APCI3120_Set4DigitalOutputOff\n");
	{
		unsigned char arg = 4;
		int error = 0;

		error = ioctl (fd, CMD_APCI3120_Set4DigitalOutputOff, arg);

		switch (error)
		{			
			case 0: printf ("Set the digital output 2 off.\n");
			break;

			case 1: printf ("The handle parameter of the board is wrong.\n");
			break;

			default:perror("ioctl");
			break;
		}
	}

	usleep(250000);
	printf("=> CMD_APCI3120_Set4DigitalOutputOff\n");
	{
		unsigned char arg = 8;
		int error = 0;

		error = ioctl (fd, CMD_APCI3120_Set4DigitalOutputOff, arg);

		switch (error)
		{			
			case 0: printf ("Set the digital output 3 off.\n");
			break;

			case 1: printf ("The handle parameter of the board is wrong.\n");
			break;

			default:perror("ioctl");
			break;
		}
	}


	printf("=> CMD_APCI3120_SetOutputMemoryOff\n");
	{
		int error = 0;

		error = ioctl (fd, CMD_APCI3120_SetOutputMemoryOff);

		switch (error)
		{			
			case 0:
			break;

			case 1: printf ("The handle parameter of the board is wrong.\n");
			break;

			default:perror("ioctl");
			break;
		}
	}

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

	int * apci3120_card_fd;
	unsigned int apci3120_card_number = 0;
	{
		apci3120_card_number = apci3120_find_cards(&apci3120_card_fd);
		
		printf("total: %d cards\n",apci3120_card_number);
	}	
	
	{
		int i;
		for (i=0;i< apci3120_card_number; i++)
		{
			printf("\n+++++++++++++++++++++++++++++++++++++\nrunning test on device of minor number %d\n+++++++++++++++++++++++++++++++++++++\n\n\n",i);
			apci3120_run_test(apci3120_card_fd[i]);	
		}
	}

	

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