/** @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 <linux/module.h>
#include <linux/version.h>
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
	#include <linux/config.h>
#else
	#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,33)
		#include <linux/autoconf.h>
	#else
		#include <generated/autoconf.h>
	#endif
#endif

#include <linux/version.h>
#include <linux/pci.h>
#include <linux/init.h>
#include <linux/sched.h>

#include <apci.h>
#include <apci3600.h>
#include <apci3600-kapi.h>

//#include "../apci3600-private.h"

#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0)
#include <linux/posix_types.h>
#include <linux/time.h>
#endif

MODULE_LICENSE ("GPL");
MODULE_AUTHOR ("ADDI-DATA GmbH <info@addi-data.com>");
MODULE_DESCRIPTION ("APCI-3600 IOCTL driver sample08");

/*minor of the board*/
static int board_minor=0;

/*can be override*/
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)  
	MODULE_PARM (board_minor,"i");
	EXPORT_NO_SYMBOLS;
#else
	module_param(board_minor, int, S_IRUGO);
#endif

//BEGIN BF151
static DECLARE_WAIT_QUEUE_HEAD(wait_interrupt);
//END BF151

/*************************************************************/
/* Global structure which is called by the interrupt routine */
/*************************************************************/
typedef struct
{
	uint8_t   b_ReceiveNewInterrupt;
	uint8_t   b_ReceiveInterrupt;
	uint8_t   b_FIFOErrorInterrupt;
}
str_Sample08_UserStruct;

static str_Sample08_UserStruct s_Sample08_GlobalUserStruct;
static str_Sample08_UserStruct *ps_Sample08_GlobalUserStructApp = &s_Sample08_GlobalUserStruct;


/*Function Name : v_InterruptRoutine ()*/
/*+----------------------------------------------------------------------------+*/
/*| Function Name : v_InterruptRoutine ()                                      |*/
/*+----------------------------------------------------------------------------+*/
/*| Task :                                                                     |*/
/*  Initialise a board                                                          */
/*+----------------------------------------------------------------------------+*/
/*| Input Parameters :                                                         |*/
/*                                                                              */
/*+----------------------------------------------------------------------------+*/
/*| Output Parameters :                                                        |*/
/*+----------------------------------------------------------------------------+*/
/*| Return Value :                                                             |*/
/*         > 0 : Warning                                                        */
/*         0 : No Error                                                         */
/*        < 0 : Error                                                           */
/*+----------------------------------------------------------------------------+*/
static void v_Sample08_InterruptRoutine  (struct pci_dev *pdev,
									 uint32_t   dw_InterruptSource,
									 uint32_t   dw_InterruptMask,
									 uint32_t   dw_NbrOfComputerBuffer,
									 uint32_t*  pdw_ComputerBufferAddressKernelLevelArray,
									 uint32_t*  pdw_ComputerBufferAddressApplicLevelArray)
{
	/*******************************/
	/* Local variable declaration  */
	/*******************************/
	str_Sample08_UserStruct *ps_UserStruct;

	ps_UserStruct = ps_Sample08_GlobalUserStructApp;

	/*************************/
	/* Test if DAC Interrupt */
	/*************************/
	if (dw_InterruptSource < 2)
	{
		/****************************/
		/* Test if FIFO Error Occur */
		/****************************/
		if (dw_InterruptMask & 2) {
			ps_UserStruct->b_FIFOErrorInterrupt = ps_UserStruct->b_FIFOErrorInterrupt + 1;

		}
		ps_UserStruct->b_ReceiveInterrupt = ps_UserStruct->b_ReceiveInterrupt + 1;

		ps_UserStruct->b_ReceiveNewInterrupt = 1;
		//BEGIN BF151
		wake_up_interruptible(&wait_interrupt);
		//END BF15
				
	
	}
}


static int sample08 (struct pci_dev *pdev)
{ // Uses 1 Analog Output in free Mode

	int i_ReturnValue = 0;
	uint8_t b_Channel = 0;
	uint32_t dw_ComputerBufferSize = 0;
	uint32_t dw_NbrOfFreeValues = 0;
	uint32_t dw_Cpt = 0;
	uint32_t dw_BuffCpt = 0;
	uint16_t pw_Values[100];
	uint32_t dw_NbrOfValues = 0;
	uint32_t dw_NbrOfWrittenValues = 0;
	uint32_t dw_OnBoardBufferSize = 0;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
	struct timespec64 time_Current;
	struct timespec64 time_Begin;
#else
	struct timeval time_Current;
	struct timeval time_Begin;
#endif



	printk("Uses 1 Analog Output in free Mode\n");


	/* Initialise the board */
	i_ReturnValue = i_PCI3600_InitBoard (pdev,
										 PCI3600_MASTER_MODE,
										 PCI3600_DIVISOR_4,
										 PCI3600_DIVISOR_4,
										 PCI3600_EXT_CLOCK_1,
										 PCI3600_EXT_CLOCK_1,
										 0,
										 0,
										 PCI3600_CHRONOMETER_DIVISOR_1);

	if (i_ReturnValue == 0)
	{
		printk ("i_PCI3600_InitBoard OK\n");

		i_ReturnValue = i_PCI3600_SetBoardIntKernelRoutine (pdev,v_Sample08_InterruptRoutine);

		if (i_ReturnValue == 0) {
			printk ("i_PCI3600_SetBoardIntKernelRoutine OK\n");

			/********************************/
			/* Initialise the analog output */
			/********************************/
			i_ReturnValue = i_PCI3600_InitAnalogOutChannel (pdev,
							b_Channel,
							PCI3600_SAMPLING_CLOCK1,
							PCI3600_SINGLE_SPEED);

			if (i_ReturnValue == 0) {
				printk ("i_PCI3600_InitAnalogOutChannel OK\n");


				// Enter the size of the computer buffer you want to use (in nbr of value) :
				dw_ComputerBufferSize = 1024;

				/*******************************/
				/* Reserve the Computer Buffer */
				/*******************************/
				i_ReturnValue = i_PCI3600_ReserveAnalogOutSignalGeneratorComputerBuffer (pdev,
								b_Channel,
								dw_ComputerBufferSize);

				if (i_ReturnValue == 0) {
					printk ("i_PCI3600_ReserveAnalogOutSignalGeneratorComputerBuffer OK, Computer Buffer Size : %u\n",dw_ComputerBufferSize);

					dw_NbrOfFreeValues = 100;


					for (dw_Cpt = 0; dw_Cpt < dw_ComputerBufferSize;dw_Cpt = dw_Cpt + 100) {
						if (dw_NbrOfFreeValues > 100) {
							dw_NbrOfFreeValues = 100;

						}
						/***********************/
						/* Complete the buffer */
						/***********************/
						for (dw_BuffCpt = 0; dw_BuffCpt < dw_NbrOfFreeValues;dw_BuffCpt++) {
							pw_Values [dw_BuffCpt] = (dw_Cpt + dw_BuffCpt) % 0x10000UL;
						}

						if ((dw_Cpt + 100) > dw_ComputerBufferSize) {
							dw_NbrOfValues = dw_ComputerBufferSize - dw_Cpt;

						} else {
							dw_NbrOfValues = 100;

						}
						/*******************************************/
						/* Write the values in the Computer Buffer */
						/*******************************************/
						i_ReturnValue = i_PCI3600_WriteAnalogOutSignalGeneratorComputerRAM (pdev,
										b_Channel,
										dw_NbrOfValues,
										pw_Values,
										&dw_NbrOfWrittenValues,
										&dw_NbrOfFreeValues);

						if (i_ReturnValue == 0) {
							printk ("i_PCI3600_WriteAnalogOutSignalGeneratorComputerRAM OK : nbr of free value : %u\n",dw_NbrOfFreeValues);

							if (dw_NbrOfFreeValues == 0) {
								break;

							}
						} else {
							printk ("i_PCI3600_WriteAnalogOutSignalGeneratorComputerRAM Error : %d\n",i_ReturnValue);
							break;

						}

					}



					if (i_ReturnValue == 0) {
						//                  Enter the nbr of bytes (n) of the on board RAM You want to use (1kB*2^n):
						//                  For example :
						//                  n = 0  ->    1 KB ->       256 Values
						//                  n = 1  ->    2 KB ->       512 Values
						//                  n = 2  ->    4 KB ->     1 024 Values
						//                  n = 11 ->    2 MB ->   524 288 Values
						//                  n = 12 ->    4 MB -> 1 048 576 Values
						//                  n = 13 ->    8 MB -> 2 097 152 Values

						dw_OnBoardBufferSize = 2;

						/*****************************/
						/* Init the signal generator */
						/*****************************/
						i_ReturnValue = i_PCI3600_InitAnalogOutSignalGenerator (pdev,
										b_Channel,
										PCI3600_FREE_RUN_MODE,
										dw_OnBoardBufferSize,
										0);

						if (i_ReturnValue == 0) {
							printk ("i_PCI3600_InitAnalogOutSignalGenerator OK Buffer Size (1kB*2^n): n:%u\n",dw_OnBoardBufferSize);

							/********************************************/
							/* Start the Analog Output Signal Generator */
							/********************************************/
							i_ReturnValue = i_PCI3600_StartAnalogOutSignalGenerator (pdev,
											b_Channel);

							if (i_ReturnValue == 0) {
								unsigned long timeout= 20*HZ;
								printk ("i_PCI3600_StartAnalogOutSignalGenerator OK\n");

								printk ("Wait 20 Seconds....\n");

								memset (&time_Begin, 0, sizeof (time_Begin));
								memset (&time_Current, 0, sizeof (time_Current));
								#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
									ktime_get_real_ts64(&time_Begin);
								#else
									do_gettimeofday (&time_Begin);
								#endif
								for(;;) {
									//BEGIN BF151
									#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,10)
										interruptible_sleep_on_timeout(&wait_interrupt,timeout);
									#else
										wait_event_interruptible_timeout(wait_interrupt, 0, timeout);
									#endif

									#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
										ktime_get_real_ts64(&time_Current);
									#else
										do_gettimeofday (&time_Current);
									#endif
									timeout-=time_Current.tv_sec-time_Begin.tv_sec;

									/*wait_event (wait_interrupt, ps_Sample08_GlobalUserStructApp->b_ReceiveNewInterrupt||((time_Begin.tv_sec + 20) < time_Current.tv_sec ));
									*/
									//END BF151

									#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
										ktime_get_real_ts64(&time_Current);
									#else
										do_gettimeofday (&time_Current);
									#endif
									if ( (time_Begin.tv_sec + 20) < time_Current.tv_sec )
										break;

									if (ps_Sample08_GlobalUserStructApp->b_ReceiveNewInterrupt == 1) {
										ps_Sample08_GlobalUserStructApp ->b_ReceiveNewInterrupt = 0;

										printk("Interrupt occur, IRQ Cpt : %hu FIFO Error IRQ Cpt : %hu\n",ps_Sample08_GlobalUserStructApp ->b_ReceiveInterrupt,ps_Sample08_GlobalUserStructApp ->b_FIFOErrorInterrupt);

									}


								}


								/*******************************************/
								/* Stop the analog output signal generator */
								/*******************************************/
								i_ReturnValue = i_PCI3600_StopAnalogOutSignalGenerator (pdev,
												b_Channel);

								if (i_ReturnValue == 0) {
									printk ("i_PCI3600_StopAnalogOutSignalGenerator OK\n");

								} else {
									printk ("i_PCI3600_StopAnalogOutSignalGenerator Error : %d\n",i_ReturnValue);

								}
							} else {
								printk ("i_PCI3600_StartAnalogOutSignalGenerator Error : %d\n",i_ReturnValue);

							}
							/********************************/
							/* Release the signal generator */
							/********************************/
							i_ReturnValue = i_PCI3600_ReleaseAnalogOutSignalGenerator (pdev,b_Channel);

							if (i_ReturnValue == 0) {
								printk ("i_PCI3600_ReleaseAnalogOutSignalGenerator OK\n");

							} else {
								printk ("i_PCI3600_ReleaseAnalogOutSignalGenerator Error : %d\n",i_ReturnValue);

							}
						} else {
							printk ("i_PCI3600_InitAnalogOutSignalGenerator Error : %d\n",i_ReturnValue);

						}
					}

					/****************************/
					/* Free the computer buffer */
					/****************************/
					i_ReturnValue = i_PCI3600_FreeAnalogOutSignalGeneratorComputerBuffer (pdev,
									b_Channel);

					if (i_ReturnValue == 0) {
						printk ("i_PCI3600_FreeAnalogOutSignalGeneratorComputerBuffer OK\n");

					} else {
						printk ("i_PCI3600_FreeAnalogOutSignalGeneratorComputerBuffer Error : %d\n",i_ReturnValue);

					}
				} else {
					printk ("i_PCI3600_ReserveAnalogOutSignalGeneratorComputerBuffer Error : %d\n",i_ReturnValue);

				}

				/*************************************/
				/* Release the analog output channel */
				/*************************************/
				i_ReturnValue = i_PCI3600_ReleaseAnalogOutChannel (pdev, 0);

				if (i_ReturnValue == 0) {
					printk ("i_PCI3600_ReleaseAnalogOutChannel OK\n");

				} else {
					printk ("i_PCI3600_ReleaseAnalogOutChannel Error : %d\n",i_ReturnValue);

				}

			} else {
				printk ("i_PCI3600_InitAnalogOutChannel Error : %d\n",i_ReturnValue);
			}

			/*********************************/
			/* Release the interrupt routine */
			/*********************************/
			i_ReturnValue = i_PCI3600_ResetBoardIntKernelRoutine (pdev);

			if (i_ReturnValue == 0) {
				printk ("i_PCI3600_ResetBoardIntKernelRoutine OK\n");
			} else {
				printk ("i_PCI3600_ResetBoardIntKernelRoutine Error : %d\n",i_ReturnValue);
			}
		} else {
			printk("i_PCI3600_SetBoardIntKernelRoutine Error : %d\n",i_ReturnValue);
		}
	} else
	{
		printk ("i_PCI3600_InitBoard Error : %d\n",i_ReturnValue);
	}

	return (i_ReturnValue);


}


//------------------------------------------------------------------------------
/** Called when module loads. */
static int __init
apci3600_sample08_init (void)
{

	printk("Using board minor %d (override with board_minor parameter)\n",board_minor);
	sample08(apci3600_lookup_board_by_index(board_minor));
	printk("Succesfull end of the sample, you can unload it...\n");

	return 0;

}

//-------------------------------------------------------------------
/** Called when module is unloaded. */
static void __exit
apci3600_sample08_exit (void)
{



}


//------------------------------------------------------------------------------
module_exit (apci3600_sample08_exit);

module_init (apci3600_sample08_init);

