/** @file primary_ai.c
*
* @author Rodolphe Hilpert
*
* @date 05.01.06
*
* @version  $LastChangedRevision$
* @version $LastChangedDate$
*
* Primary functions (hardware access) for the analog input functionality of the board.
*
*/

/** @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 "xpci3009-private.h"

EXPORT_SYMBOL(i_xpci3009_InitAnalogInput);
EXPORT_SYMBOL(i_xpci3009_StartAnalogInput);
EXPORT_SYMBOL(i_xpci3009_ReadAnalogInputBit);
EXPORT_SYMBOL(i_xpci3009_ReadAnalogInputValue);

EXPORT_SYMBOL(i_xpci3009_InitAnalogInputAutoRefresh);
EXPORT_SYMBOL(i_xpci3009_StartAnalogInputAutoRefresh);
EXPORT_SYMBOL(i_xpci3009_StopAnalogInputAutoRefresh);
EXPORT_SYMBOL(i_xpci3009_ReadAnalogInputAutoRefreshValueAndCounter);
EXPORT_SYMBOL(i_xpci3009_FastReadAnalogInputAutoRefreshValueAndCounter);
EXPORT_SYMBOL(i_xpci3009_ReleaseAnalogInputAutoRefresh);

EXPORT_SYMBOL(i_xpci3009_InitAnalogInputSequence);
EXPORT_SYMBOL(i_xpci3009_StartAnalogInputSequence);
EXPORT_SYMBOL(i_xpci3009_StopAnalogInputSequence);
EXPORT_SYMBOL(i_xpci3009_ReleaseAnalogInputSequence);

EXPORT_SYMBOL(i_xpci3009_GetNumberOfAnalogInputChannels);


static inline int is_boolean(int val)
{
	switch(val)
	{
		case ADDIDATA_ENABLE:
		case ADDIDATA_DISABLE:
			return 1;
		default:
			return 0;
	}
}

/** returns the number of channels the board has
 *
 * if mode SingleDiff is not supported, the number of channels is always the same.
 * If not, it depends on the selected mode.
 *
 */

static uint8_t get_number_of_channel(struct pci_dev *pdev, int SingleDiff)
{
	if ( XPCI3009_PRIVDATA(pdev)->b_AnalogInputSeDiffConfigurationSupported == ADDIDATA_DISABLE )
	{
		return XPCI3009_PRIVDATA(pdev)->b_NumberOfAnalogInput;
	}
	else
	{
		switch(SingleDiff)
		{
			case ADDIDATA_DIFFERENTIAL:
				return (XPCI3009_PRIVDATA(pdev)->b_NumberOfAnalogInput/2);
			default:
				return XPCI3009_PRIVDATA(pdev)->b_NumberOfAnalogInput;
		}
	}
}

/* Tests the single diff flag
 *
 *  @retval 1 if OK 0 otherwise
 */
static int singlediff_is_valid(int val)
{
	switch(val)
	{
		case ADDIDATA_DIFFERENTIAL:
		case ADDIDATA_SINGLE:
			return 1;
		default:
			return 0;
	}
}
/* Tests the gain flag
 *
 *  @retval 1 if OK 0 otherwise
 */
static int gain_is_valid(int val)
{
	switch(val)
	{
		case ADDIDATA_1_GAIN:
		case ADDIDATA_2_GAIN:
		case ADDIDATA_5_GAIN:
		case ADDIDATA_10_GAIN:
			return 1;
		default:
			return 0;
	}
}
/* Tests the polarity flag
 *
 *  @retval 1 if OK 0 otherwise
 */
static int polarity_is_valid(int val)
{
	switch(val)
	{
		case ADDIDATA_UNIPOLAR:
		case ADDIDATA_BIPOLAR:
			return 1;
		default:
			return 0;
	}
}
/* Tests the convert time unit flag
 *
 *  @retval 1 if OK 0 otherwise
 */
static int converttimeunit_is_valid(int val)
{
	switch(val)
	{
		case ADDIDATA_NANO_SECOND:
		case ADDIDATA_MICRO_SECOND:
		case ADDIDATA_MILLI_SECOND:
			return 1;
		default:
			return 0;
	}
}
/* check if single/diff parameter is valid relative to the board
 *
 * if the board doesn't support configuration of this parameter,
 * then parameter must be the same as the board
 *
 * @retval 1 if OK
 * @retval 0 otherwise
 *
||  b_SingleDiff ||  b_AnalogInputSeDiffConfigurationSupported || b_AnalogInputSeDiffConfiguration || == || result ||
|| 0 ||  0 ||  0 || 1 || 1 ||
|| 0 ||  0 || 1 || 0 || 0 ||
|| 0 || 1 || 0 || - || 1 ||
|| 0 || 1 || 1 || - || 1 ||
|| 1 ||  0 || 0 || 0 || 0 ||
|| 1 ||  0 || 1 || 1 || 1 ||
|| 1 || 1 || 0 || - || 1 ||
|| 1 || 1 || 1 || - || 1 ||
 */
static int singlediff_supported(struct pci_dev *pdev, int val)
{
	if (XPCI3009_PRIVDATA(pdev)->b_AnalogInputSeDiffConfigurationSupported == ADDIDATA_ENABLE)
		return 1;
	return ( XPCI3009_PRIVDATA(pdev)->b_AnalogInputSeDiffConfiguration == val );
}

/** Get the number of analog input channels.
 *
 * @param [in] pdev				: The device to use.
 * @param [in] b_SingleDiff		: The mode of the input channels ADDIDATA_DIFFERENTIAL or ADDIDATA_SINGLE.
 *
 * @param [out] b_NbrChannels	: The number of analog input channels.
 *
 * @retval 0: No error.
 * @retval 1: The handle parameter of the board is wrong.
 * @retval 2: Wrong single/diff value
 * @retval 3: Requested single diff parameter not supported by the board
 */
int i_xpci3009_GetNumberOfAnalogInputChannels (struct pci_dev *pdev, uint8_t b_SingleDiff, uint8_t *b_NbrChannels)
{
	if (!pdev) return 1;

	{
		XPCI3009_DEBUG_FCN();
	}

	/* If no input channels */
	if (!XPCI3009_PRIVDATA(pdev)->b_NumberOfAnalogInput)
	{
		*b_NbrChannels = 0;
		return 0;
	}

	if ( !singlediff_is_valid(b_SingleDiff) )
		return 2;

	if( !singlediff_supported(pdev, b_SingleDiff) )
		return 3;

	*b_NbrChannels = get_number_of_channel(pdev, b_SingleDiff);

	return 0;
}

/**
 *
 * Initialises one analog input channel
 *
 * @param[in]  pdev : PCI Device struct              \n
 * @param[in]   b_Channel      : channel number of the input    \n
 *                                              to be initialise               \n
 * @param[in]   b_SingleDiff   : 0 : Single ended HW config     \n
 *                                              1 : Differential HW config     \n
 * @param[in]   b_Gain         : Gain factor of the analog input\n
 *                                              - ADDIDATA_1_GAIN : Gain =  1  \n
 *                                              - ADDIDATA_2_GAIN : Gain =  2  \n
 *                                              - ADDIDATA_5_GAIN : Gain =  5  \n
 *                                              - ADDIDATA_10_GAIN: Gain = 10  \n
 * @param[in]   b_Polarity     : Polarity of the analog input:  \n
 *                                              - ADDIDATA_UNIPOLAR:  0V - +10V\n
 *                                              - ADDIDATA_BIPOLAR: -10V - +10V\n
 *
 *
 * @retval 0 No error
 * @retval 1 The handle parameter of the board is wrong
 * @retval 2 Wrong channel number
 * @retval 3 Wrong single/diff value
 * @retval 4 Wrong gain
 * @retval 5 Wrong polarity
 * @retval 6 A conversion is already started
 * @retval 7 Requested single/diff parameter not supported by the board
 */
int i_xpci3009_InitAnalogInput (struct pci_dev * pdev,
                                uint8_t   b_Channel,
                                uint8_t   b_SingleDiff,
                                uint8_t   b_Gain,
                                uint8_t   b_Polarity)
{
	uint32_t  dw_ResetMask = 0;
	uint32_t  dw_SetMask = 0;
	uint32_t  dw_WriteValueDWord = 0;
	uint32_t  dw_ReadMaskDWord = 0;
	uint32_t  dw_ReadValueDWord = 0;

	if (!pdev)
		return 1;

	{
		XPCI3009_DEBUG_FCN();
	}

	if( !singlediff_is_valid(b_SingleDiff) )
		return 3;

	if( !singlediff_supported(pdev, b_SingleDiff) )
		return 7;

	/* check b_Channel */

	/* constraint: if the channels are not in differential mode, b_Channel : 0 .. b_NumberOfAnalogInput -1*/
	/* constraint: if the channels are in differential mode, b_Channel : 0 .. b_NumberOfAnalogInput/2 -1*/

	if( b_Channel >= get_number_of_channel(pdev, b_SingleDiff) )
		return 2;

	/* Tests the gain */
	if( !gain_is_valid(b_Gain) )
		return 4;

	/* Tests the polarity */
	if( !polarity_is_valid(b_Polarity) )
		return 5;

	dw_ReadMaskDWord = 0x80000;
	INPDW_MEM(GET_MEM_BAR3(pdev), 8, &dw_ReadValueDWord);
	dw_ReadValueDWord = dw_ReadValueDWord & dw_ReadMaskDWord;

	if (dw_ReadValueDWord == 0x00080000)
		/* Conversion started */
		return 6;

	// Select the configuration index
	dw_SetMask = (uint32_t) b_Channel;
	dw_ResetMask = 0x21FFUL;
	INPDW_MEM(GET_MEM_BAR3(pdev), 4, &dw_WriteValueDWord);
	dw_WriteValueDWord = (dw_WriteValueDWord & (0xFFFFFFFFUL - dw_ResetMask)) | dw_SetMask;
	OUTPDW_MEM(GET_MEM_BAR3(pdev), 4, dw_WriteValueDWord);

//	i_ErrorCode = SET_RESET_VALUE
//					  (b_CallLevel,
//					   ps_HardwareInformation,
//					   4,
//					   (uint32_t) b_Channel,
//					   0x21FFUL,
//					   0,
//					   NULL,
//					   0,
//					   0,
//					   NULL);

	// Set the configuration
	dw_SetMask = (uint32_t)((uint32_t)b_Gain | (uint32_t)((uint32_t)b_Polarity << 6) | (uint32_t)((uint32_t)b_SingleDiff << 7));
	dw_ResetMask = 0xFFUL;
	INPDW_MEM(GET_MEM_BAR3(pdev), 0, &dw_WriteValueDWord);
	dw_WriteValueDWord = (dw_WriteValueDWord & (0xFFFFFFFFUL - dw_ResetMask)) | dw_SetMask;
	OUTPDW_MEM(GET_MEM_BAR3(pdev), 0, dw_WriteValueDWord);

//	i_ErrorCode = SET_RESET_VALUE
//					  (b_CallLevel,
//					   ps_HardwareInformation,
//					   0,
//					   (uint32_t)((uint32_t)b_Gain | (uint32_t)((uint32_t)b_Polarity << 6) | (uint32_t)((uint32_t)b_SingleDiff << 7)),
//					   0xFFUL,
//					   0,
//					   NULL,
//					   0,
//					   0,
//					   NULL);

	// Select the channel index
	dw_SetMask = (uint32_t) (0x100UL | b_Channel);
	dw_ResetMask = 0x20FFUL;
	INPDW_MEM(GET_MEM_BAR3(pdev), 4, &dw_WriteValueDWord);
	dw_WriteValueDWord = (dw_WriteValueDWord & (0xFFFFFFFFUL - dw_ResetMask)) | dw_SetMask;
	OUTPDW_MEM(GET_MEM_BAR3(pdev), 4, dw_WriteValueDWord);

//	i_ErrorCode = SET_RESET_VALUE
//					  (b_CallLevel,
//					   ps_HardwareInformation,
//					   4,
//					   (uint32_t) (0x100UL | b_Channel),
//					   0x20FFUL,
//					   0,
//					   NULL,
//					   0,
//					   0,
//					   NULL);

	// Set the channel
	dw_SetMask = (uint32_t) b_Channel;
	dw_ResetMask = 0xFFUL;
	INPDW_MEM(GET_MEM_BAR3(pdev), 0, &dw_WriteValueDWord);
	dw_WriteValueDWord = (dw_WriteValueDWord & (0xFFFFFFFFUL - dw_ResetMask)) | dw_SetMask;
	OUTPDW_MEM(GET_MEM_BAR3(pdev), 0, dw_WriteValueDWord);

//	i_ErrorCode = SET_RESET_VALUE
//					  (b_CallLevel,
//					   ps_HardwareInformation,
//					   0,
//					   (uint32_t) b_Channel,
//					   0xFFUL,
//					   0,
//					   NULL,
//					   0,
//					   0,
//					   NULL);

	XPCI3009_PRIVDATA(pdev)->b_AnalogInputSeDiffConfiguration = b_SingleDiff;
	XPCI3009_PRIVDATA(pdev)->b_AnalogInputChannelInitialised[b_Channel] = ADDIDATA_ENABLE;

	return 0;
}


/**
 *
 * Starts the conversion of one analog input channel
 *
 * @param[in]  pdev : PCI Device struct              \n
 * @param[in]   b_Channel      : channel number of the input    \n
 *                                              to be started                  \n
 * @param[in]   b_ConvertTimeUnit : Convert time unit for the   \n
 *                                              analog input:                  \n
 *                                              - ADDIDATA_NANO_SECOND  :nanos \n
 *                                              - ADDIDATA_MICRO_SECOND :micros\n
 *                                              - ADDIDATA_MILLI_SECOND :ms    \n
 * @param[in]   b_ConvertTime : Convert time value for the  \n
 *                                              analog input conversion        \n
 *
 *
 * @retval 0 No error
 * @retval 1 The handle parameter of the board is wrong
 * @retval 2 Wrong channel number
 * @retval 3 Channel not initialised
 * @retval 4 Wrong convert time unit
 * @retval 5 Wrong convert time
 * @retval 6 A conversion is already started
 */
int i_xpci3009_StartAnalogInput (struct pci_dev * pdev,
                                uint8_t   b_Channel,
  		                		uint8_t   b_ConvertTimeUnit,
		                		uint16_t   w_ConvertTime)
{

	uint32_t  dw_ResetMask = 0;
	uint32_t  dw_SetMask = 0;
	uint32_t  dw_WriteValueDWord = 0;
	uint32_t  dw_ReadValueDWord = 0;
	uint32_t  dw_ReadMaskDWord = 0;
	uint32_t  dw_CommandRegister = 0;

	if (!pdev) return 1;

	{
		XPCI3009_DEBUG_FCN();
	}

	if( b_Channel >= get_number_of_channel(pdev, XPCI3009_PRIVDATA(pdev)->b_AnalogInputSeDiffConfiguration) )
		return 2;

	if (XPCI3009_PRIVDATA(pdev)->b_AnalogInputChannelInitialised[b_Channel] != ADDIDATA_ENABLE)
		return 3;

	/* Tests the convert time unit */
	if( !converttimeunit_is_valid(b_ConvertTimeUnit) )
		/* constraint: b_ConvertTimeUnit : ( ADDIDATA_NANO_SECOND,ADDIDATA_MICRO_SECOND,ADDIDATA_MILLI_SECOND ) */
		return 4;

	/* Tests if the convert time unit is supported */
	if ( ((b_ConvertTimeUnit == ADDIDATA_NANO_SECOND) && (0 == XPCI3009_PRIVDATA(pdev)->dw_MaxConvertTimeForNanoSecond) && (0 == XPCI3009_PRIVDATA(pdev)->dw_MinConvertTimeForNanoSecond))
		|| ((b_ConvertTimeUnit == ADDIDATA_MICRO_SECOND) && (0 == XPCI3009_PRIVDATA(pdev)->dw_MaxConvertTimeForMicroSecond) && (0 == XPCI3009_PRIVDATA(pdev)->dw_MinConvertTimeForMicroSecond))
		|| ((b_ConvertTimeUnit == ADDIDATA_MILLI_SECOND) && (0 == XPCI3009_PRIVDATA(pdev)->dw_MaxConvertTimeForMilliSecond) && (0 == XPCI3009_PRIVDATA(pdev)->dw_MinConvertTimeForMilliSecond)) )
		return 4;

	/* Tests the convert time */
	if ( ((b_ConvertTimeUnit == ADDIDATA_NANO_SECOND) && (w_ConvertTime > XPCI3009_PRIVDATA(pdev)->dw_MaxConvertTimeForNanoSecond) && (w_ConvertTime < XPCI3009_PRIVDATA(pdev)->dw_MinConvertTimeForNanoSecond))
		|| ((b_ConvertTimeUnit == ADDIDATA_MICRO_SECOND) && (w_ConvertTime > XPCI3009_PRIVDATA(pdev)->dw_MaxConvertTimeForMicroSecond) && (w_ConvertTime < XPCI3009_PRIVDATA(pdev)->dw_MinConvertTimeForMicroSecond))
		|| ((b_ConvertTimeUnit == ADDIDATA_MILLI_SECOND) && (w_ConvertTime > XPCI3009_PRIVDATA(pdev)->dw_MaxConvertTimeForMilliSecond) && (w_ConvertTime < XPCI3009_PRIVDATA(pdev)->dw_MinConvertTimeForMilliSecond)) )
		return 5;

	dw_ReadMaskDWord = 0x80000;
	INPDW_MEM(GET_MEM_BAR3(pdev), 8, &dw_ReadValueDWord);
	dw_ReadValueDWord = dw_ReadValueDWord & dw_ReadMaskDWord;

	if (dw_ReadValueDWord == 0x00080000)
		/* Conversion started */
		return 6;

	// Set the conversion time base
	dw_SetMask = (uint32_t)w_ConvertTime;
	dw_ResetMask = 0xFFFFFFFFUL;
	INPDW_MEM(GET_MEM_BAR3(pdev), 32, &dw_WriteValueDWord);
	dw_WriteValueDWord = (dw_WriteValueDWord & (0xFFFFFFFFUL - dw_ResetMask)) | dw_SetMask;
	OUTPDW_MEM(GET_MEM_BAR3(pdev), 32, dw_WriteValueDWord);

//	i_ErrorCode = SET_RESET_VALUE
//	              (b_CallLevel,
//	               ps_HardwareInformation,
//	               32,
//	               dw_ConversionReloadValue,
//	               0xFFFFFFFFUL,
//	               0,
//	               NULL,
//	               0,
//	               0,
//	               NULL);

	// Set the conversion time unit
	dw_SetMask = (uint32_t)b_ConvertTimeUnit;
	dw_ResetMask = 0xFFFFFFFFUL;
	INPDW_MEM(GET_MEM_BAR3(pdev), 36, &dw_WriteValueDWord);
	dw_WriteValueDWord = (dw_WriteValueDWord & (0xFFFFFFFFUL - dw_ResetMask)) | dw_SetMask;
	OUTPDW_MEM(GET_MEM_BAR3(pdev), 36, dw_WriteValueDWord);

//	i_ErrorCode = SET_RESET_VALUE
//	              (b_CallLevel,
//	               ps_HardwareInformation,
//	               36,
//	               (uint32_t) b_ConversionTimeBase,
//	               0xFFFFFFFFUL,
//	               0,
//	               NULL,
//	               0,
//	               0,
//	               NULL);

	/* Select the channel index */
	dw_SetMask = 0x100 | b_Channel;
	dw_ResetMask = 0x2FF;
	INPDW_MEM(GET_MEM_BAR3(pdev), 4, &dw_WriteValueDWord);
	dw_WriteValueDWord = (dw_WriteValueDWord & (0xFFFFFFFFUL - dw_ResetMask)) | dw_SetMask;
	OUTPDW_MEM(GET_MEM_BAR3(pdev), 4, dw_WriteValueDWord);

	/* Set the channel */
	dw_SetMask = b_Channel;
	dw_ResetMask = 0xFF;
	INPDW_MEM(GET_MEM_BAR3(pdev), 0, &dw_WriteValueDWord);
	dw_WriteValueDWord = (dw_WriteValueDWord & (0xFFFFFFFFUL - dw_ResetMask)) | dw_SetMask;
	OUTPDW_MEM(GET_MEM_BAR3(pdev), 0, dw_WriteValueDWord);

	/* Disable the sequence initialisation */
	dw_SetMask = b_Channel;
	dw_ResetMask = 0x1FF;
	INPDW_MEM(GET_MEM_BAR3(pdev), 4, &dw_WriteValueDWord);
	dw_WriteValueDWord = (dw_WriteValueDWord & (0xFFFFFFFFUL - dw_ResetMask)) | dw_SetMask;
	OUTPDW_MEM(GET_MEM_BAR3(pdev), 4, dw_WriteValueDWord);

	/* Clear the FIFO */
	dw_SetMask = 0x10000;
	dw_ResetMask = 0;
	INPDW_MEM(GET_MEM_BAR3(pdev), 12, &dw_WriteValueDWord);
	dw_WriteValueDWord = (dw_WriteValueDWord & (0xFFFFFFFFUL - dw_ResetMask)) | dw_SetMask;
	OUTPDW_MEM(GET_MEM_BAR3(pdev), 12, dw_WriteValueDWord);

	/* Set the number of sequence to 1 */
	OUTPDW_MEM(GET_MEM_BAR3(pdev), 48, 1);

	/* Write the command register */
	dw_CommandRegister = (uint32_t)((uint32_t)b_Channel | (uint32_t)((uint32_t)b_Channel << 8) | (uint32_t)0x80000);
	OUTPDW_MEM(GET_MEM_BAR3(pdev), 8, dw_CommandRegister);

	return 0;
}

/**
 *
 * Reads the EOC Bit of the analog input conversion
 *
 * @param[in]  pdev : PCI Device struct              \n
 *
 * @param[out] pb_AnalogInputConversionFinished:                \n
 *                                   0: no analog input conversion finished    \n
 *                                   1: analog input conversion finished       \n
 *
 * @retval 0 No error
 * @retval 1 The handle parameter of the board is wrong
 *
 */
int i_xpci3009_ReadAnalogInputBit (struct pci_dev * pdev, uint8_t* pb_AnalogInputConversionFinished)
{
	uint32_t dw_ReadValueDWord = 0;

	if (!pdev) return 1;

	{
		XPCI3009_DEBUG_FCN();
	}

	/* Read the EOC Status bit */
	/* EOC is B0 of dw_ReadValueDWord ? dw_ReadValueDWord & 0x1 */
	INPDW_MEM(GET_MEM_BAR3(pdev), 20, &dw_ReadValueDWord);

	/* Returns -2 if the EOC flag is not ready */
	if ( (dw_ReadValueDWord & ADDIDATA_EOC) == ADDIDATA_EOC )
		*pb_AnalogInputConversionFinished = 1;
	else
		*pb_AnalogInputConversionFinished = 0;

	return 0;
}

/**
 *
 * Reads the value of the analog input channel
 *
 * @param[in]  pdev : PCI Device struct              \n
 *
 * @param[out] pdw_ReadValue : Value of the analog input       \n
 *                                             channel                         \n
 *
 * @retval 0 No error
 * @retval 1 The handle parameter of the board is wrong
 */
int i_xpci3009_ReadAnalogInputValue (struct pci_dev * pdev,
                                     uint32_t* pdw_ReadValue)
{
	if (!pdev) return 1;

	{
		XPCI3009_DEBUG_FCN();
	}

	/* Read the digital value of the input */
	/* Value is B0?B31 dw_ReadValueDWord ? dw_ReadValueDWord & 0xFFFFFFFF */
	INPDW_MEM(GET_MEM_BAR3(pdev), 28, pdw_ReadValue);

	return 0;
}

static void initialize_delay(struct pci_dev * pdev, uint8_t b_DelayMode, uint8_t b_DelayTimeUnit, uint16_t w_DelayTime)
{
	uint32_t	dw_ResetMask = 0;
	uint32_t	dw_SetMask = 0;
	uint32_t	dw_WriteValueDWord = 0;

	if (b_DelayMode != ADDIDATA_DISABLE)
		{

		/* Set the delay mode */
		dw_SetMask = (uint32_t) ((uint32_t) (b_DelayMode - 1) << 14);
		dw_ResetMask = 0x1D000UL;
		INPDW_MEM(GET_MEM_BAR3(pdev), 4, &dw_WriteValueDWord);
		dw_WriteValueDWord = (dw_WriteValueDWord & (0xFFFFFFFFUL - dw_ResetMask)) | dw_SetMask;
		OUTPDW_MEM(GET_MEM_BAR3(pdev), 4, dw_WriteValueDWord);
		/*SET_RESET_VALUE
		                  (pdw_Address,
		                   4,
		                   (uint32_t) ((uint32_t) (b_DelayMode - 1) << 14),
		                   0x1D000UL);*/

		/* Set the delay time base */
		dw_SetMask = (uint32_t) b_DelayTimeUnit;
		dw_ResetMask = 0xFFFFFFFFUL;
		INPDW_MEM(GET_MEM_BAR3(pdev), 44, &dw_WriteValueDWord);
		dw_WriteValueDWord = (dw_WriteValueDWord & (0xFFFFFFFFUL - dw_ResetMask)) | dw_SetMask;
		OUTPDW_MEM(GET_MEM_BAR3(pdev), 44, dw_WriteValueDWord);
		/*SET_RESET_VALUE
		                  (pdw_Address,
		                   44,
		                   (uint32_t) b_DelayTimeBase,
		                   0xFFFFFFFFUL);*/

		/* Set the delay reload value */
		dw_SetMask = w_DelayTime;
		dw_ResetMask = 0xFFFFFFFFUL;
		INPDW_MEM(GET_MEM_BAR3(pdev), 40, &dw_WriteValueDWord);
		dw_WriteValueDWord = (dw_WriteValueDWord & (0xFFFFFFFFUL - dw_ResetMask)) | dw_SetMask;
		OUTPDW_MEM(GET_MEM_BAR3(pdev), 40, dw_WriteValueDWord);
		/*SET_RESET_VALUE
		                  (pdw_Address,
		                   40,
		                   w_DelayTime,
		                   0xFFFFFFFFUL);*/

		/* Enable the delay */
		dw_SetMask = 0x40000UL;
		dw_ResetMask = 0x0UL;
		INPDW_MEM(GET_MEM_BAR3(pdev), 8, &dw_WriteValueDWord);
		dw_WriteValueDWord = (dw_WriteValueDWord & (0xFFFFFFFFUL - dw_ResetMask)) | dw_SetMask;
		OUTPDW_MEM(GET_MEM_BAR3(pdev), 8, dw_WriteValueDWord);
		/*SET_RESET_VALUE
		                  (pdw_Address,
		                   8,
		                   0x40000UL,
		                   0x0UL);*/
		}
	else
		{

	   	/* Disable the delay*/
		dw_SetMask = 0x0UL;
		dw_ResetMask = 0x40000UL;
		INPDW_MEM(GET_MEM_BAR3(pdev), 8, &dw_WriteValueDWord);
		dw_WriteValueDWord = (dw_WriteValueDWord & (0xFFFFFFFFUL - dw_ResetMask)) | dw_SetMask;
		OUTPDW_MEM(GET_MEM_BAR3(pdev), 8, dw_WriteValueDWord);
	   	/*SET_RESET_VALUE
	                  (pdw_Address,
	                   0x8,
	                   0x0UL,
	                   0x40000UL);*/
		}
}

/* there is internally in the PLD two configuration arrays:
 * - one that holds the channel configuration (gain/polarity/(single/diff)mode) for each channel
 * - one that holds the aquisition sequence
 *
 * The sequence acquisition channel is an array of 16 entries that must contain the index of the channels
 * to acquire.
 * During the acquisition this array is run through and the relevant channel is acquired.
 *
 * This means that it is legal to specify twice or more the same value.
 */
static void initialize_channels(struct pci_dev * pdev, uint8_t b_NumberOfChannels, uint8_t* pb_ChannelList, uint8_t* pb_Gain, uint8_t* pb_Polarity,	uint8_t b_SingleDiff)
{
	uint32_t	dw_ResetMask = 0;
	uint32_t	dw_SetMask = 0;
	uint32_t	dw_WriteValueDWord = 0;
	uint8_t	b_ChannelCounter = 0;

	for (b_ChannelCounter = 0 ; b_ChannelCounter < b_NumberOfChannels ; b_ChannelCounter++)
	{
		// Select the configuration index
		dw_SetMask = (uint32_t) b_ChannelCounter;
		dw_ResetMask = 0x21FFUL;
		INPDW_MEM(GET_MEM_BAR3(pdev), 4, &dw_WriteValueDWord);
		dw_WriteValueDWord = (dw_WriteValueDWord & (0xFFFFFFFFUL - dw_ResetMask)) | dw_SetMask;
		OUTPDW_MEM(GET_MEM_BAR3(pdev), 4, dw_WriteValueDWord);

		// Set the configuration
		dw_SetMask = (uint32_t)((uint32_t)pb_Gain[b_ChannelCounter] | (uint32_t)((uint32_t)pb_Polarity[b_ChannelCounter] << 6) | (uint32_t)((uint32_t)b_SingleDiff << 7));
		dw_ResetMask = 0xFFUL;
		INPDW_MEM(GET_MEM_BAR3(pdev), 0, &dw_WriteValueDWord);
		dw_WriteValueDWord = (dw_WriteValueDWord & (0xFFFFFFFFUL - dw_ResetMask)) | dw_SetMask;
		OUTPDW_MEM(GET_MEM_BAR3(pdev), 0, dw_WriteValueDWord);

		// Select the channel index
		dw_SetMask = (uint32_t) (0x100UL | b_ChannelCounter);
		dw_ResetMask = 0x20FFUL;
		INPDW_MEM(GET_MEM_BAR3(pdev), 4, &dw_WriteValueDWord);
		dw_WriteValueDWord = (dw_WriteValueDWord & (0xFFFFFFFFUL - dw_ResetMask)) | dw_SetMask;
		OUTPDW_MEM(GET_MEM_BAR3(pdev), 4, dw_WriteValueDWord);

		// Set the channel
		dw_SetMask = (uint32_t) pb_ChannelList[b_ChannelCounter];
		dw_ResetMask = 0xFFUL;
		INPDW_MEM(GET_MEM_BAR3(pdev), 0, &dw_WriteValueDWord);
		dw_WriteValueDWord = (dw_WriteValueDWord & (0xFFFFFFFFUL - dw_ResetMask)) | dw_SetMask;
		OUTPDW_MEM(GET_MEM_BAR3(pdev), 0, dw_WriteValueDWord);
	}
}

/** test the validity of NumberOfChannel parameter */
/* @retval 0 if OK, 1 otherwise */
static int test_NumberOfChannel(struct pci_dev * pdev, unsigned char b_NumberOfChannels, uint8_t b_SingleDiff)
{
	/* constraint: 0 < NumberOfChannel <= ADDIDATA_MAX_AI */
	switch(b_NumberOfChannels)
	{
		case 1 ... ADDIDATA_MAX_AI:
			break; /* OK */
		default:
			return 1;
	}

	if(	b_NumberOfChannels > get_number_of_channel(pdev,b_SingleDiff) )
		return 1;

	return 0;
}

/* Tests content of pb_ChannelList */

/* @retval 0 if OK, 1 otherwise */
static int test_pb_ChannelList(struct pci_dev * pdev, uint8_t b_SingleDiff, uint8_t b_NumberOfChannels, uint8_t* pb_ChannelList)
{
	uint8_t channelnb = get_number_of_channel(pdev,b_SingleDiff);

	{
		int i;
		for (i = 0 ; i < b_NumberOfChannels ; i++)
		{
			/* constraint: pb_ChannelList[i] : ( 0 .. channelnb) */
			if ( (uint8_t)pb_ChannelList[i] < channelnb )
				continue; /* OK */
			return 2;
		}
	}

	return 0;
}
/*******************/
/**  AUTOREFRESH  **/
/*******************/

/**
 *
 * Initialise an analog input auto refresh conversion
 *
 * @param[in]  pdev : PCI Device struct              \n
 * @param[in]    b_NumberOfChannels : Number of channel to      \n
 *                                              initialise (<=ADDIDATA_MAX_AI) \n
 * @param[in]  pb_ChannelList : List of the channels           \n
 *                                                  to initialise              \n
 * @param[in]  pb_Gain     : Gain factor array of the channels \n
 *                                          - ADDIDATA_1_GAIN : Gain =  1      \n
 *                                          - ADDIDATA_2_GAIN : Gain =  2      \n
 *                                          - ADDIDATA_5_GAIN : Gain =  5      \n
 *                                          - ADDIDATA_10_GAIN: Gain = 10      \n
 * @param[in]  pb_Polarity   : Polarity array of the channels: \n
 *                                            - ADDIDATA_UNIPOLAR:  0V - +10V  \n
 *                                            - ADDIDATA_BIPOLAR: -10V - +10V  \n
 * @param[in]   b_SingleDiff : 0 : Single ended HW config      \n
 *                                             1 : Differential HW config      \n
 * @param[in]  dw_NumberOfSequence :   0: continuous mode      \n
 *                                                   <>0: single mode          \n
 * @param[in]   b_DelayMode     : Delay mode for the            \n
 *                                               analog input:                 \n
 *                                              - ADDIDATA_DELAY_MODE_1: Mode 1\n
 *                                              - ADDIDATA_DELAY_MODE_2: Mode 2\n
 * @param[in]   b_DelayTimeUnit : Delay time unit for the       \n
 *                                              analog input:                  \n
 *                                              - ADDIDATA_MICRO_SECOND :micros\n
 *                                              - ADDIDATA_MILLI_SECOND :ms    \n
 *                                              - ADDIDATA_SECOND :s           \n
 * @param[in]   w_DelayTime : Delay time value for the          \n
 *                                              analog input conversion        \n
 *
 *
 * @retval 0  No error
 * @retval 1  The handle parameter of the board is wrong
 * @retval 2  Wrong number of channels
 * @retval 3  Wrong channel number
 * @retval 4  Wrong gain factor
 * @retval 5  Wrong polarity
 * @retval 6  Wrong single diff parameter (not 0 or 1)
 * @retval 7  Wrong delay mode parameter
 * @retval 8  Wrong delay time unit parameter
 * @retval 9  Wrong delay time parameter
 * @retval 10 A conversion is already started
 * @retval 11 requested single diff parameter not supported by the board
 *
 * @note
 *
 * <b>More on b_NumberOfChannels</b>\n \n
 *
 * b_NumberOfChannels describes the sequence to follow during acquisition. It must contain indexes of valid channels.
 * Which index is valid depends on the connection mode of the board (single or differential).
 *
 * The order of the index in this array have no influence on the order of the values
 * returned after an acquisition.
 *
 * <b>Constraints on parameter NumberOfChannel</b>\n\n
 * let NumberOfAnalogInput be the number of analog inputs (4,8,16) of the board, then NumberOfChannel is correct if
 * - 0 < NumberOfChannel <= ADDIDATA_MAX_AI
 * - if the board is in differential mode, NumberOfChannel < (b_NumberOfAnalogInput/2)
 * - if the board is in single mode, NumberOfChannel < b_NumberOfAnalogInput
 *
 *
 * <b>Constraints on parameter pb_ChannelList</b>\n\n
 * let NumberOfAnalogInput be the number of analog inputs (4,8,16) of the board, then NumberOfChannel is correct if:
 * - if the board is in differencial mode, for each value in pb_ChannelList, value < (b_NumberOfAnalogInput/2)
 * - if the board is in single mode, for each value in pb_ChannelList, value < b_NumberOfAnalogInput
 */
int i_xpci3009_InitAnalogInputAutoRefresh (struct pci_dev * pdev,
                                            uint8_t   b_NumberOfChannels,
                                            uint8_t*  pb_ChannelList,
			                                uint8_t*  pb_Gain,
			                                uint8_t*  pb_Polarity,
			                                uint8_t    b_SingleDiff,
                                            uint32_t  dw_NumberOfSequence,
                                            uint8_t    b_DelayMode,
                                            uint8_t    b_DelayTimeUnit,
                                            uint16_t    w_DelayTime)
{
	uint32_t  dw_ResetMask = 0;
	uint32_t  dw_SetMask = 0;
	uint32_t dw_WriteValueDWord = 0;
	uint32_t dw_ReadValueDWord = 0;
	uint32_t dw_ReadMaskDWord = 0;

	if (!pdev) return 1;

	{

		XPCI3009_DEBUG_FCN();
		XPCI3009_DEBUG_INTARRAY("pb_ChannelList",0, b_NumberOfChannels,pb_ChannelList);
		XPCI3009_DEBUG_INTARRAY("pb_Gain",0, b_NumberOfChannels,pb_Gain);
		XPCI3009_DEBUG_INTARRAY("pb_Polarity",0, b_NumberOfChannels,pb_Polarity);
		XPCI3009_DEBUG("b_SingleDiff: %u\n",b_SingleDiff);
		XPCI3009_DEBUG("dw_NumberOfSequence: %u\n",dw_NumberOfSequence);
		XPCI3009_DEBUG("b_DelayMode: %u\n",b_DelayMode);
		XPCI3009_DEBUG("b_DelayTimeUnit: %u\n",b_DelayTimeUnit);
		XPCI3009_DEBUG("w_DelayTime: %u\n",w_DelayTime);
	}

	/* Tests the single diff flag */
	if( !singlediff_is_valid(b_SingleDiff) )
		return 6;

	if( !singlediff_supported(pdev, b_SingleDiff) )
		return 11;

	/* Tests the number of channels */
	if(test_NumberOfChannel(pdev, b_NumberOfChannels, b_SingleDiff))
		return 3;

	/* Tests content of pb_ChannelList */
	if( test_pb_ChannelList(pdev, b_SingleDiff, b_NumberOfChannels, pb_ChannelList) )
		return 2;

	{
		uint8_t   b_ChannelCounter = 0;
		for (b_ChannelCounter = 0 ; b_ChannelCounter < b_NumberOfChannels ; b_ChannelCounter++)
		{
			/* Tests the gain */
			if(!gain_is_valid(pb_Gain[b_ChannelCounter]))
				return 4;

			if(!polarity_is_valid(pb_Polarity[b_ChannelCounter]))
				return 5;
		}
	}

	/* Tests the delay mode flag */
	if ((b_DelayMode != ADDIDATA_DISABLE) && (b_DelayMode != ADDIDATA_DELAY_MODE_1) && (b_DelayMode != ADDIDATA_DELAY_MODE_2))
		return 7;

	if (b_DelayMode != ADDIDATA_DISABLE)
		{
		/* Tests the delay time unit */
		if ( (b_DelayTimeUnit != ADDIDATA_MICRO_SECOND) && (b_DelayTimeUnit != ADDIDATA_MILLI_SECOND) && (b_DelayTimeUnit != ADDIDATA_SECOND) )
			return 8;

		/* Tests if the delay time unit is supported */
		if ( ((b_DelayTimeUnit == ADDIDATA_MICRO_SECOND) && (0 == XPCI3009_PRIVDATA(pdev)->dw_MaxDelayTimeForMicroSecond) && (0 == XPCI3009_PRIVDATA(pdev)->dw_MinDelayTimeForMicroSecond))
			|| ((b_DelayTimeUnit == ADDIDATA_MILLI_SECOND) && (0 == XPCI3009_PRIVDATA(pdev)->dw_MaxDelayTimeForMilliSecond) && (0 == XPCI3009_PRIVDATA(pdev)->dw_MinDelayTimeForMilliSecond))
			|| ((b_DelayTimeUnit == ADDIDATA_SECOND) && (0 == XPCI3009_PRIVDATA(pdev)->dw_MaxDelayTimeForSecond) && (0 == XPCI3009_PRIVDATA(pdev)->dw_MinDelayTimeForSecond)) )
			return 8;

		/* Tests the delay time */
		if ( ((b_DelayTimeUnit == ADDIDATA_MICRO_SECOND) && (w_DelayTime > XPCI3009_PRIVDATA(pdev)->dw_MaxDelayTimeForMicroSecond) && (w_DelayTime < XPCI3009_PRIVDATA(pdev)->dw_MinDelayTimeForMicroSecond))
			|| ((b_DelayTimeUnit == ADDIDATA_MILLI_SECOND) && (w_DelayTime > XPCI3009_PRIVDATA(pdev)->dw_MaxDelayTimeForMilliSecond) && (w_DelayTime < XPCI3009_PRIVDATA(pdev)->dw_MinDelayTimeForMilliSecond))
			|| ((b_DelayTimeUnit == ADDIDATA_SECOND) && (w_DelayTime > XPCI3009_PRIVDATA(pdev)->dw_MaxDelayTimeForSecond) && (w_DelayTime < XPCI3009_PRIVDATA(pdev)->dw_MinDelayTimeForSecond)) )
			return 9;
		}

	dw_ReadMaskDWord = 0x80000;
	INPDW_MEM(GET_MEM_BAR3(pdev), 8, &dw_ReadValueDWord);
	dw_ReadValueDWord = dw_ReadValueDWord & dw_ReadMaskDWord;

	if (dw_ReadValueDWord == 0x00080000)
		/* Conversion started */
		return 10;

	initialize_delay(pdev, b_DelayMode, b_DelayTimeUnit, w_DelayTime);
	initialize_channels(pdev, b_NumberOfChannels, pb_ChannelList, pb_Gain, pb_Polarity,b_SingleDiff);

	/* Set the first and last sequence index */

	/* Set the start sequence index */
	INPDW_MEM(GET_MEM_BAR3(pdev), 8, &dw_WriteValueDWord);
	dw_WriteValueDWord &= ~ANALOG_SEQUENCE_START_BITMASK;

	/* The first channel */
//	dw_WriteValueDWord |= b_FirstChannel;
	dw_WriteValueDWord |= 0;
	OUTPDW_MEM(GET_MEM_BAR3(pdev), 8, dw_WriteValueDWord);

	/* Set the stop sequence index */
	INPDW_MEM(GET_MEM_BAR3(pdev), 8, &dw_WriteValueDWord);
	dw_WriteValueDWord &= ~ANALOG_SEQUENCE_STOP_BITMASK;

	/* Set the last channel */
//	dw_WriteValueDWord |= (b_LastChannel << 8);
	dw_WriteValueDWord |= ((b_NumberOfChannels - 1) << 8);
	OUTPDW_MEM(GET_MEM_BAR3(pdev), 8, dw_WriteValueDWord);

//	/*i_ErrorCode = SET_RESET_VALUE
//	               (b_CallLevel,
//	                ps_HardwareInformation,
//	                8,
//	                (uint32_t) (((uint32_t) b_NumberOfChannels - 1) << 8),
//	                0xFFFFUL,
//	                0,
//	                NULL,
//	                0,
//	                0,
//	                NULL);*/

	/* Test if continuous mode */
	if (dw_NumberOfSequence == 0)
		{
		/* Set the continous mode and disable the DMA */
		dw_SetMask = 0x10000UL;
		dw_ResetMask = 0x230000UL;
		INPDW_MEM(GET_MEM_BAR3(pdev), 8, &dw_WriteValueDWord);
		dw_WriteValueDWord = (dw_WriteValueDWord & (0xFFFFFFFFUL - dw_ResetMask)) | dw_SetMask;
		OUTPDW_MEM(GET_MEM_BAR3(pdev), 8, dw_WriteValueDWord);

	    /*i_ErrorCode = SET_RESET_VALUE
	                  (b_CallLevel,
	                   ps_HardwareInformation,
	                   8,
	                   0x10000UL,
	                   0x230000UL,
	                   0,
	                   NULL,
	                   0,
	                   0,
	                   NULL);*/

		/* Clear the sequence counter */
		dw_SetMask = 0x0UL;
		dw_ResetMask = 0xFFFFFFFFUL;
		INPDW_MEM(GET_MEM_BAR3(pdev), 48, &dw_WriteValueDWord);
		dw_WriteValueDWord = (dw_WriteValueDWord & (0xFFFFFFFFUL - dw_ResetMask)) | dw_SetMask;
		OUTPDW_MEM(GET_MEM_BAR3(pdev), 48, dw_WriteValueDWord);

		/*i_ErrorCode = SET_RESET_VALUE
		              (b_CallLevel,
		               ps_HardwareInformation,
		               48,
		               0x0UL,
		               0xFFFFFFFFUL,
		               0,
		               NULL,
		               0,
		               0,
		               NULL);*/

		}
	else
		{
		/* Set the single mode and disable the DMA */
		dw_SetMask = 0x0UL;
		dw_ResetMask = 0x230000UL;
		INPDW_MEM(GET_MEM_BAR3(pdev), 8, &dw_WriteValueDWord);
		dw_WriteValueDWord = (dw_WriteValueDWord & (0xFFFFFFFFUL - dw_ResetMask)) | dw_SetMask;
		OUTPDW_MEM(GET_MEM_BAR3(pdev), 8, dw_WriteValueDWord);

		/*i_ErrorCode = SET_RESET_VALUE
		              (b_CallLevel,
		               ps_HardwareInformation,
		               8,
		               0x0UL,
		               0x230000UL,
		               0,
		               NULL,
		               0,
		               0,
		               NULL);*/

		/* Set the sequence counter */
		dw_SetMask = dw_NumberOfSequence;
		dw_ResetMask = 0xFFFFFFFFUL;
		INPDW_MEM(GET_MEM_BAR3(pdev), 48, &dw_WriteValueDWord);
		dw_WriteValueDWord = (dw_WriteValueDWord & (0xFFFFFFFFUL - dw_ResetMask)) | dw_SetMask;
		OUTPDW_MEM(GET_MEM_BAR3(pdev), 48, dw_WriteValueDWord);

		/*i_ErrorCode = SET_RESET_VALUE
		              (b_CallLevel,
		               ps_HardwareInformation,
		               48,
		               dw_NumberOfSequence,
		               0xFFFFFFFFUL,
		               0,
		               NULL,
		               0,
		               0,
		               NULL);*/

		}

	/* Clear the DMA sequence counter */
	dw_SetMask = 0x0UL;
	dw_ResetMask = 0xFFFFFFFFUL;
	INPDW_MEM(GET_MEM_BAR3(pdev), 56, &dw_WriteValueDWord);
	dw_WriteValueDWord = (dw_WriteValueDWord & (0xFFFFFFFFUL - dw_ResetMask)) | dw_SetMask;
	OUTPDW_MEM(GET_MEM_BAR3(pdev), 56, dw_WriteValueDWord);

	/*i_ErrorCode = SET_RESET_VALUE
	               (b_CallLevel,
	                ps_HardwareInformation,
	                56,
	                0,
	                0xFFFFFFFFUL,
	                0,
	                NULL,
	                0,
	                0,
	                NULL);*/

	/* Clear the sequence index */
	dw_SetMask = 0x10000UL;
	dw_ResetMask = 0x0UL;
	INPDW_MEM(GET_MEM_BAR3(pdev), 12, &dw_WriteValueDWord);
	dw_WriteValueDWord = (dw_WriteValueDWord & (0xFFFFFFFFUL - dw_ResetMask)) | dw_SetMask;
	OUTPDW_MEM(GET_MEM_BAR3(pdev), 12, dw_WriteValueDWord);

	/*i_ErrorCode = SET_RESET_VALUE
	               (b_CallLevel,
	                ps_HardwareInformation,
	                12,
	                0x10000UL,
	                0x0UL,
	                0,
	                NULL,
	                0,
	                0,
	                NULL);*/

	/* Complete the sequence structure */
	XPCI3009_PRIVDATA(pdev)->b_NumberOfAutoRefreshChannels = b_NumberOfChannels;
	XPCI3009_PRIVDATA(pdev)->b_AnalogInputAutoRefreshInitialised = ADDIDATA_ENABLE;

	return 0;
}


/**
 *
 * Start an analog input auto refresh conversion
 *
 * @param[in]  pdev : PCI Device struct              \n
 * @param[in]   b_ConvertTimeUnit : Convert time unit for the   \n
 *                                              analog input:                  \n
 *                                              - ADDIDATA_NANO_SECOND  :nanos \n
 *                                              - ADDIDATA_MICRO_SECOND :micros\n
 *                                              - ADDIDATA_MILLI_SECOND :ms    \n
 * @param[in]   w_ConvertTime : Convert time value for the      \n
 *                                              analog input conversion        \n
 *
 *
 * @retval 0 No error
 * @retval 1 The handle parameter of the board is wrong
 * @retval 2 Auto refresh not initialised
 * @retval 3 Wrong convert time unit
 * @retval 4 Wrong convert time
 * @retval 5 A conversion is already started
 */
int i_xpci3009_StartAnalogInputAutoRefresh (struct pci_dev * pdev,
												uint8_t   b_ConvertTimeUnit,
                                                uint16_t   w_ConvertTime)
{
	uint32_t  dw_ResetMask = 0;
	uint32_t  dw_SetMask = 0;
	uint32_t dw_WriteValueDWord = 0;
	uint32_t  dw_ReadValueDWord = 0;
	uint32_t  dw_ReadMaskDWord = 0;

	if (!pdev) return 1;

	{
		XPCI3009_DEBUG_FCN();
		XPCI3009_DEBUG("b_ConvertTimeUnit: %u\n",b_ConvertTimeUnit);
		XPCI3009_DEBUG("w_ConvertTime: %u\n",w_ConvertTime);
	}

	if (XPCI3009_PRIVDATA(pdev)->b_AnalogInputAutoRefreshInitialised != ADDIDATA_ENABLE)
		return 2;

	if( !converttimeunit_is_valid(b_ConvertTimeUnit) )
		/* constraint: b_ConvertTimeUnit : ( ADDIDATA_NANO_SECOND,ADDIDATA_MICRO_SECOND,ADDIDATA_MILLI_SECOND ) */
		return 3;

	/* Tests if the convert time unit is supported */
	if ( ((b_ConvertTimeUnit == ADDIDATA_NANO_SECOND) && (0 == XPCI3009_PRIVDATA(pdev)->dw_MaxConvertTimeForNanoSecond) && (0 == XPCI3009_PRIVDATA(pdev)->dw_MinConvertTimeForNanoSecond))
		|| ((b_ConvertTimeUnit == ADDIDATA_MICRO_SECOND) && (0 == XPCI3009_PRIVDATA(pdev)->dw_MaxConvertTimeForMicroSecond) && (0 == XPCI3009_PRIVDATA(pdev)->dw_MinConvertTimeForMicroSecond))
		|| ((b_ConvertTimeUnit == ADDIDATA_MILLI_SECOND) && (0 == XPCI3009_PRIVDATA(pdev)->dw_MaxConvertTimeForMilliSecond) && (0 == XPCI3009_PRIVDATA(pdev)->dw_MinConvertTimeForMilliSecond)) )
		return 3;

	/* Tests the convert time */
	if ( ((b_ConvertTimeUnit == ADDIDATA_NANO_SECOND) && (w_ConvertTime > XPCI3009_PRIVDATA(pdev)->dw_MaxConvertTimeForNanoSecond) && (w_ConvertTime < XPCI3009_PRIVDATA(pdev)->dw_MinConvertTimeForNanoSecond))
		|| ((b_ConvertTimeUnit == ADDIDATA_MICRO_SECOND) && (w_ConvertTime > XPCI3009_PRIVDATA(pdev)->dw_MaxConvertTimeForMicroSecond) && (w_ConvertTime < XPCI3009_PRIVDATA(pdev)->dw_MinConvertTimeForMicroSecond))
		|| ((b_ConvertTimeUnit == ADDIDATA_MILLI_SECOND) && (w_ConvertTime > XPCI3009_PRIVDATA(pdev)->dw_MaxConvertTimeForMilliSecond) && (w_ConvertTime < XPCI3009_PRIVDATA(pdev)->dw_MinConvertTimeForMilliSecond)) )
		return 4;

	dw_ReadMaskDWord = 0x80000;
	INPDW_MEM(GET_MEM_BAR3(pdev), 8, &dw_ReadValueDWord);
	dw_ReadValueDWord = dw_ReadValueDWord & dw_ReadMaskDWord;

	if (dw_ReadValueDWord == 0x00080000)
		/* Conversion started */
		return 5;

	/* Clear all DMA interrupt source */
	dw_SetMask = 0x35UL;
	dw_ResetMask = 0xFFFFFFFFUL;
	INPDW_MEM(GET_MEM_BAR3(pdev), 16, &dw_WriteValueDWord);
	dw_WriteValueDWord = (dw_WriteValueDWord & (0xFFFFFFFFUL - dw_ResetMask)) | dw_SetMask;
	OUTPDW_MEM(GET_MEM_BAR3(pdev), 16, dw_WriteValueDWord);

	/*i_ErrorCode = SET_RESET_VALUE
	              (b_CallLevel,
	               ps_HardwareInformation,
	               16,
	               0x35UL,
	               0xFFFFFFFFUL,
	               0,
	               NULL,
	               0,
	               0,
	               NULL);*/

	/* Set the conversion time base */
	dw_SetMask = (uint32_t)w_ConvertTime;
	dw_ResetMask = 0xFFFFFFFFUL;
	INPDW_MEM(GET_MEM_BAR3(pdev), 32, &dw_WriteValueDWord);
	dw_WriteValueDWord = (dw_WriteValueDWord & (0xFFFFFFFFUL - dw_ResetMask)) | dw_SetMask;
	OUTPDW_MEM(GET_MEM_BAR3(pdev), 32, dw_WriteValueDWord);

	/*i_ErrorCode = SET_RESET_VALUE
	              (b_CallLevel,
	               ps_HardwareInformation,
	               32,
	               dw_ConversionReloadValue,
	               0xFFFFFFFFUL,
	               0,
	               NULL,
	               0,
	               0,
	               NULL);*/

	/* Set the conversion time unit */
	dw_SetMask = (uint32_t)b_ConvertTimeUnit;
	dw_ResetMask = 0xFFFFFFFFUL;
	INPDW_MEM(GET_MEM_BAR3(pdev), 36, &dw_WriteValueDWord);
	dw_WriteValueDWord = (dw_WriteValueDWord & (0xFFFFFFFFUL - dw_ResetMask)) | dw_SetMask;
	OUTPDW_MEM(GET_MEM_BAR3(pdev), 36, dw_WriteValueDWord);

	/*i_ErrorCode = SET_RESET_VALUE
	              (b_CallLevel,
	               ps_HardwareInformation,
	               36,
	               (uint32_t) b_ConversionTimeBase,
	               0xFFFFFFFFUL,
	               0,
	               NULL,
	               0,
	               0,
	               NULL);*/

	/* Clear the interrupt   */
	/* Clear the DMA         */
	/* Start the acquisition */
	dw_SetMask = 0x80000UL;
	dw_ResetMask = 0x300000UL;
	INPDW_MEM(GET_MEM_BAR3(pdev), 8, &dw_WriteValueDWord);
	dw_WriteValueDWord = (dw_WriteValueDWord & (0xFFFFFFFFUL - dw_ResetMask)) | dw_SetMask;
	OUTPDW_MEM(GET_MEM_BAR3(pdev), 8, dw_WriteValueDWord);

	/*i_ErrorCode = SET_RESET_VALUE
	              (b_CallLevel,
	               ps_HardwareInformation,
	               8,
	               0x80000UL,
	               0x300000UL,
	               0,
	               NULL,
	               0,
	               0,
	               NULL);*/


      return 0;
}


/**
 *
 * Stop an analog input auto refresh conversion
 *
 * @param[in]  pdev : PCI Device struct              \n
 *
 *
 * @retval 0 No error
 * @retval 1 The handle parameter of the board is wrong
 * @retval 2 Auto refresh not initialised
 */
int i_xpci3009_StopAnalogInputAutoRefresh (struct pci_dev * pdev)
{

	uint32_t  dw_ResetMask = 0;
	uint32_t  dw_SetMask = 0;
	uint32_t dw_WriteValueDWord = 0;

	if (!pdev) return 1;

	{
		XPCI3009_DEBUG_FCN();
	}

	if (XPCI3009_PRIVDATA(pdev)->b_AnalogInputAutoRefreshInitialised != ADDIDATA_ENABLE)
		return 2;

	/* Disable the interrupt */
	/* Disable the DMA       */
	/* Stop the acquisition  */
	dw_SetMask = 0x0800000UL;
	dw_ResetMask = 0x380000UL;
	INPDW_MEM(GET_MEM_BAR3(pdev), 8, &dw_WriteValueDWord);
	dw_WriteValueDWord = (dw_WriteValueDWord & (0xFFFFFFFFUL - dw_ResetMask)) | dw_SetMask;
	OUTPDW_MEM(GET_MEM_BAR3(pdev), 8, dw_WriteValueDWord);

	dw_SetMask = 0x0UL;
	dw_ResetMask = 0x300000UL;
	INPDW_MEM(GET_MEM_BAR3(pdev), 8, &dw_WriteValueDWord);
	dw_WriteValueDWord = (dw_WriteValueDWord & (0xFFFFFFFFUL - dw_ResetMask)) | dw_SetMask;
	OUTPDW_MEM(GET_MEM_BAR3(pdev), 8, dw_WriteValueDWord);

	dw_SetMask = 0x0UL;
	dw_ResetMask = 0x300000UL;
	INPDW_MEM(GET_MEM_BAR3(pdev), 8, &dw_WriteValueDWord);
	dw_WriteValueDWord = (dw_WriteValueDWord & (0xFFFFFFFFUL - dw_ResetMask)) | dw_SetMask;
	OUTPDW_MEM(GET_MEM_BAR3(pdev), 8, dw_WriteValueDWord);

	/*-i_ErrorCode = SET_RESET_VALUE
	              (b_CallLevel,
	               ps_HardwareInformation,
	               8,
	               0x0UL,
	               0x380000UL,
	               0,
	               NULL,
	               0,
	               0,
	               NULL);*/

	/* Clear all DMA interrupt source */
	dw_SetMask = 0x35UL;
	dw_ResetMask = 0xFFFFFFFFUL;
	INPDW_MEM(GET_MEM_BAR3(pdev), 16, &dw_WriteValueDWord);
	dw_WriteValueDWord = (dw_WriteValueDWord & (0xFFFFFFFFUL - dw_ResetMask)) | dw_SetMask;
	OUTPDW_MEM(GET_MEM_BAR3(pdev), 16, dw_WriteValueDWord);

	/*i_ErrorCode = SET_RESET_VALUE
	              (b_CallLevel,
	               ps_HardwareInformation,
	               16,
	               0x35UL,
	               0xFFFFFFFFUL,
	               0,
	               NULL,
	               0,
	               0,
	               NULL);*/

      return 0;
}


/**
 *
 * Read the auto refresh values
 *
 * @param[in] pdev PCI Device struct
 * @param[in] pdw_ReadValue a buffer of 32 bits unsigned integer of size ADDIDATA_MAX_AI, containing the acquired data.
 * @param[in] pdw_CycleIndex current cycle index
 *
 *
 * @retval 0 No error
 * @retval 1 The handle parameter of the board is wrong
 * @retval 2 Auto refresh not initialised
 */
int i_xpci3009_ReadAnalogInputAutoRefreshValueAndCounter (struct pci_dev * pdev,
			                                                uint32_t pdw_ReadValue[ADDIDATA_MAX_AI],
			                                                uint32_t* pdw_CycleIndex)
{
	unsigned char i = 0;

	if (!pdev) return 1;

	{
		XPCI3009_DEBUG_FCN();
	}

	if (XPCI3009_PRIVDATA(pdev)->b_AnalogInputAutoRefreshInitialised != ADDIDATA_ENABLE)
		return 2;

	/* Read the number of sequence */
	INPDW_MEM(GET_MEM_BAR3(pdev), 48, pdw_CycleIndex);

	/* Read the digital value of the input */
	/* Value is B0?B31 dw_ReadValueDWord ? dw_ReadValueDWord & 0xFFFFFFFF */

	/* whatever is the actual b_NumberOfAnalogInput, the memory read by INPDW_MEM is correct */
	for (i = 0 ; i < ADDIDATA_MAX_AI ; i ++)
	{
		INPDW_MEM(GET_MEM_BAR3(pdev), (128 + (i * 4)), &pdw_ReadValue[i]);
	}
	return 0;
}

/** Read the specified numbers of auto refresh values.
 *
 * This function doesn't check parameters validity.
 * It is written to have a short execution time.
 * Use it only if you are sure of what you are doing.
 *
 * @param[in] pdev				: PCI Device struct.
 * @param[in] dw_NbrOfChannels	: Specified the channel number up to which is to be read (eg.: 0 to 15 dw_NbrOfChannels = 15).
 * @param[out] pdw_ReadValue	: A buffer of 32 bits unsigned integer of size ADDIDATA_MAX_AI, containing the acquired data.
 * @param[out] pdw_CycleIndex	: Current cycle index.
 *
 * @retval 0 Always 0.
 */
int i_xpci3009_FastReadAnalogInputAutoRefreshValueAndCounter (struct pci_dev * pdev,
															uint32_t dw_NbrOfChannels,
			                                                uint32_t *pdw_ReadValue,
			                                                uint32_t *pdw_CycleIndex)
{
	int i = 0;

	/* Read the number of sequence */
	INPDW_MEM(GET_MEM_BAR3(pdev), 48, pdw_CycleIndex);

	for (i=0; i<dw_NbrOfChannels; i++)
		INPDW_MEM(GET_MEM_BAR3(pdev), (128 + (i * 4)), &pdw_ReadValue[i]);

	return 0;
}

/**
 *
 * Release an analog input auto refresh conversion
 *
 * @param[in]  pdev : PCI Device struct              \n
 *
 *
 * @retval 0 : No error
 * @retval 1 : The handle parameter of the board is wrong
 * @retval 2 : Auto refresh not initialised
 * @retval 3 : A conversion is already started
 */
int i_xpci3009_ReleaseAnalogInputAutoRefresh (struct pci_dev * pdev)
{
   uint32_t dw_ReadValueDWord = 0;
   uint32_t dw_ReadMaskDWord = 0;

	if (!pdev) return 1;

	{
		XPCI3009_DEBUG_FCN();
	}

	if (XPCI3009_PRIVDATA(pdev)->b_AnalogInputAutoRefreshInitialised != ADDIDATA_ENABLE)
		return 2;

	dw_ReadMaskDWord = 0x80000;
	INPDW_MEM(GET_MEM_BAR3(pdev), 8, &dw_ReadValueDWord);
	dw_ReadValueDWord = dw_ReadValueDWord & dw_ReadMaskDWord;

	if (dw_ReadValueDWord == 0x00080000)
		/* Conversion started */
		return 3;

	/* Complete the sequence structure */
	XPCI3009_PRIVDATA(pdev)->b_NumberOfAutoRefreshChannels = 0;
	XPCI3009_PRIVDATA(pdev)->b_AnalogInputAutoRefreshInitialised = ADDIDATA_DISABLE;

	return 0;
}

/****************/
/**  SEQUENCE  **/
/****************/

/**
 *
 * Initialise an analog input sequence conversion
 *
 * @param[in]  pdev : PCI Device struct              \n
 * @param[in]    b_NumberOfChannels : Number of channel to      \n
 *                                              initialise (<=ADDIDATA_MAX_AI) \n
 * @param[in]  pb_ChannelList : List of the channels           \n
 *                                                  to initialise              \n
 * @param[in]  pb_Gain     : Gain factor array of the channels \n
 *                                          - ADDIDATA_1_GAIN : Gain =  1      \n
 *                                          - ADDIDATA_2_GAIN : Gain =  2      \n
 *                                          - ADDIDATA_5_GAIN : Gain =  5      \n
 *                                          - ADDIDATA_10_GAIN: Gain = 10      \n
 * @param[in]  pb_Polarity   : Polarity array of the channels: \n
 *                                            - ADDIDATA_UNIPOLAR:  0V - +10V  \n
 *                                            - ADDIDATA_BIPOLAR: -10V - +10V  \n
 * @param[in]   b_SingleDiff : 0 : Single ended HW config      \n
 *                                             1 : Differential HW config      \n
 * @param[in]  dw_NumberOfSequence :   if DMA not used:        \n
 *                                               - 0: continuous mode          \n
 *                                               - <>0: single mode            \n
 *                                                     if DMA used:            \n
 *                                               - define the number of DMA    \n
 *                                                 transfer                    \n
 * @param[in]    b_UseDMA : ADDIDATA_ENABLE  : Use DMA          \n
 *                                         ADDIDATA_DISABLE : Don't use DMA    \n
 * @param[in]  dw_NumberOfSequenceForEachInterrupt : number    \n
 *                                               of sequence to do before      \n
 *                                               generating an interrupt       \n
 * @param[in]   b_DelayMode     : Delay mode for the            \n
 *                                               analog input:                 \n
 *                                              - ADDIDATA_DELAY_MODE_1: Mode 1\n
 *                                              - ADDIDATA_DELAY_MODE_2: Mode 2\n
 * @param[in]   b_DelayTimeUnit : Delay time unit for the       \n
 *                                              analog input:                  \n
 *                                              - ADDIDATA_MICRO_SECOND :micros\n
 *                                              - ADDIDATA_MILLI_SECOND :ms    \n
 *                                              - ADDIDATA_SECOND :s           \n
 * @param[in]   w_DelayTime : Delay time value for the          \n
 *                                              analog input conversion        \n
 *
 *
 * @retval 0  No error
 * @retval 1  The handle parameter of the board is wrong
 * @retval 2  Wrong number of channels
 * @retval 3  Wrong channel number
 * @retval 4  Wrong gain factor
 * @retval 5  Wrong polarity
 * @retval 6  Wrong single diff parameter (not 0 or 1)
 * @retval 7  Wrong use DMA parameter
 * @retval 8  Wrong number of sequence for each interrupt parameter
 * @retval 9  Wrong delay mode parameter
 * @retval 10 Wrong delay time unit parameter
 * @retval 11 Wrong delay time parameter
 * @retval 12 A conversion is already started
 * @retval 13 DMA mode not supported
 * @retval 14 No memory space available
 * @retval 15 Error by initialising the DMA
 * @retval 16 requested single diff parameter not supported by the board
 *
 * @note
 *
 * <b>More on b_NumberOfChannels</b>\n \n
 *
 * b_NumberOfChannels describes the sequence to follow during acquisition. It must contain indexes of valid channels.
 * Which index is valid depends on the connection mode of the board (single or differential).
 *
 * The order of the index in this array is the order of the values returned after the acquisition.
 *
 * <b>Constraints on parameter NumberOfChannel</b>\n\n
 * let b_NumberOfAnalogInput be the number of analog inputs (4,8,16) of the board, then NumberOfChannel is correct if
 * - 0 < NumberOfChannel <= ADDIDATA_MAX_AI
 * - if the board is in differential mode, NumberOfChannel < (b_NumberOfAnalogInput/2)
 * - if the board is in single mode, NumberOfChannel < b_NumberOfAnalogInput
 *
 *
 * <b>Constraints on parameter pb_ChannelList</b>\n\n
 * let b_NumberOfAnalogInput be the number of analog inputs (4,8,16) of the board, then NumberOfChannel is correct if:
 * - if the board is in differencial mode, for each value in pb_ChannelList, value < (b_NumberOfAnalogInput/2)
 * - if the board is in single mode, for each value in pb_ChannelList, value < b_NumberOfAnalogInput
 *
 */
int i_xpci3009_InitAnalogInputSequence (struct pci_dev * pdev,
                                            uint8_t    b_NumberOfChannels,
                                            uint8_t*  pb_ChannelList,
			                                uint8_t*  pb_Gain,
			                                uint8_t*  pb_Polarity,
			                                uint8_t    b_SingleDiff,
                                            uint32_t  dw_NumberOfSequence,
											uint8_t    b_UseDMA,
											uint32_t  dw_NumberOfSequenceForEachInterrupt,
                                            uint8_t    b_DelayMode,
                                            uint8_t    b_DelayTimeUnit,
                                            uint16_t    w_DelayTime)
{
	uint32_t  dw_ResetMask = 0;
	uint32_t  dw_SetMask = 0;
	uint32_t dw_WriteValueDWord = 0;
	uint32_t dw_ReadValueDWord = 0;
	uint32_t dw_ReadMaskDWord = 0;
	uint8_t   b_AcquisitionMode = 0;
	int	   i_ReturnValue = 0;

	if (!pdev) return 1;

	{
		XPCI3009_DEBUG_FCN();
	}

	/* Tests the single diff flag */
	if( !singlediff_is_valid(b_SingleDiff) )
		return 6;

	if( !singlediff_supported(pdev, b_SingleDiff) )
		return 16;

	/* Tests the number of channels */
	if( test_NumberOfChannel(pdev, b_NumberOfChannels, b_SingleDiff) )
		return 3;

	if( test_pb_ChannelList(pdev, b_SingleDiff, b_NumberOfChannels, pb_ChannelList) )
		return 2;

	{
		uint8_t   b_ChannelCounter = 0;
		for (b_ChannelCounter = 0 ; b_ChannelCounter < b_NumberOfChannels ; b_ChannelCounter++)
		{
			/* Tests the gain */
			if ( (pb_Gain[b_ChannelCounter] != ADDIDATA_1_GAIN) && (pb_Gain[b_ChannelCounter] != ADDIDATA_2_GAIN) && (pb_Gain[b_ChannelCounter] != ADDIDATA_5_GAIN) && (pb_Gain[b_ChannelCounter] != ADDIDATA_10_GAIN) )
				return 4;

			/* Tests the polarity */
			if ( (pb_Polarity[b_ChannelCounter] != ADDIDATA_UNIPOLAR) && (pb_Polarity[b_ChannelCounter] != ADDIDATA_BIPOLAR) )
				return 5;
		}
	}

	/* Tests the use DMA flag */
	if( !is_boolean(b_UseDMA) )
		return 7;

	/* Tests the number of sequence for each interrupt */
	if ((b_UseDMA == ADDIDATA_ENABLE) && (dw_NumberOfSequenceForEachInterrupt == 0))
		return 8;

	/* Tests the delay mode flag */
	if ((b_DelayMode != ADDIDATA_DISABLE) && (b_DelayMode != ADDIDATA_DELAY_MODE_1) && (b_DelayMode != ADDIDATA_DELAY_MODE_2))
		return 9;

	if (b_DelayMode != ADDIDATA_DISABLE)
		{
		/* Tests the delay time unit */
		if ( (b_DelayTimeUnit != ADDIDATA_MICRO_SECOND) && (b_DelayTimeUnit != ADDIDATA_MILLI_SECOND) && (b_DelayTimeUnit != ADDIDATA_SECOND) )
			return 10;

		/* Tests if the delay time unit is supported */
		if ( ((b_DelayTimeUnit == ADDIDATA_MICRO_SECOND) && (0 == XPCI3009_PRIVDATA(pdev)->dw_MaxDelayTimeForMicroSecond) && (0 == XPCI3009_PRIVDATA(pdev)->dw_MinDelayTimeForMicroSecond))
			|| ((b_DelayTimeUnit == ADDIDATA_MILLI_SECOND) && (0 == XPCI3009_PRIVDATA(pdev)->dw_MaxDelayTimeForMilliSecond) && (0 == XPCI3009_PRIVDATA(pdev)->dw_MinDelayTimeForMilliSecond))
			|| ((b_DelayTimeUnit == ADDIDATA_SECOND) && (0 == XPCI3009_PRIVDATA(pdev)->dw_MaxDelayTimeForSecond) && (0 == XPCI3009_PRIVDATA(pdev)->dw_MinDelayTimeForSecond)) )
			return 10;

		/* Tests the delay time */
		if ( ((b_DelayTimeUnit == ADDIDATA_MICRO_SECOND) && (w_DelayTime > XPCI3009_PRIVDATA(pdev)->dw_MaxDelayTimeForMicroSecond) && (w_DelayTime < XPCI3009_PRIVDATA(pdev)->dw_MinDelayTimeForMicroSecond))
			|| ((b_DelayTimeUnit == ADDIDATA_MILLI_SECOND) && (w_DelayTime > XPCI3009_PRIVDATA(pdev)->dw_MaxDelayTimeForMilliSecond) && (w_DelayTime < XPCI3009_PRIVDATA(pdev)->dw_MinDelayTimeForMilliSecond))
			|| ((b_DelayTimeUnit == ADDIDATA_SECOND) && (w_DelayTime > XPCI3009_PRIVDATA(pdev)->dw_MaxDelayTimeForSecond) && (w_DelayTime < XPCI3009_PRIVDATA(pdev)->dw_MinDelayTimeForSecond)) )
			return 11;
		}

	dw_ReadMaskDWord = 0x80000;
	INPDW_MEM(GET_MEM_BAR3(pdev), 8, &dw_ReadValueDWord);
	dw_ReadValueDWord = dw_ReadValueDWord & dw_ReadMaskDWord;

	if (dw_ReadValueDWord == 0x00080000)
		/* Conversion started */
		return 12;

	if (b_UseDMA == ADDIDATA_ENABLE)
		{
		if (XPCI3009_PRIVDATA(pdev)->b_AnalogInputDMASupported != ADDIDATA_ENABLE)
			return 13;

		//		i_ReturnValue = i_xpci3009_AllocateSharedMemory(pdev, b_NumberOfChannels * dw_NumberOfSequenceForEachInterrupt * sizeof(uint32_t));
		//
		//		/* No memory space available */
		//		if (i_ReturnValue != 0)
		//			{
		//			i_ReturnValue = i_xpci3009_ReleaseSharedMemory (pdev, b_NumberOfChannels * dw_NumberOfSequenceForEachInterrupt * sizeof(uint32_t));
		//			return 14;
		//			}

		if (dw_NumberOfSequence == 0)
			b_AcquisitionMode = 1; // continuous mode
		else
			b_AcquisitionMode = 0; // single mode

		i_ReturnValue = i_xpci3009_InitAnalogInputDMA(pdev,
                                     b_NumberOfChannels,
									 dw_NumberOfSequence,
									 dw_NumberOfSequenceForEachInterrupt,
									 b_AcquisitionMode);

		/* Error by initialising the DMA */
		if (i_ReturnValue != 0)
			{
			// i_ReturnValue = i_xpci3009_ReleaseSharedMemory (pdev, b_NumberOfChannels * dw_NumberOfSequenceForEachInterrupt * sizeof(uint32_t));
			i_ReturnValue = i_xpci3009_ReleaseAnalogInputDMA(pdev);
			return 15;
			}

		}

	initialize_delay(pdev, b_DelayMode, b_DelayTimeUnit, w_DelayTime);
	initialize_channels(pdev, b_NumberOfChannels, pb_ChannelList, pb_Gain, pb_Polarity,b_SingleDiff);

	/* Set the first and last sequence index */

	/* Set the start sequence index */
	INPDW_MEM(GET_MEM_BAR3(pdev), 8, &dw_WriteValueDWord);
	dw_WriteValueDWord &= ~ANALOG_SEQUENCE_START_BITMASK;

	/* The first channel */
//	dw_WriteValueDWord |= b_FirstChannel;
	dw_WriteValueDWord |= 0;
	OUTPDW_MEM(GET_MEM_BAR3(pdev), 8, dw_WriteValueDWord);

	/* Set the stop sequence index */
	INPDW_MEM(GET_MEM_BAR3(pdev), 8, &dw_WriteValueDWord);
	dw_WriteValueDWord &= ~ANALOG_SEQUENCE_STOP_BITMASK;

	/* Set the last channel */
//	dw_WriteValueDWord |= (b_LastChannel << 8);
	dw_WriteValueDWord |= ((b_NumberOfChannels - 1) << 8);
	OUTPDW_MEM(GET_MEM_BAR3(pdev), 8, dw_WriteValueDWord);

//	/*i_ErrorCode = SET_RESET_VALUE
//	               (b_CallLevel,
//	                ps_HardwareInformation,
//	                8,
//	                (uint32_t) (((uint32_t) b_NumberOfChannels - 1) << 8),
//	                0xFFFFUL,
//	                0,
//	                NULL,
//	                0,
//	                0,
//	                NULL);*/


	/* Test if DMA used */
	if (b_UseDMA == ADDIDATA_ENABLE)
		{
	/* Enable the DMA mode and disable the sequence mode */
		dw_SetMask = 0x010000UL;
		//dw_SetMask = 0x210000UL;
		dw_ResetMask = 0x020000UL;
		INPDW_MEM(GET_MEM_BAR3(pdev), 8, &dw_WriteValueDWord);
		dw_WriteValueDWord = (dw_WriteValueDWord & (0xFFFFFFFFUL - dw_ResetMask)) | dw_SetMask;
		OUTPDW_MEM(GET_MEM_BAR3(pdev), 8, dw_WriteValueDWord);

		/* Clear the sequence counter */
		dw_SetMask = 0x0UL;
		dw_ResetMask = 0xFFFFFFFFUL;
		INPDW_MEM(GET_MEM_BAR3(pdev), 48, &dw_WriteValueDWord);
		dw_WriteValueDWord = (dw_WriteValueDWord & (0xFFFFFFFFUL - dw_ResetMask)) | dw_SetMask;
		OUTPDW_MEM(GET_MEM_BAR3(pdev), 48, dw_WriteValueDWord);
		}
	else
		{
		/* Test if continuous mode */
		if (dw_NumberOfSequence == 0)
			{
			/* Set the continous mode and disable the DMA */
			dw_SetMask = 0x010000UL;
			dw_ResetMask = 0x230000UL;
			INPDW_MEM(GET_MEM_BAR3(pdev), 8, &dw_WriteValueDWord);
			dw_WriteValueDWord = (dw_WriteValueDWord & (0xFFFFFFFFUL - dw_ResetMask)) | dw_SetMask;
			OUTPDW_MEM(GET_MEM_BAR3(pdev), 8, dw_WriteValueDWord);

		    /*i_ErrorCode = SET_RESET_VALUE
		                  (b_CallLevel,
		                   ps_HardwareInformation,
		                   8,
		                   0x10000UL,
		                   0x230000UL,
		                   0,
		                   NULL,
		                   0,
		                   0,
		                   NULL);*/

			/* Clear the sequence counter */
			dw_SetMask = 0x0UL;
			dw_ResetMask = 0xFFFFFFFFUL;
			INPDW_MEM(GET_MEM_BAR3(pdev), 48, &dw_WriteValueDWord);
			dw_WriteValueDWord = (dw_WriteValueDWord & (0xFFFFFFFFUL - dw_ResetMask)) | dw_SetMask;
			OUTPDW_MEM(GET_MEM_BAR3(pdev), 48, dw_WriteValueDWord);

			/*i_ErrorCode = SET_RESET_VALUE
			              (b_CallLevel,
			               ps_HardwareInformation,
			               48,
			               0x0UL,
			               0xFFFFFFFFUL,
			               0,
			               NULL,
			               0,
			               0,
			               NULL);*/

			}
		else
			{
			/* Set the single mode and disable the DMA */
			dw_SetMask = 0x0UL;
			dw_ResetMask = 0x230000UL;
			INPDW_MEM(GET_MEM_BAR3(pdev), 8, &dw_WriteValueDWord);
			dw_WriteValueDWord = (dw_WriteValueDWord & (0xFFFFFFFFUL - dw_ResetMask)) | dw_SetMask;
			OUTPDW_MEM(GET_MEM_BAR3(pdev), 8, dw_WriteValueDWord);

			/*i_ErrorCode = SET_RESET_VALUE
			              (b_CallLevel,
			               ps_HardwareInformation,
			               8,
			               0x0UL,
			               0x230000UL,
			               0,
			               NULL,
			               0,
			               0,
			               NULL);*/

			/* Set the sequence counter */
			dw_SetMask = dw_NumberOfSequence;
			dw_ResetMask = 0xFFFFFFFFUL;
			INPDW_MEM(GET_MEM_BAR3(pdev), 48, &dw_WriteValueDWord);
			dw_WriteValueDWord = (dw_WriteValueDWord & (0xFFFFFFFFUL - dw_ResetMask)) | dw_SetMask;
			OUTPDW_MEM(GET_MEM_BAR3(pdev), 48, dw_WriteValueDWord);

			/*i_ErrorCode = SET_RESET_VALUE
				              (b_CallLevel,
				               ps_HardwareInformation,
				               48,
				               dw_NumberOfSequence,
				               0xFFFFFFFFUL,
				               0,
				               NULL,
				               0,
				               0,
				               NULL);*/
			}

		}

	/* Set or Clear the DMA sequence counter */
	if (b_UseDMA == ADDIDATA_ENABLE)
        dw_SetMask = dw_NumberOfSequence * b_NumberOfChannels;
	else
	   	dw_SetMask = 0x0UL;

	dw_ResetMask = 0xFFFFFFFFUL;
	INPDW_MEM(GET_MEM_BAR3(pdev), 56, &dw_WriteValueDWord);
	dw_WriteValueDWord = (dw_WriteValueDWord & (0xFFFFFFFFUL - dw_ResetMask)) | dw_SetMask;
	OUTPDW_MEM(GET_MEM_BAR3(pdev), 56, dw_WriteValueDWord);

	/*i_ErrorCode = SET_RESET_VALUE
	               (b_CallLevel,
	                ps_HardwareInformation,
	                56,
	                0,
	                0xFFFFFFFFUL,
	                0,
	                NULL,
	                0,
	                0,
	                NULL);*/

	/* Clear the sequence index */
	dw_SetMask = 0x10000UL;
	dw_ResetMask = 0x0UL;
	INPDW_MEM(GET_MEM_BAR3(pdev), 12, &dw_WriteValueDWord);
	dw_WriteValueDWord = (dw_WriteValueDWord & (0xFFFFFFFFUL - dw_ResetMask)) | dw_SetMask;
	OUTPDW_MEM(GET_MEM_BAR3(pdev), 12, dw_WriteValueDWord);

	/*i_ErrorCode = SET_RESET_VALUE
	               (b_CallLevel,
	                ps_HardwareInformation,
	                12,
	                0x10000UL,
	                0x0UL,
	                0,
	                NULL,
	                0,
	                0,
	                NULL);*/

	/* Complete the sequence structure */
	XPCI3009_PRIVDATA(pdev)->b_NumberOfSequenceChannels = b_NumberOfChannels;
	XPCI3009_PRIVDATA(pdev)->b_AnalogInputSequenceInitialised = ADDIDATA_ENABLE;
	if (b_UseDMA == ADDIDATA_ENABLE)
		{
		XPCI3009_PRIVDATA(pdev)->b_AnalogInputDMA = ADDIDATA_ENABLE;
		XPCI3009_PRIVDATA(pdev)->dw_LastScatterGatherBuffer = 0;
		XPCI3009_PRIVDATA(pdev)->dw_NumberOfSequenceForEachInterrupt = dw_NumberOfSequenceForEachInterrupt;
		}
	else
		XPCI3009_PRIVDATA(pdev)->b_AnalogInputDMA = ADDIDATA_DISABLE;

	return 0;
}


/**
 *
 * Start an analog input sequence conversion
 *
 * @param[in]  pdev : PCI Device struct              \n
 * @param[in]   b_ConvertTimeUnit : Convert time unit for the   \n
 *                                              analog input:                  \n
 *                                              - ADDIDATA_NANO_SECOND  :nanos \n
 *                                              - ADDIDATA_MICRO_SECOND :micros\n
 *                                              - ADDIDATA_MILLI_SECOND :ms    \n
 * @param[in]   w_ConvertTime : Convert time value for the      \n
 *                                              analog input conversion        \n
 *
 *
 * @retval 0 No error
 * @retval 1 The handle parameter of the board is wrong
 * @retval 2 Sequence not initialised
 * @retval 3 Wrong convert time unit
 * @retval 4 Wrong convert time
 * @retval 5 A conversion is already started
 * @retval 6 Error by starting DMA
 */
int i_xpci3009_StartAnalogInputSequence (struct pci_dev * pdev,
												uint8_t   b_ConvertTimeUnit,
                                                uint16_t   w_ConvertTime)
{
	uint32_t  dw_ResetMask = 0;
	uint32_t  dw_SetMask = 0;
	uint32_t  dw_WriteValueDWord = 0;
	uint32_t  dw_ReadValueDWord = 0;
	uint32_t  dw_ReadMaskDWord = 0;
	int     i_ReturnValue = 0;

	if (!pdev) return 1;

	{
		XPCI3009_DEBUG_FCN();
	}

	if (XPCI3009_PRIVDATA(pdev)->b_AnalogInputSequenceInitialised != ADDIDATA_ENABLE)
		return 2;

	if( !converttimeunit_is_valid(b_ConvertTimeUnit) )
		/* constraint: b_ConvertTimeUnit : ( ADDIDATA_NANO_SECOND,ADDIDATA_MICRO_SECOND,ADDIDATA_MILLI_SECOND ) */
		return 3;

	/* Tests if the convert time unit is supported */
	if ( ((b_ConvertTimeUnit == ADDIDATA_NANO_SECOND) && (0 == XPCI3009_PRIVDATA(pdev)->dw_MaxConvertTimeForNanoSecond) && (0 == XPCI3009_PRIVDATA(pdev)->dw_MinConvertTimeForNanoSecond))
		|| ((b_ConvertTimeUnit == ADDIDATA_MICRO_SECOND) && (0 == XPCI3009_PRIVDATA(pdev)->dw_MaxConvertTimeForMicroSecond) && (0 == XPCI3009_PRIVDATA(pdev)->dw_MinConvertTimeForMicroSecond))
		|| ((b_ConvertTimeUnit == ADDIDATA_MILLI_SECOND) && (0 == XPCI3009_PRIVDATA(pdev)->dw_MaxConvertTimeForMilliSecond) && (0 == XPCI3009_PRIVDATA(pdev)->dw_MinConvertTimeForMilliSecond)) )
		return 3;

	/* Tests the convert time */
	if ( ((b_ConvertTimeUnit == ADDIDATA_NANO_SECOND) && (w_ConvertTime > XPCI3009_PRIVDATA(pdev)->dw_MaxConvertTimeForNanoSecond) && (w_ConvertTime < XPCI3009_PRIVDATA(pdev)->dw_MinConvertTimeForNanoSecond))
		|| ((b_ConvertTimeUnit == ADDIDATA_MICRO_SECOND) && (w_ConvertTime > XPCI3009_PRIVDATA(pdev)->dw_MaxConvertTimeForMicroSecond) && (w_ConvertTime < XPCI3009_PRIVDATA(pdev)->dw_MinConvertTimeForMicroSecond))
		|| ((b_ConvertTimeUnit == ADDIDATA_MILLI_SECOND) && (w_ConvertTime > XPCI3009_PRIVDATA(pdev)->dw_MaxConvertTimeForMilliSecond) && (w_ConvertTime < XPCI3009_PRIVDATA(pdev)->dw_MinConvertTimeForMilliSecond)) )
		return 4;

	dw_ReadMaskDWord = 0x80000;
	INPDW_MEM(GET_MEM_BAR3(pdev), 8, &dw_ReadValueDWord);
	dw_ReadValueDWord = dw_ReadValueDWord & dw_ReadMaskDWord;

	if (dw_ReadValueDWord == 0x00080000)
		/* Conversion started */
		return 5;

	if (XPCI3009_PRIVDATA(pdev)->b_AnalogInputDMA == ADDIDATA_ENABLE)
		{
		i_ReturnValue = i_xpci3009_StartAnalogInputDMA(pdev);

		if (i_ReturnValue != 0)
			{
			i_ReturnValue = i_xpci3009_StopAnalogInputDMA(pdev);
			return 6;
			}
		}

	/* Clear all DMA interrupt source */
	dw_SetMask = 0x35UL;
	dw_ResetMask = 0xFFFFFFFFUL;
	INPDW_MEM(GET_MEM_BAR3(pdev), 16, &dw_WriteValueDWord);
	dw_WriteValueDWord = (dw_WriteValueDWord & (0xFFFFFFFFUL - dw_ResetMask)) | dw_SetMask;
	OUTPDW_MEM(GET_MEM_BAR3(pdev), 16, dw_WriteValueDWord);

	/*i_ErrorCode = SET_RESET_VALUE
	              (b_CallLevel,
	               ps_HardwareInformation,
	               16,
	               0x35UL,
	               0xFFFFFFFFUL,
	               0,
	               NULL,
	               0,
	               0,
	               NULL);*/

	/* Set the conversion time base */
	dw_SetMask = (uint32_t)w_ConvertTime;
	dw_ResetMask = 0xFFFFFFFFUL;
	INPDW_MEM(GET_MEM_BAR3(pdev), 32, &dw_WriteValueDWord);
	dw_WriteValueDWord = (dw_WriteValueDWord & (0xFFFFFFFFUL - dw_ResetMask)) | dw_SetMask;
	OUTPDW_MEM(GET_MEM_BAR3(pdev), 32, dw_WriteValueDWord);

	/*i_ErrorCode = SET_RESET_VALUE
	              (b_CallLevel,
	               ps_HardwareInformation,
	               32,
	               dw_ConversionReloadValue,
	               0xFFFFFFFFUL,
	               0,
	               NULL,
	               0,
	               0,
	               NULL);*/

	/* Set the conversion time unit */
	dw_SetMask = (uint32_t)b_ConvertTimeUnit;
	dw_ResetMask = 0xFFFFFFFFUL;
	INPDW_MEM(GET_MEM_BAR3(pdev), 36, &dw_WriteValueDWord);
	dw_WriteValueDWord = (dw_WriteValueDWord & (0xFFFFFFFFUL - dw_ResetMask)) | dw_SetMask;
	OUTPDW_MEM(GET_MEM_BAR3(pdev), 36, dw_WriteValueDWord);

	/*i_ErrorCode = SET_RESET_VALUE
	              (b_CallLevel,
	               ps_HardwareInformation,
	               36,
	               (uint32_t) b_ConversionTimeBase,
	               0xFFFFFFFFUL,
	               0,
	               NULL,
	               0,
	               0,
	               NULL);*/

	/* Set the interrupt     */
	/* Set or Clear the DMA  */
	/* Start the acquisition */

	if (XPCI3009_PRIVDATA(pdev)->b_AnalogInputDMA == ADDIDATA_ENABLE)
		{
		dw_SetMask = 0x380000UL;
		dw_ResetMask = 0x0UL;
		}
	else
		{
		dw_SetMask = 0x180000UL;
		dw_ResetMask = 0x200000UL;
		}

	INPDW_MEM(GET_MEM_BAR3(pdev), 8, &dw_WriteValueDWord);
	dw_WriteValueDWord = (dw_WriteValueDWord & (0xFFFFFFFFUL - dw_ResetMask)) | dw_SetMask;
	OUTPDW_MEM(GET_MEM_BAR3(pdev), 8, dw_WriteValueDWord);

	/*i_ErrorCode = SET_RESET_VALUE
	              (b_CallLevel,
	               ps_HardwareInformation,
	               8,
	               0x80000UL,
	               0x300000UL,
	               0,
	               NULL,
	               0,
	               0,
	               NULL);*/

	  XPCI3009_PRIVDATA(pdev)->b_AnalogInputSequenceStarted = ADDIDATA_ENABLE;

      return 0;
}


/**
 *
 * Stop an analog input sequence conversion
 *
 * @param[in]  pdev : PCI Device struct              \n
 *
 *
 * @retval 0 No error
 * @retval 1 The handle parameter of the board is wrong
 * @retval 2 Auto refresh not initialised
 * @retval 3 Sequence not started
 * @retval 4 Error by stoping DMA
 */
int i_xpci3009_StopAnalogInputSequence (struct pci_dev * pdev)
{

	uint32_t  dw_ResetMask = 0;
	uint32_t  dw_SetMask = 0;
	uint32_t  dw_WriteValueDWord = 0;
	int     i_ReturnValue = 0;

//	printk("%s:%d\n",__FUNCTION__,__LINE__); // __FILE__,__FUNCTION__,__LINE__

	if (!pdev) return 1;

	{
		XPCI3009_DEBUG_FCN();
	}

	if (XPCI3009_PRIVDATA(pdev)->b_AnalogInputSequenceInitialised != ADDIDATA_ENABLE)
		return 2;

	if (XPCI3009_PRIVDATA(pdev)->b_AnalogInputSequenceStarted != ADDIDATA_ENABLE)
		return 3;

	/* Disable the interrupt */
	/* Disable the DMA       */
	/* Stop the acquisition  */
	dw_SetMask = 0x0800000UL;
	dw_ResetMask = 0x380000UL;
	INPDW_MEM(GET_MEM_BAR3(pdev), 8, &dw_WriteValueDWord);
	dw_WriteValueDWord = (dw_WriteValueDWord & (0xFFFFFFFFUL - dw_ResetMask)) | dw_SetMask;
	OUTPDW_MEM(GET_MEM_BAR3(pdev), 8, dw_WriteValueDWord);

	dw_SetMask = 0x0UL;
	dw_ResetMask = 0x300000UL;
	INPDW_MEM(GET_MEM_BAR3(pdev), 8, &dw_WriteValueDWord);
	dw_WriteValueDWord = (dw_WriteValueDWord & (0xFFFFFFFFUL - dw_ResetMask)) | dw_SetMask;
	OUTPDW_MEM(GET_MEM_BAR3(pdev), 8, dw_WriteValueDWord);

	dw_SetMask = 0x0UL;
	dw_ResetMask = 0x300000UL;
	INPDW_MEM(GET_MEM_BAR3(pdev), 8, &dw_WriteValueDWord);
	dw_WriteValueDWord = (dw_WriteValueDWord & (0xFFFFFFFFUL - dw_ResetMask)) | dw_SetMask;
	OUTPDW_MEM(GET_MEM_BAR3(pdev), 8, dw_WriteValueDWord);

	/*-i_ErrorCode = SET_RESET_VALUE
	              (b_CallLevel,
	               ps_HardwareInformation,
	               8,
	               0x0UL,
	               0x380000UL,
	               0,
	               NULL,
	               0,
	               0,
	               NULL);*/

	/* Clear all DMA interrupt source */
	dw_SetMask = 0x35UL;
	dw_ResetMask = 0xFFFFFFFFUL;
	INPDW_MEM(GET_MEM_BAR3(pdev), 16, &dw_WriteValueDWord);
	dw_WriteValueDWord = (dw_WriteValueDWord & (0xFFFFFFFFUL - dw_ResetMask)) | dw_SetMask;
	OUTPDW_MEM(GET_MEM_BAR3(pdev), 16, dw_WriteValueDWord);

	/*i_ErrorCode = SET_RESET_VALUE
	              (b_CallLevel,
	               ps_HardwareInformation,
	               16,
	               0x35UL,
	               0xFFFFFFFFUL,
	               0,
	               NULL,
	               0,
	               0,
	               NULL);*/

	if (XPCI3009_PRIVDATA(pdev)->b_AnalogInputDMA == ADDIDATA_ENABLE)
		{

		i_ReturnValue = i_xpci3009_StopAnalogInputDMA(pdev);

		if (i_ReturnValue != 0)
			return 4;
		}

	XPCI3009_PRIVDATA(pdev)->b_AnalogInputSequenceStarted = ADDIDATA_DISABLE;

	return 0;
}


/**
 *
 * Release an analog input sequence conversion
 *
 * @param[in]  pdev : PCI Device struct              \n
 *
 *
 * @retval 0 No error
 * @retval 1 The handle parameter of the board is wrong
 * @retval 2 Auto refresh not initialised
 * @retval 3 A conversion is already started
 * @retval 4 Error by releasing DMA
 */
int i_xpci3009_ReleaseAnalogInputSequence (struct pci_dev * pdev)
{
   	uint32_t dw_ReadValueDWord = 0;
   	uint32_t dw_ReadMaskDWord = 0;
	int    i_ReturnValue = 0;

	if (!pdev) return 1;

	{
		XPCI3009_DEBUG_FCN();
	}

	if (XPCI3009_PRIVDATA(pdev)->b_AnalogInputSequenceInitialised != ADDIDATA_ENABLE)
		return 2;

	if (XPCI3009_PRIVDATA(pdev)->b_AnalogInputSequenceStarted != ADDIDATA_DISABLE)
		return 3;

	dw_ReadMaskDWord = 0x80000;
	INPDW_MEM(GET_MEM_BAR3(pdev), 8, &dw_ReadValueDWord);
	dw_ReadValueDWord = dw_ReadValueDWord & dw_ReadMaskDWord;

	if (dw_ReadValueDWord == 0x00080000)
		/* Conversion started */
		return 3;

	//	i_ReturnValue = i_xpci3009_StopAnalogInputSequence(pdev);
	//
	//	if (XPCI3009_PRIVDATA(pdev)->b_AnalogInputDMA == ADDIDATA_ENABLE)
	//		{
	//		i_ReturnValue = i_xpci3009_StopAnalogInputDMA(pdev);
	//		}

	//	i_ReturnValue = i_xpci3009_ReleaseSharedMemory (pdev, (ADDIDATA_MAX_AI + 1) * sizeof(uint32_t));

	i_ReturnValue = i_xpci3009_ReleaseAnalogInputDMA(pdev);

	if (i_ReturnValue != 0)
		return 4;

	/* Complete the sequence structure */
	XPCI3009_PRIVDATA(pdev)->b_NumberOfSequenceChannels = 0;
	XPCI3009_PRIVDATA(pdev)->b_AnalogInputSequenceInitialised = ADDIDATA_DISABLE;
	XPCI3009_PRIVDATA(pdev)->b_AnalogInputDMA = ADDIDATA_DISABLE;
	XPCI3009_PRIVDATA(pdev)->dw_LastScatterGatherBuffer = 0;

	return 0;
}

/**
 * @retval 0 OK
 * @retval <> incorrect
 */
static int TriggerAction_is_valid(int TriggerAction)
{
	switch(TriggerAction)
	{
		case ADDIDATA_TRIGGER_START_A_SINGLE_CONVERSION:
		case ADDIDATA_ONE_SHOT_TRIGGER:
		case ADDIDATA_TRIGGER_START_A_SEQUENCE_SERIES:
		case ADDIDATA_TRIGGER_START_A_SINGLE_SEQUENCE:
		case ADDIDATA_TRIGGER_START_A_SCAN_SERIES:
		case ADDIDATA_TRIGGER_START_A_SINGLE_SCAN:
		case ADDIDATA_TRIGGER_START_A_AUTO_REFRESH_SERIES:
		case ADDIDATA_TRIGGER_START_A_SINGLE_AUTO_REFRESH:
			return 1;
		default:
			return 0;
	}
}
/**
 *
 * Enable, disable and initialise the analog input hardware trigger
 *
 * @param[in]  pdev : PCI Device struct              			\n
 * @param[in]   b_HardwareTrigger : 0 : disable the hardware trigger 	\n
 *                      						   1 : enable the hardware trigger 	\n
 * @param[in]   b_HardwareTriggerLevel : ADDIDATA_LOW: If the hardware   \n
 *              	                                	  trigger is used, it triggers  \n
 *            	      	                    	          from "1" to "0"               \n
 *              	        	                          ADDIDATA_HIGH: If the         \n
 *                  	        	                      hardware trigger is used, it  \n
 *                      	        	                  triggers from "0" to "1"      \n
 *                          	        	              ADDIDATA_LOW_HIGH: If the     \n
 *                              	        	          hardware trigger is used, it  \n
 *                                  	        	      triggers from "0" to "1" or   \n
 *                                      	        	  from "1" to "0"               \n
 * @param[in]   b_HardwareTriggerAction : Trigger action selection       \n
 *            			            	ADDIDATA_TRIGGER_START_A_SINGLE_CONVERSION	  0 \n
 *            			            	ADDIDATA_ONE_SHOT_TRIGGER					  1 \n
 *            			            	ADDIDATA_TRIGGER_START_A_SEQUENCE_SERIES	  2 \n
 *            			            	ADDIDATA_TRIGGER_START_A_SINGLE_SEQUENCE	  3 \n
 *            			            	ADDIDATA_TRIGGER_START_A_SCAN_SERIES	      6 \n
 *            			            	ADDIDATA_TRIGGER_START_A_SINGLE_SCAN  		  7 \n
 *            			            	ADDIDATA_TRIGGER_START_A_AUTO_REFRESH_SERIES 10 \n
 *            			            	ADDIDATA_TRIGGER_START_A_SINGLE_AUTO_REFRESH 11 \n
 * @param[in] dw_HardwareTriggerCount  : Hardware trigger counter.      \n
 *                  		                              Define the number of trigger  \n
 *                          		                      events before the action      \n
 *                                  		              occur (> 0)                   \n
 *
 *
 * @retval 0 No error
 * @retval 1 The handle parameter of the board is wrong
 * @retval 2 Wrong hardware trigger parameter
 * @retval 3 Wrong hardware trigger level parameter
 * @retval 4 Wrong hardware trigger action parameter
 * @retval 5 Wrong hardware trigger count parameter
 * @retval 6 No analog input functionality available
 * @retval 7 A conversion is already started
 */
int i_xpci3009_EnableDisableAnalogInputHardwareTrigger (struct pci_dev * pdev,
							                                uint8_t   b_HardwareTrigger,
															uint8_t     b_HardwareTriggerLevel,
															uint8_t     b_HardwareTriggerAction,
															uint32_t    dw_HardwareTriggerCount)
{
//	uint32_t  dw_Configuration = 0;
	uint32_t  dw_ResetMask = 0;
	uint32_t  dw_SetMask = 0;
	uint32_t  dw_WriteValueDWord = 0;
	uint32_t  dw_ReadMaskDWord = 0;
	uint32_t  dw_ReadValueDWord = 0;

	if (!pdev) return 1;

	{
		XPCI3009_DEBUG_FCN();
	}

	/* Tests the hardware trigger flag */
	if( !is_boolean(b_HardwareTrigger) )
		return 2;

	if( !TriggerAction_is_valid(b_HardwareTriggerAction) )
		return 3;

	if ((b_HardwareTriggerLevel != ADDIDATA_LOW) && (b_HardwareTriggerLevel != ADDIDATA_HIGH) && (b_HardwareTriggerLevel != ADDIDATA_LOW_HIGH) && (b_HardwareTrigger == ADDIDATA_ENABLE))
		return 4;

	if (((dw_HardwareTriggerCount > 65535) || (dw_HardwareTriggerCount == 0)) && (b_HardwareTrigger == ADDIDATA_ENABLE))
		return 5;

	if (0 == XPCI3009_PRIVDATA(pdev)->b_NumberOfAnalogInput)
		return 6;

	if (b_HardwareTrigger != ADDIDATA_DISABLE)
		{
		dw_ReadMaskDWord = 0x80000;
		INPDW_MEM(GET_MEM_BAR3(pdev), 8, &dw_ReadValueDWord);
		dw_ReadValueDWord = dw_ReadValueDWord & dw_ReadMaskDWord;

		if (dw_ReadValueDWord == 0x00080000)
			/* Conversion started */
			return 7;

		}

	if (b_HardwareTriggerAction == ADDIDATA_ONE_SHOT_TRIGGER)
		{
	  	/* Set the one shot trigger mode */
	    b_HardwareTriggerAction = 0;

	    }
	else
	    {
	  	/* Test if Single channel trigger mode */
	  	if (b_HardwareTriggerAction == ADDIDATA_TRIGGER_START_A_SINGLE_CONVERSION)
	    	{
	 		/* Set the single channel trigger mode */
	     	b_HardwareTriggerAction = 1;

			}
		}

	if (b_HardwareTrigger != ADDIDATA_DISABLE)
		{
	   	/*Enable the extern trigger, the level selection and set the trigger counter value */
		dw_SetMask = (uint32_t) (((uint32_t) b_HardwareTriggerLevel & 1)    << 2) |
	                  (uint32_t) (((uint32_t) b_HardwareTriggerLevel & 2)    << 0) |
	                  (uint32_t) (((uint32_t) (b_HardwareTriggerAction & 3)) << 3) |
	                  (uint32_t) (((uint32_t) dw_HardwareTriggerCount)       << 16);
		dw_ResetMask = 0xFFFF007E;
		INPDW_MEM(GET_MEM_BAR3(pdev), 64, &dw_WriteValueDWord);
		dw_WriteValueDWord = (dw_WriteValueDWord & (0xFFFFFFFFUL - dw_ResetMask)) | dw_SetMask;
		OUTPDW_MEM(GET_MEM_BAR3(pdev), 64, dw_WriteValueDWord);
	   	/*i_ErrorCode = SET_RESET_VALUE (b_CallLevel,
	                                  ps_HardwareInformation,
	                                  64,
	                                  (uint32_t) (((uint32_t) b_HardwareTriggerLevel & 1)    << 2) |
	                                  (uint32_t) (((uint32_t) b_HardwareTriggerLevel & 2)    << 0) |
	                                  (uint32_t) (((uint32_t) (b_HardwareTriggerAction & 3)) << 3) |
	                                  (uint32_t) (((uint32_t) dw_HardwareTriggerCount)       << 16),
	                                  0xFFFF007E,
	                                  0,
	                                  NULL,
	                                  0,
	                                  0,
	                                  NULL);*/
		}

	if (b_HardwareTrigger == ADDIDATA_ENABLE)
		{
        /* Enable the extern trigger */
		dw_SetMask = 0x1;
		dw_ResetMask = 0;
		INPDW_MEM(GET_MEM_BAR3(pdev), 64, &dw_WriteValueDWord);
		dw_WriteValueDWord = (dw_WriteValueDWord & (0xFFFFFFFFUL - dw_ResetMask)) | dw_SetMask;
		OUTPDW_MEM(GET_MEM_BAR3(pdev), 64, dw_WriteValueDWord);
		}
	else
		{
        /*Reset the Enable bit */
		dw_SetMask = 0;
		dw_ResetMask = 0x1;
		INPDW_MEM(GET_MEM_BAR3(pdev), 64, &dw_WriteValueDWord);
		dw_WriteValueDWord = (dw_WriteValueDWord & (0xFFFFFFFFUL - dw_ResetMask)) | dw_SetMask;
		OUTPDW_MEM(GET_MEM_BAR3(pdev), 64, dw_WriteValueDWord);
		}

	return 0;
}


/**
 *
 * Returns the status (occur or not), the state from input
 *
 * @param[in]  pdev : PCI Device struct              			\n
 *
 * @param[out]   pb_HardwareTriggerStatus : 0: Hardware trigger          \n
 *                                                            did not occur             \n
 *                                                         1: Hardware trigger          \n
 *                                                            occurred                  \n
 * @param[out] pdw_HardwareTriggerCount  : Number of pulse that         \n
 *                                                         fail before the next         \n
 *                                                         trigger occur                \n
 * @param[out]   pb_HardwareTriggerState  : 0: Hardware trigger          \n
 *                                                            input is not              \n
 *                                                            active                    \n
 *                                                            (Low state)               \n
 *                                                         1: Hardware trigger          \n
 *                                                            input is active           \n
 *                                                            (High state)              \n
 *
 * @retval 0 No error
 * @retval 1 The handle parameter of the board is wrong
 * @retval 2 No analog input functionality available
 */
int i_xpci3009_GetAnalogInputHardwareTriggerStatus (struct pci_dev * pdev,
										              uint8_t*   pb_HardwareTriggerStatus,
										              uint32_t* pdw_HardwareTriggerCount,
										              uint8_t*   pb_HardwareTriggerState)
{
	uint32_t  dw_ReadValueDWord = 0;

	if (!pdev) return 1;

	{
		XPCI3009_DEBUG_FCN();
	}

	if (0 == XPCI3009_PRIVDATA(pdev)->b_NumberOfAnalogInput)
		return 2;

	/* Read the status */
	INPDW_MEM(GET_MEM_BAR3(pdev), 68, &dw_ReadValueDWord);
	dw_ReadValueDWord = dw_ReadValueDWord & 0xFFFF0003UL;
	/*dw_Status = READ_BIT_MASK   (b_CallLevel,
	                                ps_HardwareInformation,
	                                68,
	                                0xFFFF0003UL,
	                                &i_ErrorCode);*/

	/* Return the status */
	*pb_HardwareTriggerStatus = (uint8_t) ((dw_ReadValueDWord >> 0) & 1);
	*pb_HardwareTriggerState  = (uint8_t) ((dw_ReadValueDWord >> 1) & 1);
	*pdw_HardwareTriggerCount = (dw_ReadValueDWord >> 16) & 0xFFFFUL;

	return 0;
}


/**
 *
 * Enable, disable and initialise the analog input software trigger
 *
 * @param[in]  pdev : PCI Device struct              			\n
 * @param[in]   b_SoftwareTrigger : 0 : disable the software trigger 	\n
 *                      						   1 : enable the software trigger 	\n
 *                      b_SoftwareTriggerAction : Trigger action selection              \n
 *            			            	ADDIDATA_TRIGGER_START_A_SINGLE_CONVERSION	  0 \n
 *            			            	ADDIDATA_ONE_SHOT_TRIGGER					  1 \n
 *            			            	ADDIDATA_TRIGGER_START_A_SEQUENCE_SERIES	  2 \n
 *            			            	ADDIDATA_TRIGGER_START_A_SINGLE_SEQUENCE	  3 \n
 *            			            	ADDIDATA_TRIGGER_START_A_SCAN_SERIES	      6 \n
 *            			            	ADDIDATA_TRIGGER_START_A_SINGLE_SCAN  		  7 \n
 *            			            	ADDIDATA_TRIGGER_START_A_AUTO_REFRESH_SERIES 10 \n
 *            			            	ADDIDATA_TRIGGER_START_A_SINGLE_AUTO_REFRESH 11 \n
 *
 *
 * @retval 0 No error
 * @retval 1 The handle parameter of the board is wrong
 * @retval 2 Wrong software trigger parameter
 * @retval 3 Wrong software trigger action parameter
 * @retval 4 No analog input functionality available
 * @retval 5 A conversion is already started
 */
int i_xpci3009_EnableDisableAnalogInputSoftwareTrigger (struct pci_dev * pdev,
							                                uint8_t   b_SoftwareTrigger,
											                uint8_t   b_SoftwareTriggerAction)
{
//	uint32_t  dw_Configuration = 0;
	uint32_t  dw_ResetMask = 0;
	uint32_t  dw_SetMask = 0;
	uint32_t  dw_WriteValueDWord = 0;
	uint32_t  dw_ReadMaskDWord = 0;
	uint32_t  dw_ReadValueDWord = 0;

	if (!pdev) return 1;

	{
		XPCI3009_DEBUG_FCN();
	}

	/* Tests the hardware trigger flag */
	if( !is_boolean(b_SoftwareTrigger) )
		return 2;

	if( !TriggerAction_is_valid(b_SoftwareTriggerAction) )
		return 3;

	if (0 == XPCI3009_PRIVDATA(pdev)->b_NumberOfAnalogInput)
		return 4;

	if (b_SoftwareTrigger != ADDIDATA_DISABLE)
		{
		dw_ReadMaskDWord = 0x80000;
		INPDW_MEM(GET_MEM_BAR3(pdev), 8, &dw_ReadValueDWord);
		dw_ReadValueDWord = dw_ReadValueDWord & dw_ReadMaskDWord;

		if (dw_ReadValueDWord == 0x00080000)
			/* Conversion started */
			return 5;

		/* Init the software trigger */
		dw_SetMask = (uint32_t) (((uint32_t) (b_SoftwareTriggerAction & 3)) << 1);
		dw_ResetMask = 0x0000001E;
		INPDW_MEM(GET_MEM_BAR3(pdev), 72, &dw_WriteValueDWord);
		dw_WriteValueDWord = (dw_WriteValueDWord & (0xFFFFFFFFUL - dw_ResetMask)) | dw_SetMask;
		OUTPDW_MEM(GET_MEM_BAR3(pdev), 72, dw_WriteValueDWord);
		/*i_ErrorCode = SET_RESET_VALUE (b_CallLevel,
	                                  ps_HardwareInformation,
	                                  72,
	                                  (uint32_t) (((uint32_t) (b_SoftwareTriggerAction & 3)) << 1),
	                                  0x0000001E,
	                                  0,
	                                  NULL,
	                                  0,
	                                  0,
	                                  NULL);*/
		}

	if (b_SoftwareTrigger == ADDIDATA_ENABLE)
		{
        /* Enable the extern trigger */
		dw_SetMask = 0x1;
		dw_ResetMask = 0;
		INPDW_MEM(GET_MEM_BAR3(pdev), 72, &dw_WriteValueDWord);
		dw_WriteValueDWord = (dw_WriteValueDWord & (0xFFFFFFFFUL - dw_ResetMask)) | dw_SetMask;
		OUTPDW_MEM(GET_MEM_BAR3(pdev), 72, dw_WriteValueDWord);
		}
	else
		{
        /*Reset the Enable bit */
		dw_SetMask = 0;
		dw_ResetMask = 0x1;
		INPDW_MEM(GET_MEM_BAR3(pdev), 72, &dw_WriteValueDWord);
		dw_WriteValueDWord = (dw_WriteValueDWord & (0xFFFFFFFFUL - dw_ResetMask)) | dw_SetMask;
		OUTPDW_MEM(GET_MEM_BAR3(pdev), 72, dw_WriteValueDWord);
		}

	return 0;
}


/**
 *
 * Trigger the analog input conversion
 *
 * @param[in]  pdev : PCI Device struct              			\n
 *
 *
 * @retval 0 No error
 * @retval 1 The handle parameter of the board is wrong
 * @retval 2 No analog input functionality available
 */
int i_xpci3009_AnalogInputSoftwareTrigger (struct pci_dev * pdev)
{
	uint32_t  dw_ResetMask = 0;
	uint32_t  dw_SetMask = 0;
	uint32_t  dw_WriteValueDWord = 0;

	if (!pdev) return 1;

	{
		XPCI3009_DEBUG_FCN();
	}

	if (0 == XPCI3009_PRIVDATA(pdev)->b_NumberOfAnalogInput)
		return 2;

	/* Software trigger */
	dw_SetMask = 0x00000020;
	dw_ResetMask = 0x00000000;
	INPDW_MEM(GET_MEM_BAR3(pdev), 72, &dw_WriteValueDWord);
	dw_WriteValueDWord = (dw_WriteValueDWord & (0xFFFFFFFFUL - dw_ResetMask)) | dw_SetMask;
	OUTPDW_MEM(GET_MEM_BAR3(pdev), 72, dw_WriteValueDWord);
	/*i_ErrorCode = SET_RESET_VALUE (b_CallLevel,
                                  ps_HardwareInformation,
                                  72,
                                  0x00000020,
                                  0x00000000,
                                  0,
                                  NULL,
                                  0,
                                  0,
                                  NULL);*/

	return 0;
}


/**
 *
 * Returns the status (occur or not) from software trigger
 *
 * @param[in]  pdev : PCI Device struct              			\n
 *
 * @param[out]   pb_SoftwareTriggerStatus : 0: Software trigger          \n
 *                                                            did not occur             \n
 *                                                         1: Software trigger          \n
 *                                                            occurred                  \n
 *
 * @retval 0 No error
 * @retval 1 The handle parameter of the board is wrong
 * @retval 2 No analog input functionality available
 */
int i_xpci3009_GetAnalogInputSoftwareTriggerStatus (struct pci_dev * pdev,
										              uint8_t*   pb_SoftwareTriggerStatus)
{
	uint32_t  dw_ReadValueDWord = 0;

	if (!pdev) return 1;

	{
		XPCI3009_DEBUG_FCN();
	}

	if (0 == XPCI3009_PRIVDATA(pdev)->b_NumberOfAnalogInput)
		return 2;

	/* Read the status */
	INPDW_MEM(GET_MEM_BAR3(pdev), 76, &dw_ReadValueDWord);
	dw_ReadValueDWord = dw_ReadValueDWord & 0x00000001UL;
	/*dw_Status = READ_BIT_MASK   (b_CallLevel,
                                        ps_HardwareInformation,
                                        76,
                                        0x00000001UL,
                                        &i_ErrorCode);*/

	/* Return the status */
	*pb_SoftwareTriggerStatus = (uint8_t) ((dw_ReadValueDWord >> 0) & 1);

	return 0;
}



/**
 *
 * Enable, disable and initialise the analog input hardware gate
 *
 * @param[in]  pdev : PCI Device struct              			\n
 * @param[in]   b_HardwareGate : 0 : disable the hardware gate       	\n
 *                      						   1 : enable the hardware gate 		\n
 *                      b_HardwareGateLevel     : ADDIDATA_LOW: If the hardware			\n
 *                                                gate is used, it is active to			\n
 *                                                "0"                          			\n
 *                                                ADDIDATA_HIGH:If the hardware			\n
 *                                                gate is used, it is active to			\n
 *                                                "1"                          			\n
 *
 *
 * @retval 0 No error
 * @retval 1 The handle parameter of the board is wrong
 * @retval 2 Wrong hardware gate parameter
 * @retval 3 Wrong hardware gate level parameter
 * @retval 4 No analog input functionality available
 * @retval 5 A conversion is already started
 *
 */
int i_xpci3009_EnableDisableAnalogInputHardwareGate (struct pci_dev * pdev,
							                                uint8_t   b_HardwareGate,
															uint8_t     b_HardwareGateLevel)
{
//	uint32_t  dw_Configuration = 0;
	uint32_t  dw_ResetMask = 0;
	uint32_t  dw_SetMask = 0;
	uint32_t  dw_WriteValueDWord = 0;
	uint32_t  dw_ReadMaskDWord = 0;
	uint32_t  dw_ReadValueDWord = 0;

	if (!pdev) return 1;

	{
		XPCI3009_DEBUG_FCN();
	}

	/* Tests the hardware gate flag */
	if( !is_boolean(b_HardwareGate) )
		return 2;

	if ((b_HardwareGateLevel != ADDIDATA_LOW) && (b_HardwareGateLevel != ADDIDATA_HIGH))
		return 3;

	if (0 == XPCI3009_PRIVDATA(pdev)->b_NumberOfAnalogInput)
		return 4;

	dw_ReadMaskDWord = 0x80000;
	INPDW_MEM(GET_MEM_BAR3(pdev), 8, &dw_ReadValueDWord);
	dw_ReadValueDWord = dw_ReadValueDWord & dw_ReadMaskDWord;

	if (dw_ReadValueDWord == 0x00080000)
		/* Conversion started */
		return 5;

	if (b_HardwareGate == ADDIDATA_ENABLE)
		{
        /*Enable the extern gate and the level */
		dw_SetMask = 0x00000001 | (uint32_t)(((uint32_t) (b_HardwareGateLevel - 1) & 1) << 1);
		dw_ResetMask = 0x3;
		INPDW_MEM(GET_MEM_BAR3(pdev), 80, &dw_WriteValueDWord);
		dw_WriteValueDWord = (dw_WriteValueDWord & (0xFFFFFFFFUL - dw_ResetMask)) | dw_SetMask;
		OUTPDW_MEM(GET_MEM_BAR3(pdev), 80, dw_WriteValueDWord);
        /*i_ErrorCode = SET_RESET_VALUE (b_CallLevel,
                                         ps_HardwareInformation,
                                         80,
                                         0x00000001 |
                                         (uint32_t)(((uint32_t) (b_HardwareGateLevel - 1) & 1) << 1),
                                         0x3,
                                         0,
                                         NULL,
                                         0,
                                         0,
                                         NULL);*/
		}
	else
		{
        /*Reset the Enable bit */
		dw_SetMask = 0;
		dw_ResetMask = 0x3;
		INPDW_MEM(GET_MEM_BAR3(pdev), 80, &dw_WriteValueDWord);
		dw_WriteValueDWord = (dw_WriteValueDWord & (0xFFFFFFFFUL - dw_ResetMask)) | dw_SetMask;
		OUTPDW_MEM(GET_MEM_BAR3(pdev), 80, dw_WriteValueDWord);
        /*i_ErrorCode = SET_RESET_VALUE (b_CallLevel,
                                         ps_HardwareInformation,
                                         80,
                                         0,
                                         0x3,
                                         0,
                                         NULL,
                                         0,
                                         0,
                                         NULL);*/
		}

	return 0;
}


/**
 *
 * Returns the status the state from input (active or not)
 *
 * @param[in]  pdev : PCI Device struct              			\n
 *
 * @param[out]   pb_HardwareGateStatus  : 0: Hardware gate input         \n
 *                                                          is not active               \n
 *                                                          (Low state)                 \n
 *                                                       1: Hardware gate input         \n
 *                                                          is active                   \n
 *                                                          (High state)                \n
 *
 * @retval 0 No error
 * @retval 1 The handle parameter of the board is wrong
 * @retval 2 No analog input functionality available
 */
int i_xpci3009_GetAnalogInputHardwareGateStatus (struct pci_dev * pdev,
										              uint8_t*   pb_HardwareGateStatus)
{
	uint32_t  dw_ReadValueDWord = 0;

	if (!pdev) return 1;

	{
		XPCI3009_DEBUG_FCN();
	}

	if (0 == XPCI3009_PRIVDATA(pdev)->b_NumberOfAnalogInput)
		return 2;

	/* Read the status */
	INPDW_MEM(GET_MEM_BAR3(pdev), 80, &dw_ReadValueDWord);
	dw_ReadValueDWord = dw_ReadValueDWord & 0x4UL;
	/*dw_Status = READ_BIT_MASK   (b_CallLevel,
	                                ps_HardwareInformation,
	                                80,
	                                0x4UL,
	                                &i_ErrorCode);*/

	/* Return the status */
	*pb_HardwareGateStatus  = (uint8_t) ((dw_ReadValueDWord >> 2) & 1);

	return 0;
}
