/** @file sample_apcie2200_input_event.c
 
   This sample demonstartes how to use digital input event in user mode.
   The thread is not necessary, it permits an easier management of events and sample stop.
 
   @par CREATION  
   @author Krauth Julien
   @date   08.01.09
   
   @par VERSION
   @verbatim
   $LastChangedRevision:$
   $LastChangedDate:$
   @endverbatim   
   
   @par LICENCE
   @verbatim
   Copyright (C) 2009  ADDI-DATA GmbH for the source code of this module.
        
   ADDI-DATA GmbH
   Airpark Business Center
   Airport Boulevard B210
   77836 Rheinmuenster
   Germany
   Tel: +49(0)7229/1847-0
   Fax: +49(0)7229/1847-200
   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 <sys/time.h>
#include <signal.h>
#include <errno.h>
#include <assert.h>
#include <pthread.h>

#include <apcie2200.h>
#include <apcie2200-events.h>

// #include "findboards.h"
#include "input.h"

unsigned char ucStopFlag;

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

/**@struct thread_arg_t
 * 
 * This structure is used to transmit some parameters to threads.  
 */
struct thread_arg_t 
{
	int threadnbr;	/**< This is the minor ID of the board */
	int fd;			/**< File descriptor of the board */ 
}; 

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

/** Exit callback function.
 *
 * This function is called on Ctrl+c key combination.
 * It set the stop flag status.
 */
static void v_ControlExit (int notused)
{
      ucStopFlag = 1;
      printf("Sample abort.\n");
}

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

void * fn_thread (void * arg)
{
	struct thread_arg_t *thread_args = (struct thread_arg_t *) arg;
	
	printf("Dig IO events\n");
	{
		printf("CMD_APCIE2200_ClearAndStopDigitalInputInterrupt\n");
		if (ioctl (thread_args->fd, CMD_APCIE2200_ClearAndStopDigitalInputInterrupt, NULL) != 0)
			perror("ioctl");

		{
			struct
			{
				int InterruptLogic;
				uint32_t InterruptMaskMode1;
				uint32_t InterruptMaskMode2;
			} params;

			printf ("\n\nFor each channels you have to set 2 masks that defined\n");
			printf ("the way to detect a change the digital input.\n\n");

			printf ("Sample 1\n");
			printf ("If mask 1 = 65534 and mask 2 = 0 (16 Input),\n");
			printf ("If mask 1 = 254 and mask 2 = 0 (8 Input),\n");
			printf ("an event is generated once a rising edge is detected on one of the 15 inputs\n");
			printf ("In binary mask 1 looks like: Input 15 -> 1111 1111 1111 1110 <- Input 0\n\n");

			printf ("Sample 2\n");
			printf ("If mask 1 = 2 and mask 2 = 4,\n");
			printf ("an event is generated once a rising edge is detected on input 1 or\n");
			printf ("a falling edge on input 2.\n\n");

			printf ("Input 0 can't generate event!.\n\n");

			printf ("+-----------+---------+-------------+--------------+------------+\n");
			printf ("| Mask 1    |    0    |      1      |      0       |      1     |\n");
			printf ("+-----------+---------+-------------+--------------+------------+\n");
			printf ("| Mask 2    |    0    |      0      |      1       |      1     |\n");
			printf ("+-----------+---------+-------------+--------------+------------+\n");
			printf ("| OR LOGIC  | DISABLE | RISING EDGE | FALLING EDGE | BOTH EDGES |\n");
			printf ("+-----------+---------+-------------+--------------+------------+\n");

			params.InterruptLogic = 0; /* Only OR (0) logic is supported by the APCIE-2200 */

			printf ("Set mask 1:\n");
			params.InterruptMaskMode1 = get_integer (0, 0xFFFE);

			printf ("Set mask 2:\n");
			params.InterruptMaskMode2 = get_integer (0, 0xFFFE);

			printf("CMD_APCIE2200_InitAndStartDigitalInputInterrupt\n");
			if (ioctl (thread_args->fd,CMD_APCIE2200_InitAndStartDigitalInputInterrupt,&params) != 0)
				perror("ioctl");

			printf("\n");
		}

		printf("CMD_APCIE2200_GetDigitalInputInterruptConfig\n");
		{
			struct
			{
				int Enabled;
				int InterruptLogic;
				uint32_t InterruptMaskMode1;
				uint32_t InterruptMaskMode2;
			} params;

			if (ioctl (thread_args->fd,CMD_APCIE2200_GetDigitalInputInterruptConfig,&params) != 0)
				perror("ioctl");

			printf("Enabled: %X\n",params.Enabled);
			printf("InterruptLogic: %X\n",params.InterruptLogic);
			printf("InterruptMaskMode1: %X\n",params.InterruptMaskMode1);
			printf("InterruptMaskMode2: %X\n",params.InterruptMaskMode2);
			printf("\n");
		}

		/* Empty the event buffer */
		printf("CMD_APCIE2200_EventEmpty\n");
		if (ioctl (thread_args->fd,CMD_APCIE2200_EventEmpty,NULL) != 0)
			perror("ioctl");

		printf("\n");

		/* Put the thread in sleep state until an event is catched */
		printf("Put the thread in sleep state until an event is catched.\n");

		{
			apcie2200_event_t evt;

                        while (ucStopFlag == 0)
			{
				printf("CMD_APCIE2200_EventWait\n");
				if (ioctl (thread_args->fd,CMD_APCIE2200_EventWait, &evt) != 0)
					perror("ioctl");

				printf("source: %d param1: 0x%lX\n", evt.source, evt.param1);
				printf("\n");
			}
		}
	}
	pthread_exit(NULL);
}

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

int main(int argc, char** argv)
{
	int ret = 0;
	int i = 0;
	int nbr_boards = 1; // only 1 Board is tested
	pthread_t *thread = NULL;
	struct thread_arg_t *thread_args = NULL;

	int apcie2200_card_fd;

	if (argc < 2 )
	{
            printf("Not enough arguments.\n");
            printf("Usage: %s /dev/apcie2200_[0..15]\n", argv[0]);
            exit(1);
	}

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

	{
		struct stat file_stat;
		if ( fstat(apcie2200_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));
	}

        ucStopFlag=0;
	
	thread = malloc (sizeof (pthread_t) * nbr_boards);
	if (thread == NULL)
	{
		printf("Can't allocate memory for thread\n");
		exit(EXIT_FAILURE);
	}

	thread_args = malloc (sizeof (struct thread_arg_t) * nbr_boards);	
	if (thread_args == NULL) 
	{
		printf("Can't allocate memory for thread_args\n");

		exit(EXIT_FAILURE);
	}	



	/* Set the v_ControlExit callback function on Ctrl+c key combination.
	 * Once Ctrl+C are pressed, the v_ControlExit function is called to end the 
	 * sample.
	 */
	signal (SIGINT, v_ControlExit);	

	for (i=0; i<nbr_boards; i++)
    {

		thread_args[i].threadnbr = i;	/**< This is the minor ID of the board */
		thread_args[i].fd = apcie2200_card_fd;		/**< File descriptor of the board */

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

		
		/* Create a thread for each board */
		if ((ret = pthread_create (&thread[i], NULL, fn_thread, &thread_args[i])) != 0)
		{
		   printf("%d : %s", i, strerror(ret));
		   exit(EXIT_FAILURE);
		}

		printf ("Create and start thread for board %d\n", i);

    }

	/* Wait Ctrl+c key combination */
	printf ("TO STOP THE SAMPLE PRESS CTRL+C\n");
	{
                while (ucStopFlag == 0)
		{
                    usleep(100000L ); // 100 ms
		}
		sleep(1);
	}

	/* Stop threads */
    for (i=0; i<nbr_boards; i++) 
    {
		pthread_cancel (thread[i]);
		
		printf("CMD_APCIE2200_ClearAndStopDigitalInputInterrupt\n");
		if (ioctl (thread_args[i].fd, CMD_APCIE2200_ClearAndStopDigitalInputInterrupt, NULL) != 0	)
			perror("ioctl");
    }

    free (thread);
    free (thread_args);
	
	return 0;
}
