/** @file kapi_irq.c

   Contains interrupt kernel functions.

   @par CREATION
   @author Krauth Julien
   @date   30.05.06

   @par VERSION
   @verbatim
   $LastChangedRevision:$
   $LastChangedDate:$
   @endverbatim

   @par LICENCE
   @verbatim
    Copyright (C) 2009  ADDI-DATA GmbH for the source code of this module.

    ADDI-DATA GmbH
    Airpark Business Center
    Airport Boulevard B210
    77836 Rheinm�nster
    Germany
    Tel: +49(0)7229/1847-0
    Fax: +49(0)7229/1847-200
    http://www.addi-data-com
    info@addi-data.com

   This program is free software; you can redistribute it and/or modify it under
   the terms of the GNU General Public License as published by the Free Software
   Foundation; either version 2 of the License, or (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
   or FITNESS FOR A PARTICULAR PURPOSE.
   See the GNU General Public License for more details.

   You should have received a copy of the GNU General Public License along with
   this program; if not, write to the Free Software Foundation,
   Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

   You shoud also find the complete GPL in the COPYING file
   accompanying this source code.
   @endverbatim
 */

#include "xpci3009-private.h"
#include "irq-private-kapi.h"

EXPORT_SYMBOL(i_XPCI3009_SetBoardIntRoutine);
EXPORT_SYMBOL(i_XPCI3009_TestModuleInterrupt);

EXPORT_NO_SYMBOLS;

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

str_InterruptFunctionality s_InterruptFunctionality [] =
								 {{XPCI3009_INCREMENTAL_COUNTER, (v_XPCI3009_InterruptFunctionality) v_XPCI3009_IncrementalCounter_InterruptFunction},
								  {XPCI3009_PULSE_ENCODER      , (v_XPCI3009_InterruptFunctionality) v_XPCI3009_ImpulsCounter_InterruptFunction},
								 {XPCI3009_CHRONOMETER        , (v_XPCI3009_InterruptFunctionality) v_XPCI3009_Chronos_InterruptFunction}};

/* Not implemented */
//								 {XPCI3009_SSI_COUNTER        , (v_XPCI3009_InterruptFunctionality) NULL},
//								 {XPCI3009_TTL_IO             , (v_XPCI3009_InterruptFunctionality) NULL},
//								 {XPCI3009_DIGITAL_IO         , (v_XPCI3009_InterruptFunctionality) NULL},
//								 {XPCI3009_82X54_TIMER        , (v_XPCI3009_InterruptFunctionality) v_XPCI3009_82X54_InterruptFunction},
//								 {XPCI3009_TOR_COUNTER        , (v_XPCI3009_InterruptFunctionality) v_XPCI3009_TOR_InterruptFunction},
//								 {XPCI3009_PWM                , (v_XPCI3009_InterruptFunctionality) v_XPCI3009_PWM_InterruptFunction},
//								 {XPCI3009_ETM                , (v_XPCI3009_InterruptFunctionality) v_XPCI3009_ETM_InterruptFunction},
//								 {XPCI3009_CDA                , (v_XPCI3009_InterruptFunctionality) v_XPCI3009_CDA_InterruptFunction},
//								 {XPCI3009_SSI_MONITOR        , (v_XPCI3009_InterruptFunctionality) v_XPCI3009_SSIMon_InterruptFunction}};

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

/* removes the user handler for a given module */
int i_XPCI3009_ClearUserHandler(struct pci_dev * pdev, uint8_t b_ModulNbr)
{
	/* Clear the module interrupt function address */
	XPCI3009_PRIVDATA(pdev)->s_InterruptFunctionality [b_ModulNbr].v_InterruptFunction = NULL;
	return 0;
}
//------------------------------------------------------------------------------
/** Disable and reset the interrupt routine.
 *
 * @param [in] pdev              : The device to initialize.
 *
 * @retval 0: No error.
 * @retval 1: The handle parameter of the board is wrong.
 */

/* moved in file reset_board-kapi.c */
//int   i_XPCI3009_ResetBoardIntRoutine     (struct pci_dev * pdev)
//	{
//	uint8_t   b_ModuleCpt       = 0;
//
//		if (!pdev) return 1;
//
//		/* Disable all XPCI-3009 interrupt */
//		for (b_ModuleCpt = 0; b_ModuleCpt < XPCI3009_PRIVDATA(pdev)->s_BoardInfos.b_NumberOfModule; b_ModuleCpt ++)
//			{
//			/* Test if incremental counter */
//			if ( XPCI3009_MODULE_FUNCTIONALITY(pdev,b_ModuleCpt) == XPCI3009_INCREMENTAL_COUNTER)
//				{
//			  	/* Disable the frequency measurement */
//			  	OUTPDW_MEM(GET_MEM_BAR3(pdev), APCI1710_FUNC_OFFSET+
//						20 + MODULE_OFFSET(b_ModuleCpt),
//						XPCI3009_PRIVDATA(pdev)->
//						s_ModuleInfo [b_ModuleCpt].
//						s_SiemensCounterInfo.
//						s_ModeRegister.
//						dw_ModeRegister1_2_3_4 & (((XPCI3009_DISABLE_FREQUENCY & XPCI3009_DISABLE_FREQUENCY_INT) << 12) | 0xFF));
//
//				/* Disable interrupt */
//				XPCI3009_PRIVDATA(pdev)->
//				s_ModuleInfo [b_ModuleCpt].
//				s_SiemensCounterInfo.
//				s_ModeRegister.
//				s_ByteModeRegister.
//				b_ModeRegister3 = XPCI3009_PRIVDATA(pdev)->
//									s_ModuleInfo [b_ModuleCpt].
//									s_SiemensCounterInfo.
//									s_ModeRegister.
//									s_ByteModeRegister.
//									b_ModeRegister3 & XPCI3009_DISABLE_FREQUENCY & XPCI3009_DISABLE_FREQUENCY_INT;
//
//				/* Disable the extern latch interrupt */
//				OUTPDW_MEM(GET_MEM_BAR3(pdev), APCI1710_FUNC_OFFSET+
//						20 + MODULE_OFFSET(b_ModuleCpt),
//						XPCI3009_PRIVDATA(pdev)->
//						s_ModuleInfo [b_ModuleCpt].
//						s_SiemensCounterInfo.
//						s_ModeRegister.
//						dw_ModeRegister1_2_3_4 & ((XPCI3009_DISABLE_LATCH_INT << 8) | 0xFF));
//
//				mdelay (1);
//
//				/* Disable interrupt */
//				XPCI3009_PRIVDATA(pdev)->
//				s_ModuleInfo [b_ModuleCpt].
//				s_SiemensCounterInfo.
//				s_ModeRegister.
//				s_ByteModeRegister.
//				b_ModeRegister2 = XPCI3009_PRIVDATA(pdev)->
//									s_ModuleInfo [b_ModuleCpt].
//									s_SiemensCounterInfo.
//									s_ModeRegister.
//									s_ByteModeRegister.
//									b_ModeRegister2 & XPCI3009_DISABLE_LATCH_INT;
//				}
//   			} // for (b_FctCpt = 0; b_FctCpt < 2; b_FctCpt ++)
//
//			/* Functionality interrupt function installation */
//			for (b_ModuleCpt = 0; b_ModuleCpt < XPCI3009_PRIVDATA(pdev)->s_BoardInfos.b_NumberOfModule; b_ModuleCpt ++)
//				{
//				/* Test if interrupt function installed */
//				if (XPCI3009_PRIVDATA(pdev)->
//					s_InterruptFunctionality [b_ModuleCpt].
//					v_InterruptFunction != NULL)
//					{
//					/* Clear the module interrupt function address */
//					i_XPCI3009_ClearUserHandler(struct pci_dev * b_ModulNbr)
//					XPCI3009_PRIVDATA(pdev)->
//					s_InterruptFunctionality [b_ModuleCpt].
//					v_InterruptFunction = NULL;
//					} // if (ps_XPCI3009Variable->s_Board [b_BoardHandle].s_InterruptFunctionality [b_ModuleCpt].v_InterruptFunction != NULL)
//				} // for (b_ModuleCpt = 0; b_ModuleCpt < 4; b_ModuleCpt ++)
//
//
//
//				XPCI3009_PRIVDATA(pdev)->s_UserInterruptCallback.v_UserInterruptFunction = NULL;
//
//				XPCI3009_PRIVDATA(pdev)->s_InterruptInfos.b_InterruptInitialized = 0;
//
//	   return 0;
//	   }

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

/** Enable and set the interrupt routine.
 *
 * @param [in] pdev              : The device to initialize.
 * @param [in] InterruptCallback : The user interrupt callback.
 *
 * @retval 0: No error.
 * @retval 1: The handle parameter of the board is wrong.
 * @retval 2: The interrupt callback is NULL.
 */
int   i_XPCI3009_SetBoardIntRoutine  (struct pci_dev * pdev, void (*InterruptCallback) (struct pci_dev * pdev))
	{
	int  i_ReturnValue = 0;
	uint8_t b_ModuleCpt = 0;
	uint8_t b_SerachModuleCpt = 0;
	uint8_t b_FunctionalityCpt = 0;

		if (!pdev)
			return 1;

		/* If the global interrupt is not set */
		if (XPCI3009_PRIVDATA(pdev)->s_UserInterruptCallback.v_UserInterruptFunction == NULL)
			{
			/* Set the user callback function */
	        XPCI3009_PRIVDATA(pdev)->s_UserInterruptCallback.v_UserInterruptFunction = InterruptCallback;
			}

		/* For all modules */
		for (b_ModuleCpt = 0; b_ModuleCpt < NUMBER_OF_MODULE(pdev); b_ModuleCpt ++)
			{
			/* For all functionality */
			for (b_FunctionalityCpt = 0; b_FunctionalityCpt < (sizeof (s_InterruptFunctionality) / sizeof (str_InterruptFunctionality)); b_FunctionalityCpt ++)
			{
				/* If the module functionality can managed interrupt and the interrupt is set. */
				if ( ( XPCI3009_MODULE_FUNCTIONALITY(pdev,b_ModuleCpt) ==s_InterruptFunctionality[b_FunctionalityCpt].dw_Functionality) &&
					 (s_InterruptFunctionality[b_FunctionalityCpt].v_InterruptFunction != NULL) )
				{
					break;
				}
			}

			/* Test if interrupt used for this functionality */
			if (b_FunctionalityCpt < (sizeof (s_InterruptFunctionality) / sizeof (str_InterruptFunctionality)))
				{
				/* Search if any other module has installed the same interrupt functionality */
				for (b_SerachModuleCpt = 0; b_SerachModuleCpt < NUMBER_OF_MODULE(pdev); b_SerachModuleCpt ++)
				{
					if ( (XPCI3009_PRIVDATA(pdev)->s_InterruptFunctionality [b_SerachModuleCpt].v_InterruptFunction != NULL) &&
						 ( XPCI3009_MODULE_FUNCTIONALITY(pdev,b_ModuleCpt) == XPCI3009_MODULE_FUNCTIONALITY(pdev,b_SerachModuleCpt) ) )
						/*((XPCI3009_PRIVDATA(pdev)->s_BoardInfos.dw_MolduleConfiguration [b_ModuleCpt] & 0xFFFF0000UL) ==
						(XPCI3009_PRIVDATA(pdev)->s_BoardInfos.dw_MolduleConfiguration [b_SerachModuleCpt] & 0xFFFF0000UL)))
						*/
					{
						/* Save the interrupt function */
						XPCI3009_PRIVDATA(pdev)->s_InterruptFunctionality [b_ModuleCpt].v_InterruptFunction =
						XPCI3009_PRIVDATA(pdev)->s_InterruptFunctionality [b_SerachModuleCpt].v_InterruptFunction;
						break;
					}
				} // for (b_SerachModuleCpt = 0; b_SerachModuleCpt < 4; b_SerachModuleCpt ++)

				/* Test if interrupt function found for this functionality */
				if (b_SerachModuleCpt != NUMBER_OF_MODULE(pdev))
					{
					continue;
					} // if (b_SerachModuleCpt != 4)

				/* Test if not interrupt function found for this functionality */
				/* Save the functionality interrupt function */
				if (XPCI3009_PRIVDATA(pdev)->s_InterruptFunctionality [b_ModuleCpt].v_InterruptFunction == NULL)
					XPCI3009_PRIVDATA(pdev)->s_InterruptFunctionality [b_ModuleCpt].v_InterruptFunction = s_InterruptFunctionality[b_FunctionalityCpt].v_InterruptFunction;

				} // if (b_FunctionalityCpt < (sizeof (s_InterruptFunctionality)) / sizeof (str_InterruptFunctionality)))
			else
				{
				/* No interrupt function used */
				i_XPCI3009_ClearUserHandler(pdev, b_ModuleCpt);
				}
			} // for (b_ModuleCpt = 0; b_ModuleCpt < 4; b_ModuleCpt ++)

			XPCI3009_PRIVDATA(pdev)->s_InterruptInfos.b_InterruptInitialized = 1;

		return (i_ReturnValue);
	}

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

/** User interrupt function call management.
 *
 * @param [in] pdev                : The device to initialize.
 * @param [in] b_ModulNbr          : Module number to configure (0 to 3).
 * @param [in] ul_InterruptMask    : Interrupt mask.
 * @param [in] ul_ConterValue      : Counter value.
 *
 */
static void v_XPCI3009_UserInterruptManagement (struct pci_dev *pdev,
                                            uint8_t b_Module,
                                            uint32_t ul_InterruptMask,
                                            uint32_t ul_ConterValue)
	{
	/***************************/
	/* Save the Interrupt mask */
	/***************************/

    XPCI3009_PRIVDATA(pdev)->
	s_InterruptParameters.
	s_FIFOInterruptParameters [XPCI3009_PRIVDATA(pdev)->
				   s_InterruptParameters.
				   ui_Write].
	ul_OldInterruptMask = ul_InterruptMask;

	/**************************/
	/* Save the Module number */
	/**************************/

	XPCI3009_PRIVDATA(pdev)->
	s_InterruptParameters.
	s_FIFOInterruptParameters [XPCI3009_PRIVDATA(pdev)->
				   s_InterruptParameters.
				   ui_Write].
	b_OldModuleMask = b_Module;

	/**************************/
	/* Save the counter value */
	/**************************/

	XPCI3009_PRIVDATA(pdev)->
	s_InterruptParameters.
	s_FIFOInterruptParameters [XPCI3009_PRIVDATA(pdev)->
				   s_InterruptParameters.
				   ui_Write].
	ul_OldCounterLatchValue = ul_ConterValue;

	/***********************************/
	/* Increment the interrupt counter */
	/***********************************/

	XPCI3009_PRIVDATA(pdev)->
	s_InterruptParameters.
	ul_InterruptOccur ++;

	/****************************/
	/* Increment the write FIFO */
	/****************************/

	XPCI3009_PRIVDATA(pdev)->
	s_InterruptParameters.
	ui_Write = (XPCI3009_PRIVDATA(pdev)->
		    s_InterruptParameters.
		    ui_Write + 1) % XPCI3009_SAVE_INTERRUPT;

	/**********************/
	/* Call user function */
	/**********************/

	/* Call the user kernel callback */
	if (XPCI3009_PRIVDATA(pdev)->s_UserInterruptCallback.v_UserInterruptFunction != NULL)
		(XPCI3009_PRIVDATA(pdev)->s_UserInterruptCallback.v_UserInterruptFunction ) (pdev);

	/* Asynchronous IO signal implementation - send a SIGIO to all registered processes*/
	if (XPCI3009_PRIVDATA(pdev)->async_queue)
		{
		kill_fasync( &(XPCI3009_PRIVDATA(pdev)->async_queue), SIGIO, POLL_IN);
		}
	}

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

/** Incremental counter interrupt function management.
 *
 * @param [in] pdev              : The device to initialize.
 * @param [in] b_ModulNbr        : Module number to configure (0 to 3).
 * @param [in] ul_InterruptMask  : Interrupt mask.
 *
 * @param [out] pb_InterruptFlag : 0 -> No interrupt generated.
 *                                 1 -> Interrupt generated.
 */
void	v_XPCI3009_IncrementalCounter_InterruptFunction (struct pci_dev *pdev,
                                                         uint8_t b_Module,
                                                         uint8_t * pb_InterruptFlag)
{
	/*
	 * ALL PART THAT ARE NOT USEFULL ARE COMMENTED
	 * They can be implemented later that's why they are keept.
	 */
	uint32_t ul_StatusRegister = 0;
	uint32_t ul_InterruptLatchReg = 0;
	uint32_t ul_LatchRegisterValue = 0;
	uint32_t ui_16BitValue = 0;
		/* Test if latch interrupt is enabled. */
	if (XPCI3009_PRIVDATA(pdev)->
		s_ModuleInfo[b_Module].
		s_SiemensCounterInfo.
		s_ModeRegister.
		s_ByteModeRegister.
		b_ModeRegister2 & XPCI3009_ENABLE_LATCH_INT)
	{
		INPDW_MEM(GET_MEM_BAR3(pdev), APCI1710_FUNC_OFFSET+ MODULE_OFFSET(b_Module), &ul_InterruptLatchReg);

		/* Test if interrupt */
		if (ul_InterruptLatchReg & 0x66)
		{
			*pb_InterruptFlag = 1;

			/* Clear the interrupt */
			OUTPDW_MEM(GET_MEM_BAR3(pdev), APCI1710_FUNC_OFFSET+ 44 + MODULE_OFFSET(b_Module), ul_InterruptLatchReg & 0x66);
		}

		/* Test if interrupt */
		if ((ul_InterruptLatchReg & 0x66) && (XPCI3009_PRIVDATA(pdev)->
												s_ModuleInfo[b_Module].
												s_SiemensCounterInfo.
												s_ModeRegister.
												s_ByteModeRegister.
												b_ModeRegister2 & 0x80))
		{
			/* Test if strobe latch I interrupt */
			if (ul_InterruptLatchReg & 6)
			{
				INPDW_MEM(GET_MEM_BAR3(pdev), APCI1710_FUNC_OFFSET+ 4 + MODULE_OFFSET(b_Module), &ul_LatchRegisterValue);

				/* Set the interrupt flag */
				*pb_InterruptFlag = 1;

				/* Test if high level */
				if (ul_InterruptLatchReg & 2)
				{
					/* User interrupt management */
					v_XPCI3009_UserInterruptManagement (pdev, b_Module, 0x1UL, ul_LatchRegisterValue);
				}

				/* Test if low level */
				if (ul_InterruptLatchReg & 4)
				{
					/* User interrupt management */
					v_XPCI3009_UserInterruptManagement (pdev, b_Module, 0x10001UL, ul_LatchRegisterValue);
				}
			}

			/* Test if strobe latch II interrupt */
			if (ul_InterruptLatchReg & 0x60)
			{
				INPDW_MEM(GET_MEM_BAR3(pdev), APCI1710_FUNC_OFFSET+ 8 + MODULE_OFFSET(b_Module), &ul_LatchRegisterValue);

				/* Set the interrupt flag */
				*pb_InterruptFlag = 1;

				/* Test if high level */
				if (ul_InterruptLatchReg & 0x20)
				{
					/* User interrupt management */
					v_XPCI3009_UserInterruptManagement (pdev, b_Module, 0x2UL, ul_LatchRegisterValue);
				}

				/* Test if high level */
				if (ul_InterruptLatchReg & 0x40)
				{
					/* User interrupt management */
					v_XPCI3009_UserInterruptManagement (pdev, b_Module, 0x10002UL, ul_LatchRegisterValue);
				}
			}
		}
	}


	/* Read the register status for Index, Frequency and compare. */
	if (XPCI3009_PRIVDATA(pdev)->
		s_ModuleInfo[b_Module].
		s_SiemensCounterInfo.
		s_ModeRegister.
		s_ByteModeRegister.
		b_ModeRegister3 & (XPCI3009_ENABLE_INDEX_INT | XPCI3009_ENABLE_FREQUENCY_INT | XPCI3009_ENABLE_COMPARE_INT))
	{
		INPDW_MEM(GET_MEM_BAR3(pdev), APCI1710_FUNC_OFFSET+ 24 + MODULE_OFFSET(b_Module), &ul_InterruptLatchReg);
	}

	if (XPCI3009_PRIVDATA(pdev)->
		s_ModuleInfo[b_Module].
		s_SiemensCounterInfo.
		s_ModeRegister.
		s_ByteModeRegister.
		b_ModeRegister3 & XPCI3009_ENABLE_INDEX_INT)
	{
		/* Test if index interrupt */
		if (ul_InterruptLatchReg & 0x48)
		{
			*pb_InterruptFlag = 1;
			XPCI3009_PRIVDATA(pdev)->
			s_ModuleInfo[b_Module].
			s_SiemensCounterInfo.
			s_InitFlag.
			b_IndexInterruptOccur = 1;

			if (XPCI3009_PRIVDATA(pdev)->
				s_ModuleInfo[b_Module].
				s_SiemensCounterInfo.
				s_ModeRegister.
				s_ByteModeRegister.
				b_ModeRegister2 & XPCI3009_INDEX_AUTO_MODE)
			{
				OUTPDW_MEM(GET_MEM_BAR3(pdev), APCI1710_FUNC_OFFSET+
				20 + MODULE_OFFSET(b_Module),
				XPCI3009_PRIVDATA(pdev)->
				s_ModuleInfo[b_Module].
				s_SiemensCounterInfo.
				s_ModeRegister.
				dw_ModeRegister1_2_3_4);
			}

			/* Test if interrupt enabled */
			if ((XPCI3009_PRIVDATA(pdev)->
				s_ModuleInfo[b_Module].
				s_SiemensCounterInfo.
				s_ModeRegister.
				s_ByteModeRegister.
				b_ModeRegister3 & XPCI3009_ENABLE_INDEX_INT) == XPCI3009_ENABLE_INDEX_INT)
			{
				/* Set the interrupt flag */
				*pb_InterruptFlag = 1;

				/* Test if high level */
				if (ul_InterruptLatchReg & 0x8)
				{
					/* User interrupt management */
					v_XPCI3009_UserInterruptManagement (pdev, b_Module, 0x4UL, ul_LatchRegisterValue);
				}

				/* Test if low level */
				if (ul_InterruptLatchReg & 0x40)
				{
					/* User interrupt management */
					v_XPCI3009_UserInterruptManagement (pdev, b_Module, 0x10004UL, ul_LatchRegisterValue);
				}
			}
		}
	}

	if (XPCI3009_PRIVDATA(pdev)->
		s_ModuleInfo[b_Module].
		s_SiemensCounterInfo.
		s_ModeRegister.
		s_ByteModeRegister.
		b_ModeRegister3 & XPCI3009_ENABLE_COMPARE_INT)
	{
		/* Test if compare interrupt */
		if (ul_InterruptLatchReg & 0x10)
		{
			*pb_InterruptFlag = 1;

			/* Test if interrupt enabled */
			if ((XPCI3009_PRIVDATA(pdev)->
				s_ModuleInfo[b_Module].
				s_SiemensCounterInfo.
				s_ModeRegister.
				s_ByteModeRegister.
				b_ModeRegister3 & XPCI3009_ENABLE_COMPARE_INT) == XPCI3009_ENABLE_COMPARE_INT)
			{
				/* Set the interrupt flag */
				*pb_InterruptFlag = 1;
				ul_LatchRegisterValue = 0;

				/* User interrupt management */
				v_XPCI3009_UserInterruptManagement (pdev, b_Module, 0x8UL, ul_LatchRegisterValue);
			}
		}
	}

	if (XPCI3009_PRIVDATA(pdev)->
			s_ModuleInfo[b_Module].
			s_SiemensCounterInfo.
			s_ModeRegister.
			s_ByteModeRegister.
			b_ModeRegister3 & XPCI3009_ENABLE_FREQUENCY_INT)
	{
		/* Test if frequency measurement interrupt */
		if (ul_InterruptLatchReg & 0x20)
		{
			*pb_InterruptFlag = 1;

			/* Read the status */
			INPDW_MEM(GET_MEM_BAR3(pdev), APCI1710_FUNC_OFFSET+ 32 + MODULE_OFFSET(b_Module), &ul_StatusRegister);

			/* Read the value */
			INPDW_MEM(GET_MEM_BAR3(pdev), APCI1710_FUNC_OFFSET+ 28 + MODULE_OFFSET(b_Module), &ul_LatchRegisterValue);

			if (((ul_StatusRegister >> 1) & 3) == 0)
			{
				/* Test the counter mode */
				if ((XPCI3009_PRIVDATA(pdev)->
					s_ModuleInfo [b_Module].
					s_SiemensCounterInfo.
					s_ModeRegister.
					s_ByteModeRegister.
					b_ModeRegister1 & XPCI3009_16BIT_COUNTER) == XPCI3009_16BIT_COUNTER)
				{
					/* Test if 16-bit counter 1 pulse occur */
					if ((ul_LatchRegisterValue & 0xFFFFU) != 0)
					{
						ui_16BitValue         = (uint32_t) ul_LatchRegisterValue & 0xFFFFU;
						ul_LatchRegisterValue = (ul_LatchRegisterValue & 0xFFFF0000UL) | (0xFFFFU - ui_16BitValue);
					}

					/* Test if 16-bit counter 2 pulse occur */
					if ((ul_LatchRegisterValue & 0xFFFF0000UL) != 0)
					{
						ui_16BitValue         = (uint32_t) ((ul_LatchRegisterValue >> 16) & 0xFFFFU);
						ul_LatchRegisterValue = (ul_LatchRegisterValue & 0xFFFFUL) | ((0xFFFFU - ui_16BitValue) << 16);
					}
				}
				else
				{
					if (ul_LatchRegisterValue != 0)
					{
						ul_LatchRegisterValue = 0xFFFFFFFFUL - ul_LatchRegisterValue;
					}
				}
			}

			if (((ul_StatusRegister >> 1) & 3) == 1)
			{
					/* Test if 16-bit counter 2 pulse occur */

				if ((ul_LatchRegisterValue & 0xFFFF0000UL) != 0)
				{
					ui_16BitValue         = (uint32_t) ((ul_LatchRegisterValue >> 16) & 0xFFFFU);
					ul_LatchRegisterValue = (ul_LatchRegisterValue & 0xFFFFUL) | ((0xFFFFU - ui_16BitValue) << 16);
				}
			}

			if (((ul_StatusRegister >> 1) & 3) == 2)
			{
				/* Test if 16-bit counter 1 pulse occur */
				if ((ul_LatchRegisterValue & 0xFFFFU) != 0)
				{
					ui_16BitValue         = (uint32_t) ul_LatchRegisterValue & 0xFFFFU;
					ul_LatchRegisterValue = (ul_LatchRegisterValue & 0xFFFF0000UL) | (0xFFFFU - ui_16BitValue);
				}
			}

			/* Set the interrupt flag */
			*pb_InterruptFlag = 1;

			/* User interrupt management */
			v_XPCI3009_UserInterruptManagement (pdev, b_Module, 0x10000UL, ul_LatchRegisterValue);
		}
	}
}


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

/** Impuls counter interrupt function management.
 *
 * @param [in] pdev              : The device to initialize.
 * @param [in] b_ModulNbr        : Module number to configure (0 to 3).
 * @param [in] ul_InterruptMask  : Interrupt mask.
 *
 * @param [out] pb_InterruptFlag : 0 -> No interrupt generated.
 *                                 1 -> Interrupt generated.
 */
void	v_XPCI3009_ImpulsCounter_InterruptFunction (struct pci_dev *pdev,
                                                    uint8_t b_Module,
                                                    uint8_t * pb_InterruptFlag)
	{
	uint8_t   b_PulseIncoderCpt;
	uint32_t ul_StatusRegister;

	*pb_InterruptFlag = 0;

	if (((XPCI3009_PRIVDATA(pdev)->
	      s_ModuleInfo[b_Module].
	      s_PulseEncoderModuleInfo.
	      s_PulseEncoderInfo [0].
	      b_PulseEncoderInit == 1) ||
	     (XPCI3009_PRIVDATA(pdev)->
	      s_ModuleInfo[b_Module].
	      s_PulseEncoderModuleInfo.
	      s_PulseEncoderInfo [1].
	      b_PulseEncoderInit == 1) ||
	     (XPCI3009_PRIVDATA(pdev)->
	      s_ModuleInfo[b_Module].
	      s_PulseEncoderModuleInfo.
	      s_PulseEncoderInfo [2].
	      b_PulseEncoderInit == 1) ||
	     (XPCI3009_PRIVDATA(pdev)->
	      s_ModuleInfo[b_Module].
	      s_PulseEncoderModuleInfo.
	      s_PulseEncoderInfo [3].
	      b_PulseEncoderInit == 1)) &&
	    ((XPCI3009_PRIVDATA(pdev)->
	      s_ModuleInfo[b_Module].
	      s_PulseEncoderModuleInfo.
	      dw_SetRegister & 0xF) != 0))
	   {
	   /****************************/
	   /* Read the status register */
	   /****************************/

	   INPDW_MEM(GET_MEM_BAR3(pdev), APCI1710_FUNC_OFFSET+ MODULE_OFFSET(b_Module) + 20, &ul_StatusRegister);

	   if (ul_StatusRegister & 0xF)
	      {
	      for (b_PulseIncoderCpt = 0; b_PulseIncoderCpt < 4; b_PulseIncoderCpt ++)
	         {
	         /*************************************/
	         /* Test if pulse encoder initialised */
	         /*************************************/

	         if ((XPCI3009_PRIVDATA(pdev)->
	              s_ModuleInfo[b_Module].
		      s_PulseEncoderModuleInfo.
		      s_PulseEncoderInfo [b_PulseIncoderCpt].
		      b_PulseEncoderInit == 1) &&
		     (((XPCI3009_PRIVDATA(pdev)->
	                s_ModuleInfo[b_Module].
		        s_PulseEncoderModuleInfo.
		        dw_SetRegister >> b_PulseIncoderCpt) & 1) == 1) &&
		     (((ul_StatusRegister >> (b_PulseIncoderCpt)) & 1) == 1))
		    {
		    /**************************/
		    /* Set the interrupt flag */
		    /**************************/

		    *pb_InterruptFlag = 1;

		    /*****************************/
		    /* User interrupt management */
		    /*****************************/

		    v_XPCI3009_UserInterruptManagement (pdev, b_Module, 0x100UL << b_PulseIncoderCpt, 0);
		    }
	         }
	      }
	   }
	}

/** Chronos interrupt function management.
 *
 * @param [in] pdev              : The device to initialize.
 * @param [in] b_ModulNbr        : Module number to configure (0 to 3).
 * @param [in] ul_InterruptMask  : Interrupt mask.
 *
 * @param [out] pb_InterruptFlag : 0 -> No interrupt generated.
 *                                 1 -> Interrupt generated.
 */
void	v_XPCI3009_Chronos_InterruptFunction (struct pci_dev *pdev,
                                                    uint8_t b_Module,
                                                    uint8_t * pb_InterruptFlag)

{
	uint32_t ul_InterruptLatchReg = 0;
	uint32_t ul_LatchRegisterValue = 0;

	// Begin CG 2249-0505 -> 2250-0705 : Only Interrupt if interrupt was enabled !
	if (XPCI3009_PRIVDATA(pdev)->
		s_ModuleInfo [(int)b_Module].
		s_ChronoModuleInfo.
		b_InterruptMask != 0)
	{
	// End CG 2249-0505 -> 2250-0705 : Only Interrupt if interrupt was enabled !

		/*****************************/
		/* Read the interrupt status */
		/*****************************/

		INPDW_MEM(GET_MEM_BAR3(pdev), APCI1710_FUNC_OFFSET+ 12 + MODULE_OFFSET(b_Module), &ul_InterruptLatchReg);

		/***************************/
		/* Test if interrupt occur */
		/***************************/

		if ((ul_InterruptLatchReg & 0x8) == 0x8)
		{
			/****************************/
			/* Clear the interrupt flag */
			/****************************/

			OUTPDW_MEM(GET_MEM_BAR3(pdev), APCI1710_FUNC_OFFSET+
			32 + MODULE_OFFSET(b_Module),
			0);

			/***************************/
			/* Test if continuous mode */
			/***************************/

			if (XPCI3009_PRIVDATA(pdev)->
				s_ModuleInfo [(int)b_Module].
				s_ChronoModuleInfo.
				b_CycleMode == XPCI3009_ENABLE)
			{
			/********************/
			/* Clear the status */
			/********************/

			OUTPDW_MEM(GET_MEM_BAR3(pdev), APCI1710_FUNC_OFFSET+
			33 + MODULE_OFFSET(b_Module),
			0);
			}

			/*************************/
			/* Read the timing value */
			/*************************/

			INPDW_MEM(GET_MEM_BAR3(pdev), APCI1710_FUNC_OFFSET+ 4 + MODULE_OFFSET(b_Module), &ul_LatchRegisterValue);

			/*****************************/
			/* Test if interrupt enabled */
			/*****************************/

			if (XPCI3009_PRIVDATA(pdev)->
				s_ModuleInfo [(int)b_Module].
				s_ChronoModuleInfo.
				b_InterruptMask)
			{
				/**************************/
				/* Set the interrupt flag */
				/**************************/

				*pb_InterruptFlag = 1;

				/*****************************/
				/* User interrupt management */
				/*****************************/

				v_XPCI3009_UserInterruptManagement (pdev, b_Module, 0x80UL, ul_LatchRegisterValue);
			}
		}
		// Begin CG 2249-0505 -> 2250-0705 : Only Interrupt if interrupt was enabled !
	}
	// End CG 2249-0505 -> 2250-0705 : Only Interrupt if interrupt was enabled !
}

/** Return interrupt information.
 *
 * @param [in] pdev                   : The device to initialize.
 * @param [out] pb_ModuleMask         : Mask of the events <br>
 *                                      which have generated the interrupt.
 * @param [out] pul_InterruptMask     :
 * @param [out] pul_CounterLatchValue : The latched values of <br>
 *                                      the counter are returned.
 *
 * @retval 1  : No interrupt.
 * @retval >0 : IRQ number
 */
int   i_XPCI3009_TestModuleInterrupt (struct pci_dev *pdev,
								uint8_t *   pb_ModuleMask,
								uint32_t * pul_InterruptMask,
								uint32_t * pul_CounterLatchValue)
	{

		if (XPCI3009_PRIVDATA(pdev)->s_InterruptParameters.ul_InterruptOccur <= 0)
			return 1;

  		*pul_InterruptMask = XPCI3009_PRIVDATA(pdev)->
							s_InterruptParameters.
							s_FIFOInterruptParameters [XPCI3009_PRIVDATA(pdev)->s_InterruptParameters.ui_Read].ul_OldInterruptMask;

		*pb_ModuleMask = XPCI3009_PRIVDATA(pdev)->
							s_InterruptParameters.
							s_FIFOInterruptParameters [XPCI3009_PRIVDATA(pdev)->s_InterruptParameters.ui_Read].b_OldModuleMask;

		*pul_CounterLatchValue = XPCI3009_PRIVDATA(pdev)->
							s_InterruptParameters.
							s_FIFOInterruptParameters [XPCI3009_PRIVDATA(pdev)->s_InterruptParameters.ui_Read].ul_OldCounterLatchValue;

		XPCI3009_PRIVDATA(pdev)->s_InterruptParameters.ul_InterruptOccur --;

		/* Increment the read FIFO */
		XPCI3009_PRIVDATA(pdev)->s_InterruptParameters.ui_Read = (XPCI3009_PRIVDATA(pdev)->s_InterruptParameters.ui_Read + 1) % XPCI3009_SAVE_INTERRUPT;


		return (pdev->irq);
	}
