/** @file analog_in-kapi.c

Kernel functions for the analog input channels.

@par CREATION
@author Krauth Julien
@date   14.08.06

@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 "apci3120-private.h"

EXPORT_SYMBOL(i_APCI3120_InitAnalogInput);
EXPORT_SYMBOL(i_APCI3120_StartAnalogInput);
EXPORT_SYMBOL(i_APCI3120_ReadAnalogInputBit);
EXPORT_SYMBOL(i_APCI3120_ReadAnalogInputValue);
EXPORT_SYMBOL(i_APCI3120_ReadMoreAnalogInputs);
EXPORT_SYMBOL(i_APCI3120_InitReadMoreAnalogInput);
EXPORT_SYMBOL(i_APCI3120_TestEndOfReadMoreConversion);
EXPORT_SYMBOL(i_APCI3120_ReadMoreValues);
EXPORT_SYMBOL(i_APCI3120_StartReadMoreAnalogInput);

/**@def EXPORT_NO_SYMBOLS
* Function in this file are not exported.
*/
EXPORT_NO_SYMBOLS;

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

/** Initialize the analog input value.
*
* @param [in] 	pdev			: The device to initialize.
* @param [in]	b_ChannelNumber	: The analog input channel.
* @param [in]	b_Gain			: Gain factor of the analog input: <br>
*									- APCI3120_1_GAIN : Gain =  1 <br>
*									- APCI3120_2_GAIN : Gain =  2 <br>
*									- APCI3120_5_GAIN : Gain =  5 <br>
*									- APCI3120_10_GAIN: Gain = 10
* @param [in]	b_Polarity		: Polarity of the analog input: <br>
*									- APCI3120_UNIPOLAR:  0V - +10V <br>
*									- APCI3120_BIPOLAR: -10V - +10V
* @param [in]	ui_ConvertTiming: The convert timing of the analog input: <br>
*									from 10us up to 32767us
* @param [in]	b_InterruptFlag:	This parameter has to be 0 or PCI3120_DISABLE.<br>
* 																The interrupt is not implemeted!
*
* @retval 0: No error.
* @retval 1: The handle parameter of the board is wrong.
* @retval 2: Number of the analog input is wrong.
* @retval 3: The selected gain is wrong.
* @retval 4: The selected input voltage range is wrong.
* @retval 5: The selected conversion time is wrong.
* @retval 6: A wrong parameter has been passed for <br>
*			  b_InterruptFlag or the user interrupt routine has <br>
*			  not been installed.
*/
int i_APCI3120_InitAnalogInput (struct pci_dev *pdev,
		uint8_t b_ChannelNumber,
		uint8_t b_Gain,
		uint8_t b_Polarity,
		uint32_t ui_ConvertTiming,
		uint8_t b_InterruptFlag)
{
	uint8_t  b_DummyRead = 0;
	uint16_t ui_DummyRead = 0;


	/* Interrupt not implemented */
	b_InterruptFlag = APCI3120_DISABLE;

	if (!pdev) return 1;

	/* Check the channel number */
	if (b_ChannelNumber >= APCI3120_PRIVDATA(pdev)->nrAnaIn) return 2;

	APCI3120_PRIVDATA(pdev)->lastChannel = b_ChannelNumber;

	/* Test the gain */
	if (b_Gain != APCI3120_1_GAIN  &&
			b_Gain != APCI3120_2_GAIN  &&
			b_Gain != APCI3120_5_GAIN  &&
			b_Gain != APCI3120_10_GAIN) return 3;

	/* Test the polarity */
	if (b_Polarity != APCI3120_UNIPOLAR && b_Polarity != APCI3120_BIPOLAR) return 4;

	/* Test the converting time */
	if ((ui_ConvertTiming < 10) || (ui_ConvertTiming > 32767)) return 5;


	if ((b_InterruptFlag == APCI3120_ENABLE) && (APCI3120_PRIVDATA(pdev)->interruptInitialized == APCI3120_DISABLE)) return 6;

	/* Set the interrupt mode */
	if (b_InterruptFlag == APCI3120_ENABLE)
		APCI3120_PRIVDATA(pdev)->intMode = APCI3120_EOC_INT_MODE;
	else
		APCI3120_PRIVDATA(pdev)->intMode = 0;

	/* Initialise the sequence array */
	outw ((0 << 8) + (b_ChannelNumber | b_Gain | b_Polarity), GET_BASE_IO(pdev) + APCI3120_FIFO_ADDRESS);

	/* Clear the FIFO of the board */
	b_DummyRead = XPCI3120_INB (pdev, GET_BASE_IO(pdev) + APCI3120_RESET_FIFO);

	/* Initialize the timer 0 mode 5 (Start by trigger) */
	APCI3120_PRIVDATA(pdev)->crt1 = (APCI3120_PRIVDATA(pdev)->crt1 & 0xFC) | APCI3120_TIMER_0_MODE_5;

	outb (APCI3120_PRIVDATA(pdev)->crt1, GET_BASE_IO(pdev) + APCI3120_TIMER_CRT1);

	/* Write the interrupt select register */
	if (b_InterruptFlag == APCI3120_ENABLE)
		APCI3120_PRIVDATA(pdev)->modeSelect = ((APCI3120_PRIVDATA(pdev)->modeSelect & 0x34) | APCI3120_ENABLE_EOC_INT);
	else
		APCI3120_PRIVDATA(pdev)->modeSelect = APCI3120_PRIVDATA(pdev)->modeSelect & 0x34;

	outb (APCI3120_PRIVDATA(pdev)->modeSelect, (GET_BASE_IO(pdev) + APCI3120_WRITE_MODE_SELECT));

	ui_DummyRead = XPCI3120_INW(pdev, GET_BASE_IO(pdev) + APCI3120_RD_STATUS);

	/* Set gate 0 */
	APCI3120_PRIVDATA(pdev)->nWrAddr = (APCI3120_PRIVDATA(pdev)->nWrAddr & APCI3120_CLEAR_PA_PR) |  APCI3120_ENABLE_TIMER0;

	outw (APCI3120_PRIVDATA(pdev)->nWrAddr, GET_BASE_IO(pdev) + APCI3120_WR_ADDRESS);

	/* Convert the Convertion Time to the right format */
	ui_ConvertTiming = (uint16_t) (((uint32_t) ((uint32_t) (ui_ConvertTiming) * 2L)) - 2);

	APCI3120_PRIVDATA(pdev)->crt0.timer_select = APCI3120_SELECT_TIMER_0_WORD;
	outb (APCI3120_PRIVDATA(pdev)->crt0.raw, GET_BASE_IO(pdev) +  APCI3120_TIMER_CRT0);

	/* Write the conversion timing */
	outw (ui_ConvertTiming, GET_BASE_IO(pdev) + APCI3120_TIMER_VALUE);

	return 0;
}

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

/** Start the analog input conversion.
*
* @param [in] 	pdev			: The device to initialize.
*
* @retval 0: No error.
* @retval 1: The handle parameter of the board is wrong.
*/
int i_APCI3120_StartAnalogInput (struct pci_dev *pdev)
{
	uint16_t ui_DummyWrite  = 0;

	if (!pdev) return 1;

	/* Dummy write for the software trigger to start the conversation */
	outw (ui_DummyWrite, (GET_BASE_IO(pdev) + APCI3120_RD_STATUS));

	return 0;
}

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

/** Read the analog input conversion bit.
*
* Test the endo of conversion.
*
* @param [in] 	pdev			: The device to initialize.
*
* @retval 0: No error.
* @retval 1: The handle parameter of the board is wrong.
* @retval 2: The EOC flag is not set.
*/
int i_APCI3120_ReadAnalogInputBit (struct pci_dev *pdev)
{
	return i_APCI3120_TestEndOfReadMoreConversion(pdev);
}

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

/** Read the analog input value.
*
* @param [in] 	pdev			: The device to initialize.
* @param [out]	ui_ReadValue	: The analog input value.
*
* @retval 0: No error.
* @retval 1: The handle parameter of the board is wrong.
*/
int i_APCI3120_ReadAnalogInputValue (struct pci_dev *pdev, uint32_t *ui_ReadValue)
{
	if (!pdev) return 1;

	*ui_ReadValue = XPCI3120_INW (pdev, GET_BASE_IO(pdev));

	return 0;
}

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

/* returns true if conversion finished */
static int check_condition_ReadMoreAnalogInputs (struct pci_dev * pdev)
{
	// board specific code
	return ( ( XPCI3120_INW (pdev, GET_BASE_IO(pdev) + APCI3120_RD_STATUS) & APCI3120_EOS) == APCI3120_EOS);
}

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

/** Read more analog input.
*
* @param [in] 	pdev				: The device to initialize.
* @param [in]	b_SequenzArraySize		: Size of the sequence array (1 to 16 elements).
* @param [in]	pb_ChannelArray			: Channels to read (array size = b_SequenzArraySize).
* @param [in]	pb_GainArray			: Gain selection array.
* @param [in]	pb_PolarityArray		: Selection of the input.
* @param [in]	ui_ConvertTiming		: Selection of the conversion time from 10 to 32767 us.
* @param [in]	b_InterruptFlag:	This parameter has to be 0 or PCI3120_DISABLE.<br>
* 										The interrupt is not implemeted!
*
* @param [out]	pui_AnalogInputValueArray	: Read values.
*
* @retval 0: No error.
* @retval 1: The handle parameter of the board is wrong.
* @retval 2: The size of the sequence table is wrong.
* @retval 3: Wrong parameter found in table "pb_ChannelArray".
* @retval 4: Wrong parameter found in table "pb_GainArray".
* @retval 5: Wrong parameter found in table "pb_PolarityArray".
* @retval 6: The selected conversion time is wrong.
* @retval 7: A wrong parameter has been passed for b_InterruptFlag.
* @retval 8: Time out.
*
* @warning This function must not be called while the board is locked
*/
int   i_APCI3120_ReadMoreAnalogInputs     (struct pci_dev *pdev,
		uint8_t    b_SequenzArraySize,
		uint8_t *  pb_ChannelArray,
		uint8_t * pb_GainArray,
		uint8_t *  pb_PolarityArray,
		uint32_t    ui_ConvertTiming,
		uint8_t b_InterruptFlag,
		uint32_t * pui_AnalogInputValueArray)
{
	uint8_t  b_Cpt;
	uint32_t ui_DummyRead;
	uint8_t   b_DummyRead;
	uint32_t ui_RamArray [16];
	uint32_t dw_NbrOfMicroSecond = 0;

	/* Interrupt not implemented */
	b_InterruptFlag = APCI3120_DISABLE;

	for (b_Cpt = 0; b_Cpt < b_SequenzArraySize; b_Cpt ++)
	{
		/* Test the channel number  */
		if (pb_ChannelArray [b_Cpt] >= 16)
		{
			return 3;
		}

		/* Test the gain */
		if (pb_GainArray [b_Cpt] != APCI3120_1_GAIN &&
				pb_GainArray [b_Cpt] != APCI3120_2_GAIN &&
				pb_GainArray [b_Cpt] != APCI3120_5_GAIN &&
				pb_GainArray [b_Cpt] != APCI3120_10_GAIN)
		{
			return 4;
		}

		/* Test the polarity */
		if (pb_PolarityArray [b_Cpt] != APCI3120_UNIPOLAR && pb_PolarityArray [b_Cpt] != APCI3120_BIPOLAR)
		{
			return 5;
		}

		ui_RamArray [b_Cpt] = (	(uint32_t) b_Cpt << 8) |
		(pb_GainArray     [b_Cpt] & 0xFF) |
		(pb_PolarityArray [b_Cpt] & 0xFF) |
		(pb_ChannelArray  [b_Cpt] & 0xFF);
	}

	if (b_SequenzArraySize > 16)
	{
		return 2;
	}

	/* Test the conversion time */
	if ((ui_ConvertTiming < 10) || (ui_ConvertTiming > 32767U))
	{
		return 6;
	}

	ui_DummyRead = XPCI3120_INW (pdev, GET_BASE_IO(pdev));

	/* Clear the FIFO */
	b_DummyRead = XPCI3120_INB (pdev, GET_BASE_IO(pdev) + APCI3120_RESET_FIFO);

	for (b_Cpt = 0; b_Cpt < b_SequenzArraySize; b_Cpt ++)
	{
		/* Initialize the sequence array */
		outw (ui_RamArray [b_Cpt], GET_BASE_IO(pdev) + APCI3120_FIFO_ADDRESS);
	}

	dw_NbrOfMicroSecond = b_SequenzArraySize * ui_ConvertTiming;
	ui_ConvertTiming    = (uint32_t)((uint32_t)(((uint32_t)((((uint32_t)(ui_ConvertTiming)) * 2))))) - 2;

	/* Initialize Timer 0 mode 2 */
	outb (APCI3120_TIMER_0_MODE_2, GET_BASE_IO(pdev) + APCI3120_TIMER_CRT1);

	/* Select timer 0 */
	APCI3120_PRIVDATA(pdev)->crt0.timer_select = APCI3120_SELECT_TIMER_0_WORD;
	outb (APCI3120_PRIVDATA(pdev)->crt0.raw, GET_BAR1(pdev) + APCI3120_TIMER_CRT0);

	/* Set the conversion time */
	outw (ui_ConvertTiming, GET_BASE_IO(pdev) + APCI3120_TIMER_VALUE);

	/* Set the SCAN bit */
	outb (APCI3120_ENABLE_SCAN, GET_BASE_IO(pdev) + APCI3120_WRITE_MODE_SELECT);
	ui_DummyRead = XPCI3120_INW (pdev, GET_BASE_IO(pdev) + APCI3120_RD_STATUS);

	outw (APCI3120_ENABLE_TIMER0 | (((uint32_t) b_SequenzArraySize - 1) << 8), GET_BASE_IO(pdev) + APCI3120_WR_ADDRESS);

	/* Start the conversion */
	outw (0, GET_BASE_IO(pdev) + APCI3120_START_CONVERSION);

	/* busywait with a timeout of dw_NbrOfMicroSecond + 100 microseconds for security */
	if ( apci3120_busywait_with_timeout(pdev, check_condition_ReadMoreAnalogInputs, 20+(dw_NbrOfMicroSecond/5)) )
		return -ETIMEDOUT;

	for (b_Cpt = 0; b_Cpt < b_SequenzArraySize; b_Cpt ++)
	{
		pui_AnalogInputValueArray [b_Cpt] = XPCI3120_INW (pdev, GET_BASE_IO(pdev));
	}

	return 0;
}

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

/** Initialize read more analog input.
*
* @param [in] pdev						: The device to initialize.
* @param [in]	b_SequenzArraySize	: Size of the sequence array (1 to 16 elements).
* @param [in]	pb_ChannelArray		: Channels to read (array size = b_SequenzArraySize).
* @param [in]	pb_GainArray			: Gain selection array.
* @param [in]	pb_PolarityArray		: Selection of the input.
* @param [in]	ui_ConvertTiming		: Selection of the conversion time from 10 to 32767 us.
* @param [in]	b_InterruptFlag		:	This parameter has to be 0 or PCI3120_DISABLE.<br>
* 										The interrupt is not implemeted!
*
* @retval 0: No error.
* @retval 1: The handle parameter of the board is wrong.
* @retval 2: The size of the sequence table is wrong.
* @retval 3: Wrong parameter found in table "pb_ChannelArray".
* @retval 4: Wrong parameter found in table "pb_GainArray".
* @retval 5: Wrong parameter found in table "pb_PolarityArray".
* @retval 6: The selected conversion time is wrong.
* @retval 7: A wrong parameter has been passed for b_InterruptFlag.
*/
int i_APCI3120_InitReadMoreAnalogInput (struct pci_dev *pdev,
		uint8_t b_SequenzArraySize,
		uint8_t *  pb_ChannelArray,
		uint8_t * pb_GainArray,
		uint8_t *  pb_PolarityArray,
		uint32_t ui_ConvertTiming,
		uint8_t b_InterruptFlag)
{
	uint8_t  b_Cpt;
	uint32_t ui_DummyRead;
	uint8_t   b_DummyRead;
	uint32_t ui_RamArray [16];
	uint32_t dw_NbrOfMicroSecond = 0;

	/* Interrupt not implemented */
	b_InterruptFlag = APCI3120_DISABLE;

	if (!pdev) return 1;

	for (b_Cpt = 0; b_Cpt < b_SequenzArraySize; b_Cpt ++)
	{
		/* Test the channel number  */
		if (pb_ChannelArray [b_Cpt] >= 16)
		{
			return 3;
		}

		/* Test the gain */
		if (pb_GainArray [b_Cpt] != APCI3120_1_GAIN &&
				pb_GainArray [b_Cpt] != APCI3120_2_GAIN &&
				pb_GainArray [b_Cpt] != APCI3120_5_GAIN &&
				pb_GainArray [b_Cpt] != APCI3120_10_GAIN)
		{
			return 4;
		}

		/* Test the polarity */
		if (pb_PolarityArray [b_Cpt] != APCI3120_UNIPOLAR && pb_PolarityArray [b_Cpt] != APCI3120_BIPOLAR)
		{
			return 5;
		}

		ui_RamArray [b_Cpt] = (	(uint32_t) b_Cpt << 8) |
		(pb_GainArray     [b_Cpt] & 0xFF) |
		(pb_PolarityArray [b_Cpt] & 0xFF) |
		(pb_ChannelArray  [b_Cpt] & 0xFF);
	}

	if (b_SequenzArraySize > 16)
	{
		return 2;
	}

	/* Test the conversion time */
	if ((ui_ConvertTiming < 10) || (ui_ConvertTiming > 32767U))
	{
		return 6;
	}

	ui_DummyRead = XPCI3120_INW (pdev, GET_BASE_IO(pdev));

	/* Clear the FIFO */
	b_DummyRead = XPCI3120_INB (pdev, GET_BASE_IO(pdev) + APCI3120_RESET_FIFO);

	for (b_Cpt = 0; b_Cpt < b_SequenzArraySize; b_Cpt ++)
	{
		/* Initialize the sequence array */
		outw (ui_RamArray [b_Cpt], GET_BASE_IO(pdev) + APCI3120_FIFO_ADDRESS);
	}

	dw_NbrOfMicroSecond = b_SequenzArraySize * ui_ConvertTiming;
	ui_ConvertTiming    = (uint32_t)((uint32_t)(((uint32_t)((((uint32_t)(ui_ConvertTiming)) * 2))))) - 2;

	/* Initialize Timer 0 mode 2 */
	outb (APCI3120_TIMER_0_MODE_2, GET_BASE_IO(pdev) + APCI3120_TIMER_CRT1);

	/* Select timer 0 */
	APCI3120_PRIVDATA(pdev)->crt0.timer_select = APCI3120_SELECT_TIMER_0_WORD;
	outb (APCI3120_PRIVDATA(pdev)->crt0.raw, GET_BAR1(pdev) + APCI3120_TIMER_CRT0);

	/* Set the conversion time */
	outw (ui_ConvertTiming, GET_BASE_IO(pdev) + APCI3120_TIMER_VALUE);

	/* Set the SCAN bit */
	outb (APCI3120_ENABLE_SCAN, GET_BASE_IO(pdev) + APCI3120_WRITE_MODE_SELECT);
	ui_DummyRead = XPCI3120_INW (pdev, GET_BASE_IO(pdev) + APCI3120_RD_STATUS);

	return 0;
}

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

/** Start read more analog input.
*
* @param [in] 	pdev						: The device to initialize.
* @param [in]		b_SequenzArraySize	: Size of the sequence array (1 to 16 elements).
*
* @retval 0: No error.
* @retval 1: The handle parameter of the board is wrong.
* @retval 2: The size of the sequence table is wrong.
*/
int i_APCI3120_StartReadMoreAnalogInput (struct pci_dev *pdev, uint8_t b_SequenzArraySize)
{
	if (!pdev) return 1;

	if (b_SequenzArraySize > 16) return 2;

	/* Set the scan sequence */
	outw (APCI3120_ENABLE_TIMER0 | (((unsigned short int) b_SequenzArraySize - 1) << 8), GET_BASE_IO(pdev) + APCI3120_WR_ADDRESS);

	/* Starts the conversion */
	outw (0, GET_BASE_IO(pdev) + APCI3120_START_CONVERSION);

	return 0;
}

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

/** Test end of conversion of read more analog input.
*
* @param [in] 	pdev	: The device to initialize.
*
* @retval 0: No error (Conversion is not running).
* @retval 1: The handle parameter of the board is wrong.
* @retval 2: Conversion is running.
*/
int i_APCI3120_TestEndOfReadMoreConversion (struct pci_dev *pdev)
{
	uint32_t ui_PortValue = 0;

	if (!pdev) return 1;

	ui_PortValue = XPCI3120_INW (pdev, GET_BASE_IO(pdev) + APCI3120_RD_STATUS);

	if (((ui_PortValue & APCI3120_EOS) >> 13) == 0)
		return 2;

	return 0;
}

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

/** Start read more analog input.
*
* @param [in] 	pdev						: The device to initialize.
* @param [in]		b_SequenzArraySize	: Size of the sequence array (1 to 16 elements).
* @param [out]	pui_Values				: Read values.
*
* @retval 0: No error.
* @retval 1: The handle parameter of the board is wrong.
* @retval 2: The size of the sequence table is wrong.
*/
int i_APCI3120_ReadMoreValues (struct pci_dev *pdev, uint8_t b_SequenzArraySize, uint32_t * pui_Values)
{
	uint8_t b_Cpt = 0;

	if (!pdev) return 1;

	if (b_SequenzArraySize > 16) return 2;

	/* Read the analog value */
	for (b_Cpt = 0; b_Cpt < b_SequenzArraySize; b_Cpt ++)
	{
		pui_Values [b_Cpt] = XPCI3120_INW (pdev, GET_BASE_IO(pdev));
	}

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

/** Initialize a sequence acquisition
 *
 * @param [in]	pdev				: The device to initialize
 * @param [in]	sequence_array_size	: The number of channels in the sequence
 * @param [in]	sequence			: An array containing the id of the channel that will be acquired during the sequence
 * @param [in]	gain				: An array containing the gain of the channels
 * @param [in]	polarity			: An array containing the polarity of the channels
 * @param [in]	sequence_data		: A pointer to a structure containing usefull data for the sequence acquisition. The structure is described in the file apci3120.h
 * Description of the structure apci3120_sequence_acquisition_data
 *					uint8_t acquisition_mode		: Two conversion cycles are possible:
															- APCI3120_SIMPLE_MODUS:
																A conversion occurs every acquisition_timing (time interval)
															- APCI3120_DELAY_MODUS:
																The both times are used in this mode:
																	ui_AcquisitionTiming and l_DelayTiming.
																Conversions occur every ui_AcquisitionTiming (time interval) until all the analog inputs have been acquired
																(determined by b_SequenzArraySize)
																Step 1.
																	Afterwards there is a waiting time of l_DelayTiming.
																Step 2
																	The two steps are repeated
					uint8_t extern_trigger			: APCI3120_ENABLE or APCI3120_DISABLE
					uint32_t acquisition_timing		: Time in us between two conversions of successive input.
														If APCI_DMA_USED: from 7 us to 45874 us
														If APCI3120_NOT_DMA_USED: from 1500 us to 45874 us
					uint64_t delay_timing			: Waiting time in us between two conversion cycles. From 70us to 4587400 (Only have a meaning if use : APCI3120_DELAY_MODUS)
					uint32_t number_of_acquisition	: How many acquisitions have to happened (1 to 32767) before an interrupt occurs ( it's not the sequence number, but the acquisition number !!!! )
					uint8_t dma_mode				: Determine is DMA is used or not.
														If APCI3120_DMA_USED: all the conversion values are saved and returned to the user. An interrupt is generated when
														the conversions (set by number_of_acquisition) are completed
														If APCI3120_DMA_NOT_USED: An interrupt is generated at the end of each conversion and the analog value is returned
														to the user thanks to the interrupt routine
					uint8_t acquisition_cycle		: Type of DMA conversion
														If APCI3120_CONTINUOUS: An interrupt is generated each time a DMA conversion cycle is completed .A new DMA conversion cycle is started
														If APCI3120_SINGLE: The DMA conversion cycle is only carried out once: i.e.you receive one single interrupt at the end of the
														first DMA conversion cycle.
  | Return Value      : 0  : No error                                         |
  |                     1  : The handle parameter of the board is wrong       |
  |                     2  : The user interrupt routine is not installed.     |
  |                           See function "i_APCI3120_SetBoardIntRoutine"     |
  |                     3  : The size of the sequence table is wrong          |
  |                     4  : Wrong parameter found in table "pb_ChannelArray" |
  |                     5  : Wrong parameter found in table "pb_GainArray"    |
  |                     6  : Wrong parameter found in table "pb_PolarityArray"|
  |                     7  : The waiting time between two conversion cycles is|
  |                           too high                                         |
  |                     8  : The selected time for ui_AcquisitionTiming or    |
  |                           l_DelayTiming is wrong                           |
  |                     9  : The parameter b_DMAUsed is wrong                 |
  |                     10 : The parametered running time of the DMA          |
  |                           conversion cycle is wrong                        |
  |                           (APCI3120_CONTINUOUS or APCI3120_SINGLE)         |
  |                     11 : The parametered conversion cycle is wrong        |
  |                           (APCI3120_SIMPLE_MODUS or APCI3120_DELAY_MODUS   |
  |                     12: Not enough memory available.                      |
  |                     13: Extern Trigger is wrong                           |
  |                     14: ul_NumberOfAcquisition is wrong                   |
  |                     16: The Delay time is too small.                      |
  |                     17: A sequence was still started                      |
 */
int i_APCI3120_InitAnalogInputSequenceAcquisition (	struct pci_dev *pdev,
													//uint8_t sequence_array_size,
													//uint8_t* sequence,
													//uint8_t* gain,
													//uint8_t* polarity,
													struct apci3120_sequence_acquisition_data *sequence_data){
	int cpt = 0;
	unsigned char dummy_read;
	short pages;

	if(! pdev) return 1; // handle parameter of the board is wrong

	// First of all, test if we are not doing a sequence acquisition
	if( APCI3120_PRIVDATA(pdev)->device_state == STATE_SEQUENCE_STARTED )
	{
		return 17;
	}

	/* Test the parameters */
	if ( sequence_data->sequence_array_size < 1 || sequence_data->sequence_array_size > APCI3120_MAX_CHANNEL_NBR )
	{
		return 3; // Array size is wrong
	}

	for( cpt = 0; cpt < sequence_data->sequence_array_size; cpt++ )
	{
		// Test the channel number
		if( sequence_data->sequence_channels[cpt] < 0 || sequence_data->sequence_channels[cpt] >= 16 )
		{
			return 4; // Wrong parameter in sequence array
		}

		// Test the gain
		if( (sequence_data->gain[cpt] != APCI3120_1_GAIN) && (sequence_data->gain[cpt] != APCI3120_2_GAIN) && (sequence_data->gain[cpt] != APCI3120_5_GAIN) && (sequence_data->gain[cpt] != APCI3120_10_GAIN) )
		{
			return 5; // Wrong parameter in gain array
		}

		// Test the polarity
		if( (sequence_data->polarity[cpt] != APCI3120_UNIPOLAR) && (sequence_data->polarity[cpt] != APCI3120_BIPOLAR) )
		{
			return 6; // Wrong parameter in polarity array
		}

		// Save the value of the acquistion for the channel in the internal structure of the driver
		APCI3120_PRIVDATA(pdev)->ram_array[cpt] =  (	(uint32_t) cpt << 8) | (sequence_data->gain     [cpt] & 0xFF)
														| (sequence_data->polarity [cpt] & 0xFF) | (sequence_data->sequence_channels  [cpt] & 0xFF);
	}

	// Test if the user still installed an interrupt routine
	if ( APCI3120_PRIVDATA(pdev)->interruptInitialized != APCI3120_ENABLE )
	{
		return 2;
	}

	// Test the parameters in sequence_data
	if( (sequence_data->number_of_acquisition <= 0) || (sequence_data->number_of_acquisition > 32767) )
	{
		return 14;
	}

	// number of aquisitions should also be a multiple of 2 when DMA is used
	if ( ((sequence_data->number_of_acquisition % 2) != 0) && (sequence_data->dma_mode == APCI3120_DMA_USED) )
	{
		return 14;
	}

	if( (sequence_data->extern_trigger != APCI3120_DISABLE) && (sequence_data->extern_trigger != APCI3120_ENABLE ))
	{
		return 13;
	}

	if( (sequence_data->acquisition_mode != APCI3120_SIMPLE_MODUS) && (sequence_data->acquisition_mode != APCI3120_DELAY_MODUS))
	{
		return 11;
	}

	if ( (sequence_data->acquisition_mode == APCI3120_DELAY_MODUS) && ( sequence_data->delay_timing < 70 || sequence_data->delay_timing > 4587400L ) )
	{
		return 8;
	}

	if ( (sequence_data->acquisition_mode == APCI3120_DELAY_MODUS) && ( (sequence_data->delay_timing + (sequence_data->acquisition_timing * sequence_data->sequence_array_size)) > 327650UL ) )
	{
		return 7;
	}

	if ( (sequence_data->acquisition_cycle != APCI3120_SINGLE) && (sequence_data->acquisition_cycle != APCI3120_CONTINUOUS))
	{
		return 10;
	}

	if ((sequence_data->acquisition_timing < 10) || (sequence_data->acquisition_timing > 32767U))
	{
		return 8;
	}

	// Save the conversion time in the internal structure
	APCI3120_PRIVDATA(pdev)->conversion_time =  (unsigned short)((unsigned long)(((unsigned long)((((unsigned long)(sequence_data->acquisition_timing)) * 2))))) - 2;
	//@TODO: handle delay mode


	// Test the DMA
	if ( (sequence_data->dma_mode != APCI3120_DMA_USED) && (sequence_data->dma_mode != APCI3120_DMA_NOT_USED) )
	{
		return 9;
	}

	// Test if the DMA is already used
	if (  APCI3120_PRIVDATA(pdev)->dma_state == APCI3120_ENABLE )
	{
		// Deallocate allocated memory
		switch( APCI3120_PRIVDATA(pdev)->sequence_data.acquisition_cycle )
		{
			case APCI3120_SINGLE:
				if ( APCI3120_PRIVDATA(pdev)->dma_buffer != NULL )
				{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27)
					dma_free_coherent(&pdev->dev,
						PAGE_SIZE << APCI3120_PRIVDATA(pdev)->dma_buffer_pages,
						APCI3120_PRIVDATA(pdev)->dma_buffer,
						APCI3120_PRIVDATA(pdev)->dma_buffer_hw);
#else
					free_pages((unsigned long)APCI3120_PRIVDATA(pdev)->dma_buffer, APCI3120_PRIVDATA(pdev)->dma_buffer_pages);
#endif
				}
				APCI3120_PRIVDATA(pdev)->dma_buffer = NULL;
				break;
			case APCI3120_CONTINUOUS:
				if ( APCI3120_PRIVDATA(pdev)->dma_buffer != NULL )
				{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27)
					dma_free_coherent(&pdev->dev,
						PAGE_SIZE << APCI3120_PRIVDATA(pdev)->dma_buffer_pages,
						APCI3120_PRIVDATA(pdev)->dma_buffer,
						APCI3120_PRIVDATA(pdev)->dma_buffer_hw);
#else
					free_pages((unsigned long)APCI3120_PRIVDATA(pdev)->dma_buffer, APCI3120_PRIVDATA(pdev)->dma_buffer_pages);
#endif
				}
				if ( APCI3120_PRIVDATA(pdev)->dma_old_buffer != NULL )
				{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27)
					dma_free_coherent(&pdev->dev,
						PAGE_SIZE << APCI3120_PRIVDATA(pdev)->dma_old_buffer_pages,
						APCI3120_PRIVDATA(pdev)->dma_old_buffer,
						APCI3120_PRIVDATA(pdev)->dma_old_buffer_hw);
#else
					free_pages((unsigned long)APCI3120_PRIVDATA(pdev)->dma_old_buffer, APCI3120_PRIVDATA(pdev)->dma_old_buffer_pages);
#endif
				}
				APCI3120_PRIVDATA(pdev)->dma_buffer = NULL;
				APCI3120_PRIVDATA(pdev)->dma_old_buffer = NULL;
				break;
		}
	}

	// Initialize the DMA (if we choose to use it )
	if ( sequence_data->dma_mode == APCI3120_DMA_USED )
	{
		// Initialize the size of the dma buffer
		APCI3120_PRIVDATA(pdev)->dma_buffer_size = (unsigned long) (sequence_data->number_of_acquisition + 24);
		APCI3120_PRIVDATA(pdev)->dma_old_buffer_size = (unsigned long) (sequence_data->number_of_acquisition + 24);


		if( sequence_data->acquisition_cycle == APCI3120_CONTINUOUS )
		{
			// Malloc the dma buffer
			// First try with 4 pages, then if fail with 3 pages, etc ...
			for (pages=4; pages>=0; pages--)
			{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27)
				/* modern DMA API: coherent allocation with proper bus address */
				APCI3120_PRIVDATA(pdev)->dma_buffer = (unsigned long*) dma_alloc_coherent(
					&pdev->dev,
					PAGE_SIZE << pages,
					&APCI3120_PRIVDATA(pdev)->dma_buffer_hw,
					GFP_KERNEL);
				if (APCI3120_PRIVDATA(pdev)->dma_buffer)
					break;
#else
				if( (APCI3120_PRIVDATA(pdev)->dma_buffer = (void*) __get_free_pages(__GFP_DMA,pages)) )
					break; // Succeed, or dma_buffer has a size of "pages" pages
#endif
			}

			if ((APCI3120_PRIVDATA(pdev)->dma_buffer != NULL) && ( APCI3120_PRIVDATA(pdev)->dma_buffer_size < PAGE_SIZE*pages))
			{
				// We have enough memory
				APCI3120_PRIVDATA(pdev)->dma_buffer_pages = pages;
				APCI3120_PRIVDATA(pdev)->dma_buffer_size = PAGE_SIZE*pages;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27)
				/* dma_buffer_hw already filled by dma_alloc_coherent() */
#elif LINUX_VERSION_CODE > KERNEL_VERSION(3,16,0)
				APCI3120_PRIVDATA(pdev)->dma_buffer_hw = (void *) virt_to_phys(APCI3120_PRIVDATA(pdev)->dma_buffer);
#else
				APCI3120_PRIVDATA(pdev)->dma_buffer_hw = (void *) virt_to_bus(APCI3120_PRIVDATA(pdev)->dma_buffer);
#endif
			}
			else
			{
				// Failed at allocating memory
				// If (APCI3120_PRIVDATA(pdev)->dma_buffer is not null we have to free it
				if ( APCI3120_PRIVDATA(pdev)->dma_buffer != NULL )
				{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27)
					dma_free_coherent(&pdev->dev, PAGE_SIZE << pages,
						APCI3120_PRIVDATA(pdev)->dma_buffer,
						APCI3120_PRIVDATA(pdev)->dma_buffer_hw);
#else
					free_pages((unsigned long)APCI3120_PRIVDATA(pdev)->dma_buffer, pages);
#endif
				}
				return 12; // Memory error
			}

			// Malloc the dma second buffer
			// First try with 4 pages, then if fail with 3 pages, etc ...
			for (pages=4; pages>=0; pages--)
			{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27)
				APCI3120_PRIVDATA(pdev)->dma_old_buffer = (unsigned long*) dma_alloc_coherent(
					&pdev->dev,
					PAGE_SIZE << pages,
					&APCI3120_PRIVDATA(pdev)->dma_old_buffer_hw,
					GFP_KERNEL);
				if (APCI3120_PRIVDATA(pdev)->dma_old_buffer)
					break;
#else
				if( (APCI3120_PRIVDATA(pdev)->dma_old_buffer = (void*) __get_free_pages(__GFP_DMA,pages)) )
					break; // Succeed, or dma_buffer has a size of "pages" pages
#endif
			}

			if ((APCI3120_PRIVDATA(pdev)->dma_old_buffer != NULL) && ( APCI3120_PRIVDATA(pdev)->dma_old_buffer_size < PAGE_SIZE*pages))
			{
				// We have enough memory
				APCI3120_PRIVDATA(pdev)->dma_old_buffer_pages = pages;
				APCI3120_PRIVDATA(pdev)->dma_old_buffer_size = PAGE_SIZE*pages;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27)
				/* dma_old_buffer_hw already filled by dma_alloc_coherent() */
#elif LINUX_VERSION_CODE > KERNEL_VERSION(3,16,0)
				APCI3120_PRIVDATA(pdev)->dma_old_buffer_hw = (void *) virt_to_phys(APCI3120_PRIVDATA(pdev)->dma_old_buffer);
#else
				APCI3120_PRIVDATA(pdev)->dma_old_buffer_hw = (void *) virt_to_bus(APCI3120_PRIVDATA(pdev)->dma_old_buffer);
#endif
			}
			else
			{
				// Failed at allocating memory
				// If (APCI3120_PRIVDATA(pdev)->dma_buffer is not null we have to free it
				if ( APCI3120_PRIVDATA(pdev)->dma_old_buffer != NULL )
				{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27)
					dma_free_coherent(&pdev->dev, PAGE_SIZE << pages,
						APCI3120_PRIVDATA(pdev)->dma_old_buffer,
						APCI3120_PRIVDATA(pdev)->dma_old_buffer_hw);
#else
					free_pages((unsigned long)APCI3120_PRIVDATA(pdev)->dma_old_buffer, pages);
#endif
				}
				if ( APCI3120_PRIVDATA(pdev)->dma_buffer != NULL )
				{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27)
					dma_free_coherent(&pdev->dev,
						PAGE_SIZE << APCI3120_PRIVDATA(pdev)->dma_buffer_pages,
						APCI3120_PRIVDATA(pdev)->dma_buffer,
						APCI3120_PRIVDATA(pdev)->dma_buffer_hw);
#else
					free_pages((unsigned long)APCI3120_PRIVDATA(pdev)->dma_buffer, pages);
#endif
				}
				// The same for dma_buffer
				return 12; // Memory error
			}

		}
		if( sequence_data->acquisition_cycle == APCI3120_SINGLE )
		{
			// Malloc the dma buffer
			// First try with 4 pages, then if fail with 3 pages, etc ...
			for (pages=4; pages>=0; pages--)
			{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27)
				APCI3120_PRIVDATA(pdev)->dma_buffer = (unsigned long*) dma_alloc_coherent(
					&pdev->dev,
					PAGE_SIZE << pages,
					&APCI3120_PRIVDATA(pdev)->dma_buffer_hw,
					GFP_KERNEL);
				if (APCI3120_PRIVDATA(pdev)->dma_buffer)
					break;
#else
				if( (APCI3120_PRIVDATA(pdev)->dma_buffer = (void*) __get_free_pages(__GFP_DMA,pages)) )
					break; // Succeed, or dma_buffer has a size of "pages" pages
#endif
			}

			if ((APCI3120_PRIVDATA(pdev)->dma_buffer != NULL) && ( APCI3120_PRIVDATA(pdev)->dma_buffer_size < PAGE_SIZE*pages))
			{
				// We have enough memory
				APCI3120_PRIVDATA(pdev)->dma_buffer_pages = pages;
				APCI3120_PRIVDATA(pdev)->dma_buffer_size = PAGE_SIZE*pages;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27)
				/* dma_buffer_hw already filled by dma_alloc_coherent() */
#elif LINUX_VERSION_CODE > KERNEL_VERSION(3,16,0)
				APCI3120_PRIVDATA(pdev)->dma_buffer_hw = (void *) virt_to_phys(APCI3120_PRIVDATA(pdev)->dma_buffer);
#else
				APCI3120_PRIVDATA(pdev)->dma_buffer_hw = (void *) virt_to_bus(APCI3120_PRIVDATA(pdev)->dma_buffer);
#endif
			}
			else
			{
				// Failed at allocating memory
				// If (APCI3120_PRIVDATA(pdev)->dma_buffer is not null we have to free it
				if ( APCI3120_PRIVDATA(pdev)->dma_buffer != NULL )
				{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27)
					dma_free_coherent(&pdev->dev, PAGE_SIZE << pages,
						APCI3120_PRIVDATA(pdev)->dma_buffer,
						APCI3120_PRIVDATA(pdev)->dma_buffer_hw);
#else
					free_pages((unsigned long)APCI3120_PRIVDATA(pdev)->dma_buffer, pages);
#endif
				}
				return 12; // Memory error
			}
		}
		APCI3120_PRIVDATA(pdev)->dma_state = APCI3120_ENABLE;
	}

	// Reset the FIFO
	dummy_read = inb(GET_BAR1(pdev) + APCI3120_RESET_FIFO);
	APCI3120_PRIVDATA(pdev)->sequence_data.acquisition_cycle = sequence_data->acquisition_cycle;
	APCI3120_PRIVDATA(pdev)->sequence_data.acquisition_mode = sequence_data->acquisition_mode;
	APCI3120_PRIVDATA(pdev)->sequence_data.number_of_acquisition = sequence_data->number_of_acquisition;
	APCI3120_PRIVDATA(pdev)->sequence_array_size = sequence_data->sequence_array_size;
	APCI3120_PRIVDATA(pdev)->sequence_data.extern_trigger = sequence_data->extern_trigger;
	APCI3120_PRIVDATA(pdev)->sequence_data.dma_mode = sequence_data->dma_mode;
	APCI3120_PRIVDATA(pdev)->dma_buf_index = 0;

	APCI3120_PRIVDATA(pdev)->device_state = STATE_SEQUENCE_INITIALISED;

	return 0;
}
/*
 * Starts the cyclic conversion. It has been previously initialized with function "i_APCI3120_InitAnalogInputAcquisition".
  | Return Value      :  0: No error                                           |
  |                      1: The handle parameter of the board is wrong         |
  |                      2: The cyclic conversion has not been initialized.    |
  |                         "i_APCI3120_InitAnalogInputAcquisition"            |
  |                      3: Interrupt routine has not been installed.          |
  |                      4: There was still a running acquisition              |
 */
int i_APCI3120_StartAnalogInputSequenceAcquisition ( struct pci_dev *pdev)
{
	unsigned char dummy_read;
	unsigned char cpt;
	unsigned long dma_buffer_size = 0;
	unsigned short timer_2_low_word = 0;
	unsigned short timer_2_high_word = 0;

	if(! pdev) return 1; // handle parameter of the board is wrong

	if( APCI3120_PRIVDATA(pdev)->device_state != STATE_SEQUENCE_INITIALISED )
	{
		return 2;
	}

	// Test if the user still installed an interrupt routine
	if ( APCI3120_PRIVDATA(pdev)->interruptInitialized != APCI3120_ENABLE )
	{
		return 3;
	}

	if( APCI3120_PRIVDATA(pdev)->device_state == STATE_SEQUENCE_STARTED )
	{
		return 4;
	}


	/* Clear Timer Write TC int16 */
	outl(APCI3120_CLEAR_WRITE_TC_INT, GET_BAR0(pdev) + APCI3120_AMCC_OP_REG_INTCSR);


	/* Clears the timer status register */
	dummy_read = inb(GET_BAR1(pdev) + APCI3120_TIMER_STATUS_REGISTER);

	/* Disables All Timer     */
	/* Sets PR and PA to 0    */
	APCI3120_PRIVDATA(pdev)->nWrAddr = APCI3120_PRIVDATA(pdev)->nWrAddr & APCI3120_DISABLE_TIMER0 & APCI3120_DISABLE_TIMER1 & APCI3120_CLEAR_PA_PR ;

	if (APCI3120_PRIVDATA(pdev)->sequence_data.dma_mode == APCI3120_DMA_NOT_USED)
	{
		/* Disables Timer 2       */
		APCI3120_PRIVDATA(pdev)->nWrAddr = APCI3120_PRIVDATA(pdev)->nWrAddr & APCI3120_DISABLE_TIMER2;
	}

	outw(APCI3120_PRIVDATA(pdev)->nWrAddr, GET_BAR1(pdev) + APCI3120_WR_ADDRESS);

	/* Resets the FIFO */
	dummy_read = inb(GET_BAR1(pdev) + APCI3120_RESET_FIFO);

	/* Initializes the sequence array */
	for (cpt = 0; cpt < APCI3120_PRIVDATA(pdev)->sequence_array_size; cpt ++)
	{
	  outw(APCI3120_PRIVDATA(pdev)->ram_array[cpt], GET_BAR1(pdev) + APCI3120_FIFO_ADDRESS);
	}

	/* Initializes timer 0 in mode 2 */ /* crt1 */
	APCI3120_PRIVDATA(pdev)->crt1 = (APCI3120_PRIVDATA(pdev)->crt1 & 0xFC) | APCI3120_TIMER_0_MODE_2;

	// Handle with delay modus
	if ((APCI3120_PRIVDATA(pdev)->sequence_data.acquisition_mode == APCI3120_DELAY_MODUS) )
	{
	  /* Initializes Timer 1 in mode 2 */
		APCI3120_PRIVDATA(pdev)->crt1 = (APCI3120_PRIVDATA(pdev)->crt1 & 0xF3) | APCI3120_TIMER_1_MODE_2;
	}

	if (APCI3120_PRIVDATA(pdev)->sequence_data.dma_mode == APCI3120_DMA_NOT_USED)
	{
	  /* Initializes timer 2 in mode 0 */
		APCI3120_PRIVDATA(pdev)->crt1 = (APCI3120_PRIVDATA(pdev)->crt1 & 0x0F) | APCI3120_TIMER_2_MODE_0;

	}

	/* Writes timer mode */
	outb(APCI3120_PRIVDATA(pdev)->crt1, GET_BAR1(pdev) + APCI3120_TIMER_CRT1);

	if (APCI3120_PRIVDATA(pdev)->sequence_data.dma_mode == APCI3120_DMA_NOT_USED)
	{
		APCI3120_PRIVDATA(pdev)->crt0.timer_select = APCI3120_SELECT_TIMER_2_LOW_WORD;
		/* Writes timer 2 low word */
		outb(APCI3120_PRIVDATA(pdev)->crt0.raw, GET_BAR1(pdev) + APCI3120_TIMER_CRT0);

		dma_buffer_size = (APCI3120_PRIVDATA(pdev)->sequence_data.number_of_acquisition - 2);

		if (dma_buffer_size <=  0)
			dma_buffer_size = 1;

		timer_2_low_word = (unsigned short)dma_buffer_size;
		timer_2_high_word = (unsigned short)(dma_buffer_size >> 16);

		outw(timer_2_low_word, GET_BAR1(pdev) + APCI3120_TIMER_VALUE);

		APCI3120_PRIVDATA(pdev)->crt0.timer_select = APCI3120_SELECT_TIMER_2_HIGH_WORD;
		/* Writes timer 2 high word */
		outb(APCI3120_PRIVDATA(pdev)->crt0.raw, GET_BAR1(pdev) + APCI3120_TIMER_CRT0);

		outw(timer_2_high_word, GET_BAR1(pdev) + APCI3120_TIMER_VALUE);
	}

	APCI3120_PRIVDATA(pdev)->crt0.timer_select = APCI3120_SELECT_TIMER_0_WORD;

	/* Selects timer 0 */
	outb(APCI3120_PRIVDATA(pdev)->crt0.raw, GET_BAR1(pdev) + APCI3120_TIMER_CRT0);


	/* Sets the conversion time */
	outw(APCI3120_PRIVDATA(pdev)->conversion_time, GET_BAR1(pdev) + APCI3120_TIMER_VALUE);

	/* Tests if delay modus */
	if ((APCI3120_PRIVDATA(pdev)->sequence_data.acquisition_mode == APCI3120_DELAY_MODUS) )
	{
		//@TODO: handle delay
	}

	if (APCI3120_PRIVDATA(pdev)->sequence_data.dma_mode == APCI3120_DMA_NOT_USED)
	{
		//@TODO: handle DMA_NOT_USED
	}

	/* Clears the SCAN bit */
	APCI3120_PRIVDATA(pdev)->modeSelect = APCI3120_PRIVDATA(pdev)->modeSelect & APCI3120_DISABLE_SCAN;

	/* Tests if interrupt installed */
	if (APCI3120_PRIVDATA(pdev)->interruptInitialized == APCI3120_ENABLE)
	{
		/* Tests if DMA used */
		if (APCI3120_PRIVDATA(pdev)->sequence_data.dma_mode == APCI3120_DMA_USED)
		{
			/* Disables the EOC, EOS interrupt  */
			APCI3120_PRIVDATA(pdev)->modeSelect = (APCI3120_PRIVDATA(pdev)->modeSelect & APCI3120_DISABLE_EOC_INT & APCI3120_DISABLE_EOS_INT);
			APCI3120_PRIVDATA(pdev)->intMode = APCI3120_DMA_INT_MODE;
		}
		else /*if (ps_APCI3120Variable ...b_DMAUsed == APCI3120_DMA_USED)*/
		{
			/* Disables the EOC and enables the EOS interrupt */
			APCI3120_PRIVDATA(pdev)->modeSelect = (APCI3120_PRIVDATA(pdev)->modeSelect & APCI3120_DISABLE_EOC_INT ) | APCI3120_ENABLE_EOS_INT;
			APCI3120_PRIVDATA(pdev)->intMode =  APCI3120_EOS_INT_MODE;
		}/* else if (ps_APCI3120Variable ...b_DMAUsed == APCI3120_DMA_USED)*/

		/* Tests if the timer 2 interrupt is enabled */
		if (APCI3120_PRIVDATA(pdev)->t2Interrupt == APCI3120_ENABLE)
		{
			APCI3120_PRIVDATA(pdev)->modeSelect = APCI3120_PRIVDATA(pdev)->modeSelect | APCI3120_ENABLE_TIMER_INT;
		}
	}
	else /* Test if interrupt is installed*/
	{
		APCI3120_PRIVDATA(pdev)->modeSelect = APCI3120_PRIVDATA(pdev)->modeSelect & APCI3120_DISABLE_ALL_INTERRUPT;
		/* Interrupt routine has not been installed */
		return 3;
	}

	/* Writes the interrupt select register */
	outb(APCI3120_PRIVDATA(pdev)->modeSelect, GET_BAR1(pdev) + APCI3120_WRITE_MODE_SELECT);


	/* Test if DMA is used */
	if (APCI3120_PRIVDATA(pdev)->sequence_data.dma_mode == APCI3120_DMA_USED)
	{
		APCI3120_PRIVDATA(pdev)->dma_state = APCI3120_ENABLE;
		APCI3120_PRIVDATA(pdev)->lastChannel = APCI3120_MAX_INPUT_CHANNEL + 1;

		/* ENABLE BUS MASTER */
		outw(APCI3120_ADD_ON_AGCSTS_LOW, GET_BAR2(pdev));
		outw(APCI3120_ENABLE_TRANSFER_ADD_ON_LOW, GET_BAR2(pdev) + 2);
		outw(APCI3120_ADD_ON_AGCSTS_HIGH, GET_BAR2(pdev));
		outw(0x1000, GET_BAR2(pdev) + 2);

		/* A2P FIFO MANAGEMENT */
		outl(APCI3120_A2P_FIFO_MANAGEMENT, GET_BAR0(pdev) + APCI3120_AMCC_OP_MCSR);

		/* DMA Start Address Low */
		outw(APCI3120_ADD_ON_MWAR_LOW, GET_BAR2(pdev));
		outw((unsigned short)((unsigned long)APCI3120_PRIVDATA(pdev)->dma_buffer_hw & 0x0000FFFFUL), GET_BAR2(pdev) + 2);

		/* DMA Start Adress High */
		outw(APCI3120_ADD_ON_MWAR_HIGH, GET_BAR2(pdev));
		outw((unsigned short)(((unsigned long)APCI3120_PRIVDATA(pdev)->dma_buffer_hw & 0xFFFF0000UL) >> 16), GET_BAR2(pdev) + 2);

		/* Inc Buffer Index */
		APCI3120_PRIVDATA(pdev)->dma_buf_index = (APCI3120_PRIVDATA(pdev)->dma_buf_index ^ 1);  /* toggle index from 0 to 1 */

		/* Nbr of acquisition LOW */
		outw(APCI3120_ADD_ON_MWTC_LOW, GET_BAR2(pdev));
		outw((unsigned short)((APCI3120_PRIVDATA(pdev)->sequence_data.number_of_acquisition * 2) & 0xFFFF), GET_BAR2(pdev) + 2);

		/* Nbr of acquisition HIGH */
		outw(APCI3120_ADD_ON_MWTC_HIGH, GET_BAR2(pdev));
		outw((unsigned short)((APCI3120_PRIVDATA(pdev)->sequence_data.number_of_acquisition * 2 ) / 65536UL), GET_BAR2(pdev) + 2);

		/* A2P FIFO RESET */
		outl(0x04000000, GET_BAR0(pdev) + APCI3120_AMCC_OP_MCSR);

		/* A2P FIFO CONFIGURATE, END OF DMA INTERRUPT INIT */
		outl((APCI3120_FIFO_ADVANCE_ON_BYTE_2 | APCI3120_ENABLE_WRITE_TC_INT), GET_BAR0(pdev) + APCI3120_AMCC_OP_REG_INTCSR);
	} /* dma used*/

	/* Clears PA, sets PR */
	APCI3120_PRIVDATA(pdev)->nWrAddr = ((APCI3120_PRIVDATA(pdev)->nWrAddr & APCI3120_CLEAR_PA_PR)| (((unsigned short)APCI3120_PRIVDATA(pdev)->sequence_array_size - 1) << 8));

	 /* Extern Trigger */
	if (APCI3120_PRIVDATA(pdev)->sequence_data.extern_trigger == APCI3120_ENABLE)
	{
		APCI3120_PRIVDATA(pdev)->nWrAddr = (APCI3120_PRIVDATA(pdev)->nWrAddr | APCI3120_ENABLE_EXT_TRIGGER);
	}
	else
	{
		APCI3120_PRIVDATA(pdev)->nWrAddr = (APCI3120_PRIVDATA(pdev)->nWrAddr & APCI3120_DISABLE_EXT_TRIGGER);
	}

	if (APCI3120_PRIVDATA(pdev)->sequence_data.dma_mode == APCI3120_DMA_NOT_USED)
	{
		/* Sets gate 2 */
		APCI3120_PRIVDATA(pdev)->nWrAddr = APCI3120_PRIVDATA(pdev)->nWrAddr | APCI3120_ENABLE_TIMER2;
	}

	/* Tests if delay modus */
	if ((APCI3120_PRIVDATA(pdev)->sequence_data.acquisition_mode == APCI3120_DELAY_MODUS))
	{
		/* Set Gate 1 */
		APCI3120_PRIVDATA(pdev)->nWrAddr = (APCI3120_PRIVDATA(pdev)->nWrAddr | APCI3120_ENABLE_TIMER1);
	}

	/* Set Gate 0 */
	APCI3120_PRIVDATA(pdev)->nWrAddr = (APCI3120_PRIVDATA(pdev)->nWrAddr | APCI3120_ENABLE_TIMER0);

	if (APCI3120_PRIVDATA(pdev)->sequence_data.dma_mode == APCI3120_DMA_USED)
	{
		/* ENABLE A2P FIFO WRITE AND ENABLE AMWEN */
		outw(3, GET_BAR2(pdev) + 4);
		outl(0x04000000, GET_BAR0(pdev) + APCI3120_AMCC_OP_MCSR);
	}

	/* Starts timer */
	outw(APCI3120_PRIVDATA(pdev)->nWrAddr, GET_BAR1(pdev) + APCI3120_WR_ADDRESS);

	APCI3120_PRIVDATA(pdev)->lastChannel = 0;
	APCI3120_PRIVDATA(pdev)->device_state = STATE_SEQUENCE_STARTED;

	return 0;
}


/*
  +----------------------------------------------------------------------------+
  | Function's Name   : _INT_ i_APCI3120_StopAnalogInputAcquisition            |
  |                               (BYTE_    b_BoardHandle)                     |
  +----------------------------------------------------------------------------+
  | Task              : Stops the cyclic conversion . It has been started      |
  |                     previously with function                               |
  |                     "i_APCI3120_StartAnalogInputAcquisition".              |
  +----------------------------------------------------------------------------+
  | Input Parameters  : BYTE      b_BoardHandle     : Handle of board APCI3120 |
  +----------------------------------------------------------------------------+
  | Return Value      :  0: No error                                           |
  |                     1: The handle parameter of the board is wrong         |
  |                     2: The cyclic conversion has not been started.        |
  |                         "i_APCI3120_StartAnalogInputAcquisition"           |
  +----------------------------------------------------------------------------+
*/
int i_APCI3120_StopSequenceAcquisition ( struct pci_dev *pdev)
{
	unsigned char dummy_read;
	if(!pdev)
	{
		return 1; // the handle to the device is wrong
	}
	if( APCI3120_PRIVDATA(pdev)->device_state != STATE_SEQUENCE_STARTED )
	{
		return 2; // there is no sequence started at the time
	}

	/* Init FC Timer IT */
	dummy_read = inb(GET_BAR1(pdev) + APCI3120_TIMER_STATUS_REGISTER);

	/* Test if DELAY mode */
	if ((APCI3120_PRIVDATA(pdev)->sequence_data.acquisition_mode == APCI3120_DELAY_MODUS) )
	{
		APCI3120_PRIVDATA(pdev)->nWrAddr = APCI3120_PRIVDATA(pdev)->nWrAddr & APCI3120_DISABLE_TIMER0 & APCI3120_DISABLE_TIMER1;
	}
	else
	{
		APCI3120_PRIVDATA(pdev)->nWrAddr = APCI3120_PRIVDATA(pdev)->nWrAddr & APCI3120_DISABLE_TIMER0;
	}/* else if (ps_APCI3120Variable ...b_AcquisitionMode == APCI3120_DELAY_MODUS)*/

	/* Extern Trigger */
	if (APCI3120_PRIVDATA(pdev)->sequence_data.extern_trigger == APCI3120_ENABLE)
	{
		APCI3120_PRIVDATA(pdev)->nWrAddr = (APCI3120_PRIVDATA(pdev)->nWrAddr & APCI3120_DISABLE_EXT_TRIGGER);
	}

	/* Stops timer */
	outw(APCI3120_PRIVDATA(pdev)->nWrAddr, GET_BAR1(pdev) + APCI3120_WR_ADDRESS);

	/* Writes the interrupt select register */
	outb(APCI3120_PRIVDATA(pdev)->modeSelect, GET_BAR1(pdev) + APCI3120_WRITE_MODE_SELECT);
	dummy_read = inb(GET_BAR1(pdev) + APCI3120_TIMER_STATUS_REGISTER);

	/* Test if DMA is used */
	if (APCI3120_PRIVDATA(pdev)->sequence_data.dma_mode == APCI3120_ENABLE)
	{
		/* Clear Timer Write TC int16 */
		outl(APCI3120_CLEAR_WRITE_TC_INT, GET_BAR0(pdev) + APCI3120_AMCC_OP_REG_INTCSR);

		/* DISABLE END OF DMA INTERRUPT */
		outl(0, GET_BAR0(pdev) + APCI3120_AMCC_OP_REG_INTCSR);

		/* Init FC Timer IT */
		dummy_read = inb(GET_BAR1(pdev) + APCI3120_TIMER_STATUS_REGISTER);

		/* DISABLE A2P WRITE AND AMWEN */
		outw(APCI3120_DISABLE_AMWEN_AND_A2P_FIFO_WRITE, GET_BAR2(pdev) + 4);

		/* DISABLE BUS MASTER */
		outw(APCI3120_ADD_ON_AGCSTS_LOW, GET_BAR2(pdev));
		outw(APCI3120_DISABLE_BUS_MASTER_ADD_ON, GET_BAR2(pdev) + 2);
		outw(APCI3120_ADD_ON_AGCSTS_HIGH, GET_BAR2(pdev));
		outw(APCI3120_DISABLE_BUS_MASTER_ADD_ON, GET_BAR2(pdev) + 2);

		/* DISABLE BUS MASTER PCI */
		outl(APCI3120_DISABLE_BUS_MASTER_PCI, GET_BAR0(pdev) + APCI3120_AMCC_OP_MCSR);
	}

	/*pK2	(Passiert bei cmd_APCI3120_ClearAnalogInputAcquisition)		pDev->dma.acqEnable = APCI3120_DISABLE;*/
	APCI3120_PRIVDATA(pdev)->sequence_data.dma_mode = APCI3120_DISABLE;
	APCI3120_PRIVDATA(pdev)->device_state = STATE_SEQUENCE_INITIALISED;
	return 0;
}
