/** @file sample_apci2200.c
*
* @author Krauth Julien
*
* This software
* - scans the /proc/sys/apci2200/ 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 <apci2200.h>


/* Please set here the number of
 * digital outputs of your APCI-2200 */
#define NUMBER_OF_DIGITAL_OUTPUTS	16

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

		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");
				return(1);
			}

			{
				struct stat file_stat;
				if ( fstat(fd,&file_stat) )
				{
					perror("fstat");
					return(1);
				}
				if (!S_ISCHR(file_stat.st_mode))
				{
					/* should never happen !*/
					printf("file %d is not a character device\n",i);
					return(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 test_1_on_off(int fd, unsigned char outputmemory)
{
	int i = 0;
	int rc = 0;

	/* Set all digital outputs on one by one */
	printf ("CMD_APCI2200_Set1DigitalOutputOn\n");

	for (i=0; i<NUMBER_OF_DIGITAL_OUTPUTS; i++)
	{
		/* Set the output */
		if ((rc = ioctl (fd, CMD_APCI2200_Set1DigitalOutputOn, i)) < 0)
		{
			perror("ioctl");
		}

		/* Test other errors */
		switch (rc)
		{
			case 0: printf ("Set digital output %i on\n", i);
					usleep (250000);
			break;

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

			case 2: printf ("2: Wrong digital output (0 to 15).");
					return;
			break;
		}
	}

	/* Off functions only available when output memory is used. */
	if (!outputmemory)
		return;

	/* Set all digital outputs off one by one */
	printf ("CMD_APCI2200_Set1DigitalOutputOff\n");

	for (i=0; i<NUMBER_OF_DIGITAL_OUTPUTS; i++)
	{
		/* Set the output */
		if ((rc = ioctl (fd, CMD_APCI2200_Set1DigitalOutputOff, i)) < 0)
		{
			perror("ioctl");
		}

		/* Test other errors */
		switch (rc)
		{
			case 0: printf ("Set digital output %i off\n", i);
					usleep (250000);
			break;

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

			case 2: printf ("2: Digital output memory is not activated.");
					return;
			break;

			case 3: printf ("3: Wrong digital output (0 to 15).");
					return;
			break;
		}
	}
}

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

void test_8_on_off(int fd, unsigned char outputmemory)
{
	int rc = 0;
	int i = 0;
	int j = 0;
	uint8_t ptmp[2] = {0, 0};

	/*
	 * Set all digital outputs on one by one.
	 * This function works on port.
	 * A port includes 8 digital outputs.
	 */
	printf ("CMD_APCI2200_Set8DigitalOutputOn\n");

	for (i=0; i<(NUMBER_OF_DIGITAL_OUTPUTS/8); i++)
	{
		/* This is the port to manage (0 or 1) */
		ptmp[0] = i;

		for (j=0; j<8; j++)
		{
			/* This is the value to write on the port */
			ptmp[1] = (1<<j);

			/* Set the output value */
			if ((rc = ioctl (fd, CMD_APCI2200_Set8DigitalOutputOn, ptmp)) < 0)
			{
				perror("ioctl");
			}

			/* Test other errors */
			switch (rc)
			{
				case 0: printf ("Set the %i value on outputs from port %i\n", ptmp[1],ptmp[0]);
						usleep (250000);
				break;

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

				case 2: printf ("2: The port parameter of the board is wrong (has to be 0: Port 1, 1: Port 2).");
						return;
				break;
			}
		}
	}

	/* Off functions only available when output memory is used. */
	if (!outputmemory)
		return;

	/*
	 * Set all digital outputs off one by one.
	 * This function works on port.
	 * A port includes 8 digital outputs.
	 */
	printf ("CMD_APCI2200_Set8DigitalOutputOff\n");

	for (i=0; i<(NUMBER_OF_DIGITAL_OUTPUTS/8); i++)
	{
		/* This is the port to manage (0 or 1) */
		ptmp[0] = i;

		for (j=0; j<8; j++)
		{
			/* This is the value to write on the port */
			ptmp[1] = (1<<j);

			/* Set the output value */
			if ((rc = ioctl (fd, CMD_APCI2200_Set8DigitalOutputOff, ptmp)) < 0)
			{
				perror("ioctl");
			}

			/* Test other errors */
			switch (rc)
			{
				case 0: printf ("Set the %i value on outputs from port %i\n", ptmp[1],ptmp[0]);
						usleep (250000);
				break;

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

				case 2: printf ("2: The port parameter of the board is wrong (has to be 0: Port 1, 1: Port 2).");
						return;
				break;
			}
		}
	}
}

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

void test_16_on_off(int fd, unsigned char outputmemory)
{
	int i = 0;
	int rc = 0;

	/* Set all digital outputs on one by one */
	printf ("CMD_APCI2200_Set16DigitalOutputOn\n");

	for (i=0; i<NUMBER_OF_DIGITAL_OUTPUTS; i++)
	{
		/* Set the output */
		if ((rc = ioctl (fd, CMD_APCI2200_Set16DigitalOutputOn, (1<<i))) < 0)
		{
			perror("ioctl");
		}

		/* Test other errors */
		switch (rc)
		{
			case 0: printf ("Set output %i value\n", (1<<i));
					usleep (250000);
			break;

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

	/* Off functions only available when output memory is used. */
	if (!outputmemory)
		return;

	/* Set all digital outputs off one by one */
	printf ("CMD_APCI2200_Set16DigitalOutputOff\n");

	for (i=0; i<NUMBER_OF_DIGITAL_OUTPUTS; i++)
	{
		/* Set the output */
		if ((rc = ioctl (fd, CMD_APCI2200_Set16DigitalOutputOff, i)) < 0)
		{
			perror("ioctl");
		}

		/* Test other errors */
		switch (rc)
		{
			case 0: printf ("Set output %i value\n", (1<<i));
					usleep (250000);
			break;

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

			case 2: printf ("2: Digital output memory is not activated.");
					return;
			break;
		}
	}
}

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

void test_watchdog (int fd)
{
	int i = 0;
	int rc = 0;
	uint8_t tmp = 0;
	uint16_t wtmp = 0;

	if (ioctl (fd, CMD_APCI2200_InitWatchdog, 100))
	{
		perror("ioctl");
	}

	/* The step is 20ms, 100 x 20 = 2000ms */
	printf ("CMD_APCI2200_InitWatchdog (on 2 seconds)\n");
	if ((rc = ioctl (fd, CMD_APCI2200_InitWatchdog, 100)) < 0)
	{
		perror("ioctl");
	}

	/* Test other errors */
	switch (rc)
	{
		case 0:
		break;

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

		case 2: printf ("2: Wrong watchdog delay value (has to be 1 to 255).");
				return;
		break;
	}

	printf ("CMD_APCI2200_StartWatchdog\n");
	if ((rc = ioctl (fd, CMD_APCI2200_StartWatchdog)) < 0)
	{
		perror("ioctl");
	}

	/* Test other errors */
	switch (rc)
	{
		case 0:
		break;

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

		case 2: printf ("2: Watchdog already started");
				return;
		break;
	}

	ioctl (fd, CMD_APCI2200_GetWatchdogSoftwareTriggerStatus, &tmp);

	printf ("CMD_APCI2200_GetWatchdogStatus\n");
	if ((rc = ioctl (fd, CMD_APCI2200_GetWatchdogStatus, &tmp)) < 0)
	{
		perror("ioctl");
	}

	/* Test other errors */
	switch (rc)
	{
		case 0: printf (" Watchdog status = %i\n", tmp);
		break;

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

		case 2: printf ("2: Watchdog not started");
				return;
		break;
	}

	printf ("CMD_APCI2200_GetWatchdogSoftwareTriggerStatus\n");
	if ((rc = ioctl (fd, CMD_APCI2200_GetWatchdogSoftwareTriggerStatus, &tmp)) < 0)
	{
		perror("ioctl");
	}

	/* Test other errors */
	switch (rc)
	{
		case 0: printf (" Software trigger status = %i\n", tmp);
		break;

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

		case 2: printf ("2: Watchdog not started");
				return;
		break;
	}

	for (i=0; i<4; i++)
	{
		printf ("CMD_APCI2200_TriggerWatchdog\n");
		if ((rc = ioctl (fd, CMD_APCI2200_TriggerWatchdog)) < 0)
		{
			perror("ioctl");
		}

		/* Test other errors */
		switch (rc)
		{
			case 0: /* Wait one second before the next trigger */
					sleep (1);
			break;

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

			case 2: printf ("2: Watchdog not started");
					return;
			break;
		}
	}

	printf ("CMD_APCI2200_Set16DigitalOutputOn\n");
	if ((rc = ioctl (fd, CMD_APCI2200_Set16DigitalOutputOn, 65535)) < 0)
	{
		perror("ioctl");
	}

	/* Test other errors */
	switch (rc)
	{
		case 0: printf (" Set all output on\n");
		break;

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

		case 2: printf ("2: Digital output memory is not activated.");
				return;
		break;
	}

	printf ("CMD_APCI2200_Read16DigitalOutput\n");
	if ((rc = ioctl (fd, CMD_APCI2200_Read16DigitalOutput, &wtmp)) < 0)
	{
		perror("ioctl");
	}

	/* Test other errors */
	switch (rc)
	{
		case 0: printf (" Digital outputs value before the watchdog occured = %i\n", wtmp);
		break;

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

	/* Wait 3 seconds so that the watchdog occured */
	sleep (3);

	printf ("CMD_APCI2200_GetWatchdogStatus\n");
	if ((rc = ioctl (fd, CMD_APCI2200_GetWatchdogStatus, &tmp)) < 0)
	{
		perror("ioctl");
	}

	/* Test other errors */
	switch (rc)
	{
		case 0: printf (" Watchdog status = %i\n", tmp);
		break;

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

		case 2: printf ("2: Watchdog not started");
				return;
		break;
	}

	printf ("CMD_APCI2200_Read16DigitalOutput\n");
	if ((rc = ioctl (fd, CMD_APCI2200_Read16DigitalOutput, &wtmp)) < 0)
	{
		perror("ioctl");
	}

	/* Test other errors */
	switch (rc)
	{
		case 0: printf (" Digital outputs value after the watchdog occured = %i\n", wtmp);
		break;

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

	printf ("CMD_APCI2200_StopWatchdog\n");
	if ((rc = ioctl (fd, CMD_APCI2200_StopWatchdog)) < 0)
	{
		perror("ioctl");
	}

	/* Test other errors */
	switch (rc)
	{
		case 0:
		break;

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

		case 2: printf ("2: Watchdog not started");
				return;
		break;
	}
}

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

void apci2200_run_test(int fd)
{
	int i = 0;
	int rc = 0;
	uint8_t tmp = 0;

	/* Read all digital inputs of the APCI-2200 */
	printf ("CMD_APCI2200_Read8DigitalInput\n");
	if (ioctl (fd, CMD_APCI2200_Read8DigitalInput, &tmp))
	{
		perror("ioctl");
	}

	for (i=0; i<8; i++)
	{
		printf ("Channel %i = %s ", i, ((tmp & (1<<i))==(1<<i)?"on":"off"));
	}


	/* The output memory permit to hold the previous
	 * digital output status.
	 * If memory is off:
	 * set digital output 1 on -> outputs value = 1
	 * set digital output 2 on -> outputs value = 2
	 * set digital output 3 on -> outputs value = 4
	 * set digital output 4 on -> outputs value = 8
	 * ...
	 *
	 * If memory is on:
	 * set digital output 1 on -> outputs value = 1
	 * set digital output 2 on -> outputs value = 3
	 * set digital output 3 on -> outputs value = 7
	 * set digital output 4 on -> outputs value = 15
	 * ...
	 *
	 * In memory is off, you can't use
	 * CMD_APCI2200_SetXDigitalOutputOff function!
	 */
	printf ("\nWITH OUTPUT MEMORY\n");

	printf ("CMD_APCI2200_SetDigitalOutputMemoryOn\n");
	if ((rc = ioctl (fd, CMD_APCI2200_SetDigitalOutputMemoryOn)) < 0)
	{
		perror("ioctl");
	}

	/* Test other errors */
	switch (rc)
	{
		case 0: printf ("0: No error.");
		break;

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

		case 2: printf ("2: Output memory is already set on.");
				return;
		break;
	}

	test_1_on_off(fd, 1);
	test_8_on_off(fd, 1);
	test_16_on_off(fd, 1);

	printf ("CMD_APCI2200_SetDigitalOutputMemoryOff\n");
	if ((rc = ioctl (fd, CMD_APCI2200_SetDigitalOutputMemoryOff)) < 0)
	{
		perror("ioctl");
	}

	/* Test other errors */
	switch (rc)
	{
		case 0: printf ("0: No error.");
		break;

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

		case 2: printf ("2: Digital output memory is not activated.");
				return;
		break;
	}

	printf ("WITHOUT OUTPUT MEMORY\n");

	test_1_on_off(fd, 0);
	test_8_on_off(fd, 0);
	test_16_on_off(fd, 0);

	test_watchdog (fd);

}

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

	int apci2200_card_fd;

	if (argc < 2 )
	{
		printf("Usage : %s device_name\n",argv[0]);
		printf("\n\tExample : %s /dev/apci2200_0\n",argv[0]);
		exit(1);
	}

	if ( ( apci2200_card_fd = open(argv[1], O_RDWR)) == -1 )
	{
		perror("open");
		exit(1);
	}

	{
		struct stat file_stat;
		if ( fstat(apci2200_card_fd,&file_stat) )
		{
			perror("fstat");
			exit(1);
		}
		if (!S_ISCHR(file_stat.st_mode))
		{
			/* should never happen !*/
			printf("file %s is not a character device\n",argv[1]);
			printf("failed");
			exit(1);
		}
		printf("%s, major:%d minor:%d \n",argv[1], major(file_stat.st_rdev),minor(file_stat.st_rdev));
	}

	printf("\n+++++++++++++++++++++++++++++++++++++\nrunning test on device %s  \n+++++++++++++++++++++++++++++++++++++\n\n\n",argv[1]);

	/* run test */
	{
		int i;
		for (i=0;i< apci2200_card_fd; i++)
		{
			apci2200_run_test(apci2200_card_fd);
		}
	}

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