/** @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/delay.h>
#include <linux/init.h>
#include <apci.h>
#include <apci3600.h>
#include <apci3600-kapi.h>

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

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

/*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

#define SAMPLE_BUFFER_SIZE 1000
#define SAMPLE_BUFFER_FACTOR 2048

#define SIZE_OF_INTERRUPT_FIFO 500

#define TIME_OUT 60

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


//BEGIN BF151
/*in ms*/
#define SLEEP_PERIOD 10
static DECLARE_WAIT_QUEUE_HEAD(wait_interrupt);
static int interrupt_dac = 1;
//END BF151


/**********************/
/* Struct declaration */
/**********************/
/*************************************************************/
/* Global structure which is called by the interrupt routine */
/*************************************************************/

typedef struct
{
	uint32_t dw_InterruptIndex;
	uint8_t   b_FIFOErrorInterrupt;
	uint8_t   b_SDRAMUnderflowInterrupt;
	uint32_t dw_CompareInterrupt;
	uint8_t   b_ComputerBufferEmptyInterrupt;
}
str_Sample09_InterruptFIFO;


typedef struct
{
	uint32_t dw_ReceiveInterrupt;
	uint8_t  b_Error;
	str_Sample09_InterruptFIFO ps_InterruptFIFO [SIZE_OF_INTERRUPT_FIFO];
	uint32_t dw_ReadInterruptFIFOPointer;
	uint32_t dw_WriteInterruptFIFOPointer;
	uint8_t  b_InterruptFIFOFull;
	uint8_t  b_InterruptFIFOEmpty;
	uint8_t  b_InterruptFIFOOverFlow;
}
str_Sample09_UserStruct;

str_Sample09_UserStruct s_Sample09_GlobalUserStruct;

str_Sample09_UserStruct *ps_Sample09_GlobalUserStructApp = &s_Sample09_GlobalUserStruct;
str_Sample09_UserStruct *ps_Sample09_GlobalUserStructKernel = &s_Sample09_GlobalUserStruct;



/*Function Name : i_WriteNewAnalogOutValueInPCBuffer ()*/
/*+----------------------------------------------------------------------------+*/
/*| Function Name : i_WriteNewAnalogOutValueInPCBuffer ()                   |*/
/*+----------------------------------------------------------------------------+*/
/*| Task :                                                                     |*/
/*  Write New analog output value in the PC Buffer                              */
/*+----------------------------------------------------------------------------+*/
/*| Input Parameters :                                                         |*/
/*                                                                              */
/*+----------------------------------------------------------------------------+*/
/*| Output Parameters :                                                        |*/
/*+----------------------------------------------------------------------------+*/
/*| Return Value :                                                             |*/
/*         0 : No Error                                                         */
/*        -1 : Buffer Full                                                      */
/*        -2 : Write function error                                             */
/*+----------------------------------------------------------------------------+*/
int i_WriteNewAnalogOutValueInPCBuffer  (struct pci_dev *pdev,
		uint8_t    b_Channel,
		uint16_t*   pw_NextValueToWrite,
		uint32_t*  pdw_NbrOfWrittenValues,
		uint32_t*  pdw_NbrOfFreeValues)
{
	uint16_t                    pw_Values[SAMPLE_BUFFER_SIZE];
	uint32_t                   dw_BuffCpt = 0;
	int                     i_ReturnValue = 0;
	uint32_t                   dw_NbrOfValues = 0;

	/***********************/
	/* Complete the buffer */
	/***********************/
	for (dw_BuffCpt = 0; dw_BuffCpt < SAMPLE_BUFFER_SIZE;dw_BuffCpt++)
	{
		pw_Values [dw_BuffCpt] = (uint16_t)((*pw_NextValueToWrite + (dw_BuffCpt * SAMPLE_BUFFER_FACTOR)) % 0x10000UL);

	}
	dw_NbrOfValues = SAMPLE_BUFFER_SIZE;

	/*******************************************/
	/* Write the values in the Computer Buffer */
	/*******************************************/
	i_ReturnValue = i_PCI3600_WriteAnalogOutSignalGeneratorComputerRAM (pdev,
					b_Channel,
					dw_NbrOfValues,
					pw_Values,
					pdw_NbrOfWrittenValues,
					pdw_NbrOfFreeValues);

	if (i_ReturnValue == 0)
	{
		/************************************************/
		/* Define the next value to write in the buffer */
		/************************************************/
		*pw_NextValueToWrite = ((pw_Values [*pdw_NbrOfWrittenValues-1] + SAMPLE_BUFFER_FACTOR) % 0x10000UL);

	} else
	{
		if (i_ReturnValue == -4) {
			i_ReturnValue = -1;

		} else {
			printk ("i_PCI3600_WriteAnalogOutSignalGeneratorComputerRAM error = %d\n",i_ReturnValue);

			i_ReturnValue = -2;

		}
	}
	return (i_ReturnValue);

}
/*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                                                           */
/*+----------------------------------------------------------------------------+*/
void v_Sample09_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_Sample09_UserStruct *ps_UserStruct;

	ps_UserStruct = ps_Sample09_GlobalUserStructApp;

	if (ps_UserStruct -> b_InterruptFIFOFull == 0 &&
			ps_UserStruct -> b_InterruptFIFOOverFlow == 0)
	{
		/*************************/
		/* Test if DAC Interrupt */
		/*************************/
		if (dw_InterruptSource <= 2) {
			//BEGIN BF151
			interrupt_dac = 1;
			//END BF151
			/********************************/
			/* Test Compare Interrupt Occur */
			/********************************/
			if (dw_InterruptMask & 1) {
				ps_UserStruct->
				ps_InterruptFIFO[ps_UserStruct -> dw_WriteInterruptFIFOPointer].
				dw_CompareInterrupt = 1;

			}
			/****************************/
			/* Test if FIFO Error Occur */
			/****************************/
			if (dw_InterruptMask & 2) {
				ps_UserStruct->
				ps_InterruptFIFO[ps_UserStruct -> dw_WriteInterruptFIFOPointer].
				b_FIFOErrorInterrupt = 1;

				ps_UserStruct -> b_Error = 1;

			}
			/*********************************/
			/* Test if SDRAM Underflow occur */
			/*********************************/
			if (dw_InterruptMask & 4) {
				ps_UserStruct->
				ps_InterruptFIFO[ps_UserStruct -> dw_WriteInterruptFIFOPointer].
				b_SDRAMUnderflowInterrupt = 1;

				ps_UserStruct -> b_Error = 1;

			}
			/*************************************************/
			/* Test if Computer buffer empty interrupt occur */
			/*************************************************/
			if (dw_InterruptMask & 8) {
				ps_UserStruct->
				ps_InterruptFIFO[ps_UserStruct -> dw_WriteInterruptFIFOPointer].
				b_ComputerBufferEmptyInterrupt = 1;

				ps_UserStruct -> b_Error = 1;

			}
			ps_UserStruct->
			ps_InterruptFIFO[ps_UserStruct -> dw_WriteInterruptFIFOPointer].
			dw_InterruptIndex = ps_UserStruct-> dw_ReceiveInterrupt + 1;

			ps_UserStruct -> dw_WriteInterruptFIFOPointer = (ps_UserStruct -> dw_WriteInterruptFIFOPointer + 1) % SIZE_OF_INTERRUPT_FIFO;
			ps_UserStruct -> b_InterruptFIFOEmpty = 0;

			if (ps_UserStruct -> dw_WriteInterruptFIFOPointer == ps_UserStruct -> dw_ReadInterruptFIFOPointer) {
				ps_UserStruct -> b_InterruptFIFOFull = 1;

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

		}
		//BEGIN BF151
		else
			interrupt_dac = 0;
		//END BF151
			
	} else
	{
		ps_UserStruct -> b_InterruptFIFOOverFlow = 1;

	}
}


/*Function Name : i_RingBufferManagement()*/
/*+----------------------------------------------------------------------------+*/
/*| Function   Name   : i_RingBufferManagement ()                              |*/
/*+----------------------------------------------------------------------------+*/
/*| Task              : Analog Output Ring Buffer management                   |*/
/*+----------------------------------------------------------------------------+*/
/*| Input Parameters  :                                                        |*/
/*                                                                              */
/*+----------------------------------------------------------------------------+*/
/*| Output Parameters : -                                                      |*/
/*+----------------------------------------------------------------------------+*/
/*| Return Value      :  0 : No error occur                                    |*/
/*|                     -1 : Error occur                                       |*/
/*+----------------------------------------------------------------------------+*/
int i_Sample09_RingBufferManagement (struct pci_dev *pdev,
									   uint8_t  b_Channel,
									   uint16_t  w_NextValueToWrite)
{
	/************************/
	/* Variable declaration */
	/************************/
	CHAR*                   pc_Wait                             = {"|/-\\"};
	uint8_t                    b_Error                             = 0;
	uint16_t                     w_Wait                             = 0;
	uint32_t                   dw_NbrOfWrittenValues = 0;
	uint32_t                   dw_NbrOfFreeValues = 0;
	int                     i_ReturnValue = 0;
	uint8_t                    b_NextLine = 0;
	uint8_t                    b_TimeOut = 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

	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

	do
	{
//BEGIN BF151
	wait_event (wait_interrupt, interrupt_dac);


//END BF151

		/**********************/
		/* Wait the interrupt */
		/**********************/
		printk ("\n");

		do {
			//BEGIN BF151
			mdelay(SLEEP_PERIOD);
			
			/*************************/
			/* Wait interrupt string */
			/*************************/
// 			if ((w_Wait % 8650) == 0) {
// 			
// 				printk ("\rWait interrupt %c", pc_Wait[w_Wait / 8650]);
			//if ((++w_Wait % 4) == 0) {
			
				printk ("\rWait interrupt %c", pc_Wait[w_Wait % 4]);

				//END BF151
			

				/**************************************/
				/* Complete the PC Buffer with values */
				/**************************************/
				i_ReturnValue = i_WriteNewAnalogOutValueInPCBuffer  (pdev,
								b_Channel,
								&w_NextValueToWrite,
								&dw_NbrOfWrittenValues,
								&dw_NbrOfFreeValues);

				if (i_ReturnValue == 0) {
					printk (" Write new value in PC Buffer OK Nbr of Free place :%08u Nbr of value written : %08u\n",dw_NbrOfFreeValues,dw_NbrOfWrittenValues);

					if (b_NextLine == 1) {
						printk("\n");
						b_NextLine = 0;

					}
				} else {
					if (i_ReturnValue == -1) {
						if (b_NextLine == 0) {
							printk(" PC Buffer Full                                                                                          ");
							b_NextLine = 1;

						}
					}
				}
			}
			//BEGIN BF151
			
// 			w_Wait = (w_Wait + 1) % 34600;
// 			do_gettimeofday (&time_Current);
// 			if ( (time_Begin.tv_sec + TIME_OUT) < time_Current.tv_sec )
// 				b_TimeOut = 1;

			if ((++w_Wait % 4) == 0) {
				#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 + TIME_OUT) < time_Current.tv_sec )
					b_TimeOut = 1;
			}

			//END BF151
			

		} while ((ps_Sample09_GlobalUserStructApp -> b_InterruptFIFOEmpty == 1) && (b_TimeOut == 0));


		/***************************/
		/* Test if interrupt occur */
		/***************************/
		if (ps_Sample09_GlobalUserStructApp -> b_InterruptFIFOEmpty == 0) {
			while ((ps_Sample09_GlobalUserStructApp -> b_InterruptFIFOEmpty == 0) &&
					(ps_Sample09_GlobalUserStructApp -> b_InterruptFIFOOverFlow == 0) &&
					(ps_Sample09_GlobalUserStructApp -> b_Error == 0) &&
					(b_TimeOut == 0)) {

				printk("IRQ Cpt:%u IRQ Index:%u Comp:%u FIFO Error IRQ:%hu SDRAM Underflow:%hu PC Buffer Empty:%hu\n",
					   ps_Sample09_GlobalUserStructApp ->dw_ReceiveInterrupt,
					   ps_Sample09_GlobalUserStructApp ->
					   ps_InterruptFIFO[ps_Sample09_GlobalUserStructApp ->dw_ReadInterruptFIFOPointer].
					   dw_InterruptIndex,
					   ps_Sample09_GlobalUserStructApp ->
					   ps_InterruptFIFO[ps_Sample09_GlobalUserStructApp ->dw_ReadInterruptFIFOPointer].
					   dw_CompareInterrupt,
					   ps_Sample09_GlobalUserStructApp ->
					   ps_InterruptFIFO[ps_Sample09_GlobalUserStructApp ->dw_ReadInterruptFIFOPointer].
					   b_FIFOErrorInterrupt,
					   ps_Sample09_GlobalUserStructApp ->
					   ps_InterruptFIFO[ps_Sample09_GlobalUserStructApp ->dw_ReadInterruptFIFOPointer].
					   b_SDRAMUnderflowInterrupt,
					   ps_Sample09_GlobalUserStructApp ->
					   ps_InterruptFIFO[ps_Sample09_GlobalUserStructApp ->dw_ReadInterruptFIFOPointer].
					   b_ComputerBufferEmptyInterrupt);

				ps_Sample09_GlobalUserStructApp -> dw_ReadInterruptFIFOPointer = (ps_Sample09_GlobalUserStructApp -> dw_ReadInterruptFIFOPointer + 1) % SIZE_OF_INTERRUPT_FIFO;

				ps_Sample09_GlobalUserStructApp -> b_InterruptFIFOFull = 0;

				if (ps_Sample09_GlobalUserStructApp -> dw_ReadInterruptFIFOPointer == ps_Sample09_GlobalUserStructApp -> dw_WriteInterruptFIFOPointer) {
					ps_Sample09_GlobalUserStructApp -> b_InterruptFIFOEmpty = 1;

				}
				/**************************************/
				/* Complete the PC Buffer with values */
				/**************************************/
				i_ReturnValue = i_WriteNewAnalogOutValueInPCBuffer  (pdev,
								b_Channel,
								&w_NextValueToWrite,
								&dw_NbrOfWrittenValues,
								&dw_NbrOfFreeValues);

				if (i_ReturnValue == 0) {
					printk ("Write new value in PC Buffer OK Nbr of Free place :%08u Nbr of value written : %08u\n",dw_NbrOfFreeValues,dw_NbrOfWrittenValues);

				} else {
					if (i_ReturnValue == -1) {
						printk("PC Buffer Full                                                                                          \n");

					}
				}

				#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 + TIME_OUT) < time_Current.tv_sec )
					b_TimeOut = 1;

			}
		}
		/************************************************/
		/* Test if there was an interrupt FIFO Overflow */
		/************************************************/
		if ((ps_Sample09_GlobalUserStructApp -> b_InterruptFIFOOverFlow == 1) ||
				(ps_Sample09_GlobalUserStructApp -> b_Error == 1) ||
				(b_TimeOut == 1)) {
			if ((ps_Sample09_GlobalUserStructApp -> b_InterruptFIFOOverFlow == 1) ||
					(b_TimeOut == 1)) {
				/*******************************************/
				/* 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);

				}
				if (b_TimeOut == 0) {
					printk("ERROR : Sample Interrupt FIFO Overflow \n");
					b_Error = 1;
				}

			} else {
				printk("ERROR : Board Error or PC Buffer underflow \n");
				b_Error = 1;

			}

			printk("Interrupt saved before the Error or time out... \n");

			/*******************************/
			/* Display the saved interrupt */
			/*******************************/
			do {
				printk(" IRQ Cpt:%u IRQ Index:%u Comp:%u FIFO Error IRQ:%hu SDRAM Underflow:%hu PC Buffer Empty:%hu\n",
					   ps_Sample09_GlobalUserStructApp ->dw_ReceiveInterrupt,
					   ps_Sample09_GlobalUserStructApp ->
					   ps_InterruptFIFO[ps_Sample09_GlobalUserStructApp ->dw_ReadInterruptFIFOPointer].
					   dw_InterruptIndex,
					   ps_Sample09_GlobalUserStructApp ->
					   ps_InterruptFIFO[ps_Sample09_GlobalUserStructApp ->dw_ReadInterruptFIFOPointer].
					   dw_CompareInterrupt,
					   ps_Sample09_GlobalUserStructApp ->
					   ps_InterruptFIFO[ps_Sample09_GlobalUserStructApp ->dw_ReadInterruptFIFOPointer].
					   b_FIFOErrorInterrupt,
					   ps_Sample09_GlobalUserStructApp ->
					   ps_InterruptFIFO[ps_Sample09_GlobalUserStructApp ->dw_ReadInterruptFIFOPointer].
					   b_SDRAMUnderflowInterrupt,
					   ps_Sample09_GlobalUserStructApp ->
					   ps_InterruptFIFO[ps_Sample09_GlobalUserStructApp ->dw_ReadInterruptFIFOPointer].
					   b_ComputerBufferEmptyInterrupt);

				ps_Sample09_GlobalUserStructApp -> dw_ReadInterruptFIFOPointer = (ps_Sample09_GlobalUserStructApp -> dw_ReadInterruptFIFOPointer + 1) % SIZE_OF_INTERRUPT_FIFO;

				if (ps_Sample09_GlobalUserStructApp -> dw_ReadInterruptFIFOPointer == ps_Sample09_GlobalUserStructApp -> dw_WriteInterruptFIFOPointer) {
					ps_Sample09_GlobalUserStructApp -> b_InterruptFIFOEmpty = 1;

				}
			} while (ps_Sample09_GlobalUserStructApp -> b_InterruptFIFOEmpty == 0);
		}

		#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 + TIME_OUT) < time_Current.tv_sec )
			b_TimeOut = 1;

	} while ((b_Error == 0) && (b_TimeOut == 0));

	/********************/
	/* Error mamagement */
	/********************/
	/**************************/
	/* Test if no error occur */
	/**************************/
	if (b_Error == 0)
	{
		/******************/
		/* No error occur */
		/******************/
		return (0);

	} else
	{
		/***************/
		/* Error occur */
		/***************/
		return (-1);

	}
}



static int sample09 (struct pci_dev *pdev)
{ // Uses 1 Analog Output in Ring Buffer 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_NbrOfWrittenValues = 0;
	uint32_t dw_OnBoardBufferSize = 0;
	uint16_t w_NextValueToWrite = 0;



	printk("Uses 1 Analog Output in Ring Buffer 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_Sample09_InterruptRoutine);

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

			ps_Sample09_GlobalUserStructApp -> dw_ReadInterruptFIFOPointer = 0;
			ps_Sample09_GlobalUserStructApp -> dw_WriteInterruptFIFOPointer = 0;
			ps_Sample09_GlobalUserStructApp -> b_InterruptFIFOEmpty = 1;
			ps_Sample09_GlobalUserStructApp -> b_InterruptFIFOFull = 0;
			ps_Sample09_GlobalUserStructApp -> b_InterruptFIFOOverFlow = 0;

			/********************************/
			/* 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\n");


					/**************************************/
					/* Complete the PC Buffer with values */
					/**************************************/
					w_NextValueToWrite = 0;

					for (dw_Cpt = 0; dw_Cpt < dw_ComputerBufferSize;dw_Cpt = dw_Cpt + SAMPLE_BUFFER_SIZE) {
						i_ReturnValue = i_WriteNewAnalogOutValueInPCBuffer  (pdev,
										b_Channel,
										&w_NextValueToWrite,
										&dw_NbrOfWrittenValues,
										&dw_NbrOfFreeValues);

						if (i_ReturnValue == 0) {
							printk ("Write new value in PC Buffer OK Nbr of Free place :%08u Nbr of value written : %08u\n",dw_NbrOfFreeValues,dw_NbrOfWrittenValues);

							if (dw_NbrOfFreeValues == 0) {
								break;

							}
						} else {
							if (i_ReturnValue == -1) {
								printk("PC Buffer Full                                                                                          \n");
								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\n");


							do {
								/**************************************/
								/* Complete the PC Buffer with values */
								/**************************************/
								i_ReturnValue = i_WriteNewAnalogOutValueInPCBuffer  (pdev,
												b_Channel,
												&w_NextValueToWrite,
												&dw_NbrOfWrittenValues,
												&dw_NbrOfFreeValues);

								if (i_ReturnValue == 0) {
									printk ("Write new value in PC Buffer OK Nbr of Free place :%08u Nbr of value written : %08u\n",dw_NbrOfFreeValues,dw_NbrOfWrittenValues);

								} else {
									if (i_ReturnValue == -1) {
										printk(" PC Buffer Full                                                                                          \n");
										break;

									}
								}
							} while (dw_NbrOfFreeValues != 0);


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

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

								i_Sample09_RingBufferManagement (pdev,
																 b_Channel,
																 w_NextValueToWrite);


								/*******************************************/
								/* 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_sample09_init (void)
{

	printk("Using board minor %d (override with board_minor parameter)\n",board_minor);
	sample09(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_sample09_exit (void)
{

}


//------------------------------------------------------------------------------
module_exit (apci3600_sample09_exit);

module_init (apci3600_sample09_init);

