/** @file ioctl.c
*
* @author Sylvain Nahas
*
* This module implements the apci3600_do_ioctl function
*/

/** @par LICENCE
* @verbatim
 Copyright (C) 2004,2005  ADDI-DATA GmbH for the source code and the documentation.
        
        ADDI-DATA GmbH
        Dieselstrasse 3 
        D-77833 Ottersweier
        Tel: +19(0)7223/9493-0
        Fax: +49(0)7223/9493-92
        http://www.addi-data-com
        info@addi-data.com
 
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.
 
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details. 
        
    You shoud find the complete GPL in the COPYING file accompanying 
    this source code. 
* @endverbatim
*/

#include <linux/version.h>
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
	#include <linux/config.h>
#else
	#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,33)
		#include <linux/autoconf.h>
	#else
		#include <generated/autoconf.h>
	#endif
#endif

#include <linux/module.h>
#include <linux/fs.h>
#include <linux/pci.h>
#include <asm/io.h>
#if LINUX_VERSION_CODE < KERNEL_VERSION(3,4,0)
#include <asm/system.h>
#endif
#include <asm/uaccess.h>
#include <asm/bitops.h>
#include <linux/sched.h>
#include <linux/errno.h>

#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/delay.h>

#include <apci3600.h>

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



#ifdef IOCTL_DEBUG
#define IOCTL_DPRINTK(fmt, args...)	printk("%s: " fmt, __FUNCTION__ , ## args)
#define IOCTL_MARK()	printk("%s:%i\n", __FUNCTION__, __LINE__)
#define IOCTL_RETURN(__ret,__fcn, args...) { __ret = __fcn ( args ) ; printk( #__fcn "() returned %d\n", __ret); }
#else
#define IOCTL_DPRINTK(fmt, args...)
#define IOCTL_MARK()
#define IOCTL_RETURN(__ret,__fcn, args...) __ret = __fcn ( args ) ;
#endif	


//TODO: remove
//typedef struct
//{
//	uint32_t dw_NbrOfValues;
//	uint32_t dw_NbrOfComputerBuffer;
//	uint32_t 
//	pdw_ComputerBufferAddressKernelLevelArray
//	[PCI3600_MAX_NBR_COMPUTER_BUFFER];
//	uint32_t 
//	pdw_ComputerBufferAddressApplicLevelArray
//	[PCI3600_MAX_NBR_COMPUTER_BUFFER];
//	uint32_t dw_InterruptMask;
//	uint32_t dw_CompareInterrupt;
//	uint8_t b_FIFOErrorInterrupt;
//	uint8_t b_SDRAMOverFlowInterrupt;
//	uint8_t b_PCBufferOverFlowInterrupt;
//	uint8_t b_AutoStopInterrupt;
//	uint32_t dw_InterruptIndex;
//}
//str_Sample13_InterruptFIFO;
//typedef struct
//{
//	uint32_t dw_ReceiveInterrupt;
//	uint8_t b_Error;
//	//str_Sample13_InterruptFIFO ps_InterruptFIFO [SIZE_OF_INTERRUPT_FIFO];
//	uint32_t dw_ReadInterruptFIFOPointer;
//	uint32_t dw_WriteInterruptFIFOPointer;
//	uint8_t b_InterruptFIFOFull;
//	uint8_t b_InterruptFIFOEmpty;
//	uint8_t b_InterruptFIFOOverFlow;
//}
//str_Sample13_UserStruct;

//------------------------------------------------------------------------------
/** dummy command to be called when a ioctl command is incorrect */
static int apci3600_do_dummy (struct pci_dev *pdev, unsigned int cmd, unsigned long arg)
{
	printk (KERN_WARNING "%s: %d: invalid ioctl\n", __APCI3600_DRIVER_NAME, _IOC_NR (cmd));
	return -EINVAL;
}
static int apci3600_do_dummy_with_id (struct pci_dev *pdev, unsigned int cmd, unsigned long arg, unsigned long id)
{
	printk (KERN_WARNING "%s: %ld: %d: invalid ioctl\n", __APCI3600_DRIVER_NAME, id, _IOC_NR (cmd));
	return -EINVAL;
}
//------------------------------------------------------------------------------
int do_CMD_APCI3600_CheckAndGetPCISlotNumber (struct pci_dev *pdev, unsigned int cmd, unsigned long arg)
{
	int retval = 0;		/* return value of this call is the number of boards */
	char *b_SlotArray = NULL; 
	IOCTL_MARK();

	/* this command returns an area of size CONFIG_APCI3600_MAX_BOARD_NBR+1 filled with the PCI_SLOT() value and as last field the major number associated to the device - this is deprecated and should not be used anymore.
	   The call returns 0 when memory is too low or the number of boards  */
	b_SlotArray = kmalloc (CONFIG_APCI3600_MAX_BOARD_NBR + 1 * sizeof (char), GFP_KERNEL);
	if (!b_SlotArray)

	{
		/* bad param. Let output some information for debug */
		printk ("%s: CMD_APCI3600_CheckAndGetPCISlotNumber: -EINVAL\n", __APCI3600_DRIVER_NAME);
		return 0;
	}
	
	memset (b_SlotArray, 0, CONFIG_APCI3600_MAX_BOARD_NBR + 1);
	printk ("%s: the use of CMD_APCI3600_CheckAndGetPCISlotNumber is deprecated\n", __APCI3600_DRIVER_NAME);

	/* record the PCI_SLOT for each device from 0 to CONFIG_APCI3600_MAX_BOARD_NBR  */
	{
		int i;
		for (i = 0; ((i < atomic_read (&apci3600_count)) || (i < CONFIG_APCI3600_MAX_BOARD_NBR)); i++)

		{
			struct pci_dev *dev = apci3600_lookup_board_by_index (i);
			if (!dev)
				break;
			b_SlotArray[i] = PCI_SLOT (dev->devfn);
		}
	}
	b_SlotArray[CONFIG_APCI3600_MAX_BOARD_NBR] = apci3600_majornumber;
	
	if (copy_to_user ((uint8_t __user *) arg, b_SlotArray, sizeof (uint8_t[CONFIG_APCI3600_MAX_BOARD_NBR + 1])))
	{
		/* bad address. Let output some information for debug */
		printk ("%s: CMD_APCI3600_CheckAndGetPCISlotNumber: -EFAULT\n", __APCI3600_DRIVER_NAME);
		kfree (b_SlotArray);
		return 0;
	}

	/* return the smaller value between CONFIG_APCI3600_MAX_BOARD_NBR and number of board - Note: apci3600_count is assumed to to be always consistent with the system PCI devices list */
	retval = (atomic_read (&apci3600_count) < CONFIG_APCI3600_MAX_BOARD_NBR ? atomic_read (&apci3600_count) : CONFIG_APCI3600_MAX_BOARD_NBR);
	kfree (b_SlotArray);
	return retval;
}


//------------------------------------------------------------------------------
/** Returns the informations of base address, IRQ to the user.
 * @deprecated: use-space doesn't need it and kernel-space has better means to get it.
 *  */
int do_CMD_APCI3600_GetHardwareInformation (struct pci_dev *pdev,
										unsigned int cmd, unsigned long arg)
{

	str_BaseInformations s_BaseInformations;
	s_BaseInformations.ui_BaseAddress[0] = GET_BAR0 (pdev);
	s_BaseInformations.ui_BaseAddress[1] = GET_BAR1 (pdev);
	s_BaseInformations.ui_BaseAddress[2] = GET_BAR2 (pdev);
	s_BaseInformations.ui_BaseAddress[3] = GET_BAR3 (pdev);
	s_BaseInformations.ui_BaseAddress[4] = 0;
	s_BaseInformations.b_Interrupt = pdev->irq;
	s_BaseInformations.b_SlotNumber = PCI_SLOT (pdev->devfn);
	
	IOCTL_MARK();	
	
	if (copy_to_user ((str_BaseInformations __user *) arg, &s_BaseInformations, sizeof (s_BaseInformations)))
		return -EFAULT;
		
	return 0;
}


//------------------------------------------------------------------------------
int do_CMD_APCI3600_SetBoardInformation (struct pci_dev *pdev, unsigned int cmd,
									 unsigned long arg)
{
	IOCTL_MARK();
	return 0;
}

//BEGIN 24/04/06  BF151
// typedef struct {
// 	uint8_t b_MasterSlaveMode;
// 	uint8_t b_ClockGen1_Divisor;
// 	uint8_t b_ClockGen2_Divisor;
// 	uint8_t b_SamplingClock1_Selection;
// 	uint8_t b_SamplingClock2_Selection;
// 	uint8_t b_SlaveSamplingClock1_Selection;
// 	uint8_t b_SlaveSamplingClock2_Selection;
// 	uint8_t b_Chronometer_MasterClock_Divisor;
// } str_InitBoardArg;
//END 24/04/06  BF151

//BEGIN 7/11/05 CC
//------------------------------------------------------------------------------
static int do_CMD_APCI3600_InitBoard (struct pci_dev *pdev, unsigned int cmd,
						   unsigned long arg)
{
	
	str_InitBoardArg_t p;
	int ret;
	
	IOCTL_MARK();
	if ( copy_from_user  (&p, (const void __user *)arg, sizeof(str_InitBoardArg_t)) )
	{
		return -EFAULT;
	}

	IOCTL_DPRINTK ("str_InitBoardArg.b_ClockGen1_Divisor=%d\n",p.b_ClockGen1_Divisor);
	IOCTL_DPRINTK ("str_InitBoardArg.b_ClockGen2_Divisor=%d\n",p.b_ClockGen2_Divisor);
	IOCTL_DPRINTK ("str_InitBoardArg.b_SamplingClock1_Selection=%d\n",p.b_SamplingClock1_Selection);
	IOCTL_DPRINTK ("str_InitBoardArg.b_SamplingClock2_Selection=%d\n",p.b_SamplingClock2_Selection);
	IOCTL_DPRINTK ("str_InitBoardArg.b_SlaveSamplingClock1_Selection=%d\n",p.b_SlaveSamplingClock1_Selection);
	IOCTL_DPRINTK ("str_InitBoardArg.b_SlaveSamplingClock2_Selection=%d\n",p.b_SlaveSamplingClock2_Selection);
	IOCTL_DPRINTK ("str_InitBoardArg.b_Chronometer_MasterClock_Divisor=%d\n",p.b_Chronometer_MasterClock_Divisor);


	/*ret =i_PCI3600_InitBoard (pdev,	p.b_MasterSlaveMode,
							p.b_ClockGen1_Divisor,
							p.b_ClockGen2_Divisor,
							p.b_SamplingClock1_Selection,
							p.b_SamplingClock2_Selection,
 							p.b_SlaveSamplingClock1_Selection,
							p.b_SlaveSamplingClock2_Selection,
							p.b_Chronometer_MasterClock_Divisor); */
	
	{
		unsigned long irqstate;
		APCI3600_LOCK(pdev,&irqstate);
		IOCTL_RETURN(	ret, 	
						i_PCI3600_InitBoard,
						pdev,
						p.b_MasterSlaveMode,
						p.b_ClockGen1_Divisor,
						p.b_ClockGen2_Divisor,
						p.b_SamplingClock1_Selection,
						p.b_SamplingClock2_Selection,
	 					p.b_SlaveSamplingClock1_Selection,
						p.b_SlaveSamplingClock2_Selection,
						p.b_Chronometer_MasterClock_Divisor);
		APCI3600_UNLOCK(pdev,irqstate);
	}
	return KAPI2IOCTL(ret);
}

//------------------------------------------------------------------------------
static int do_CMD_APCI3600_DumpDMAController(struct pci_dev *pdev, unsigned int cmd, unsigned long arg)
 {
 	int i,j;
 	uint32_t dw_ReadValue;
 	
 	IOCTL_MARK();
 	
 	for (i=0;i<212;i+=12)
 	{
 		printk("%d:\t",i);
 		for (j=0;j<12;j++)
 		{
 			INPDW_MEM (GET_MEM_BAR3(pdev) + PCI3600_ON_BOARD_LOCAL_DMA_CONTROLLER_OFFSET,i+j,&dw_ReadValue);
 			printk("%08d ",(uint32_t)dw_ReadValue);
 		}
 		printk("\n");
 	}
 	return 0;
}


//------------------------------------------------------------------------------
static int do_CMD_APCI3600_GetDMATransferStatus(struct pci_dev *pdev, unsigned int cmd, unsigned long arg)
{
	uint8_t status=1;
	int ret;

	IOCTL_MARK();

	IOCTL_RETURN( 	ret,
					i_PCI3600_Primary_PCI9054GetDMATransferStatus,
					GET_MEM_BAR0(pdev), PCI3600_DMA_CHANNEL_0, &status);
	
	if (copy_to_user ((uint8_t __user *) arg, &status, sizeof (uint8_t)))
	{
		return -EFAULT;
	}

	return KAPI2IOCTL(ret);
}
//------------------------------------------------------------------------------
static int do_CMD_APCI3600_QueryAnalogInputCyclicAcquisitionBufferSize(struct pci_dev *pdev,
		unsigned int cmd,
		unsigned long arg)
{
	uint32_t dw_BufferSize=0;
	int ret;
	{
		unsigned long irqstate;
		APCI3600_LOCK(pdev,&irqstate);
		IOCTL_RETURN(	ret,
					i_PCI3600_QueryAnalogInputCyclicAcquisitionBufferSize,
					pdev, &dw_BufferSize);
		APCI3600_UNLOCK(pdev,irqstate);
	}
	if (copy_to_user ((uint32_t __user *)arg, &dw_BufferSize, sizeof (uint32_t)))
		return -EFAULT;

	return KAPI2IOCTL(ret);
}

//------------------------------------------------------------------------------
static int do_CMD_APCI3600_GetBoardInitialisation(struct pci_dev *pdev, unsigned int cmd, unsigned long arg)
{
	str_GetBoardInitialisation_t param;
	int ret;

	IOCTL_MARK();

	{
		unsigned long irqstate;
		APCI3600_LOCK(pdev,&irqstate);
		IOCTL_RETURN( 	ret,
					i_PCI3600_GetBoardInitialisation,
					pdev,
					&param.b_MasterSlaveMode, 
					&param.b_ClockGen1_Divisor, 
					&param.b_ClockGen2_Divisor, 
					&param.b_SamplingClock1_Selection, 
					&param.b_SamplingClock2_Selection, 
					&param.b_SlaveSamplingClock1_Selection,
					&param.b_SlaveSamplingClock2_Selection,
					&param.b_Chronometer_MasterClock_Divisor);
		APCI3600_UNLOCK(pdev,irqstate);
	}
	if (copy_to_user ((str_GetBoardInitialisation_t __user *)arg, &param, sizeof (str_GetBoardInitialisation_t)))
		return -EFAULT;
		
	return KAPI2IOCTL(ret);
}
//------------------------------------------------------------------------------
static int do_CMD_APCI3600_InitDigitalInputExternalTrigger(struct pci_dev *pdev, unsigned int cmd, unsigned long arg)
{
	int ret;
	InitDigitalInputExternalTrigger_t param;

	IOCTL_MARK();

	if (copy_from_user  (&param, (const void __user *)arg, sizeof(InitDigitalInputExternalTrigger_t)))
		return -EFAULT;	

	IOCTL_DPRINTK ("InitDigitalInputExternalTrigger_t=%d\n",param.b_TriggerLevel);	
	IOCTL_DPRINTK ("InitDigitalInputExternalTrigger_t=%d\n",param.w_TriggerCountValue);

	{
		unsigned long irqstate;
		APCI3600_LOCK(pdev,&irqstate);
		IOCTL_RETURN( 	ret,
					i_PCI3600_InitDigitalInputExternalTrigger,
					pdev, param.b_TriggerLevel, param.w_TriggerCountValue );
		APCI3600_UNLOCK(pdev,irqstate);
	}
	return KAPI2IOCTL(ret);				
}
//------------------------------------------------------------------------------
static int do_CMD_APCI3600_ResetAcquisitionBuffer (struct pci_dev *pdev, unsigned int cmd, unsigned long arg)
{
	
	int ret;	
	IOCTL_MARK();

	{
		unsigned long irqstate;
		APCI3600_LOCK(pdev,&irqstate);
		IOCTL_RETURN( ret, i_ADDIUTILS_ResetDMABuffer,pdev);
		APCI3600_UNLOCK(pdev,irqstate);
	}
	return KAPI2IOCTL(ret);
}

static int do_CMD_APCI3600_QueryAnalogInputCyclicAcquisitionNumberOfData(struct pci_dev *pdev, unsigned int cmd, unsigned long arg)
{
	unsigned long ret = 0;
	IOCTL_MARK();

	{
		unsigned long irqstate;
		APCI3600_LOCK(pdev,&irqstate);
		ret = i_PCI3600_Intern_GetNumberOfData(pdev); 
		APCI3600_UNLOCK(pdev,irqstate);
	}
	
	if (copy_to_user ((unsigned long __user *)arg, &ret, sizeof (unsigned long)))
		return -EFAULT;
	
	return 0;
}


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

static int do_CMD_APCI3600_Read8DigitalInputs(struct pci_dev *pdev, unsigned int cmd, unsigned long arg)
{
	uint8_t channelValue = 0;
	int ret;

	IOCTL_MARK();
	IOCTL_RETURN(ret, i_PCI3600_Read8DigitalInputs, pdev, &channelValue);
	if (copy_to_user((uint8_t __user *)arg, &channelValue, sizeof(uint8_t)))
		return -EFAULT;

	return KAPI2IOCTL(ret);
}


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

static int do_CMD_APCI3600_EnableDisableDigitalOutputMemory(struct pci_dev *pdev, unsigned int cmd, unsigned long arg)
{
	int ret;	
	IOCTL_MARK();

	{
		unsigned long irqstate;
		APCI3600_LOCK(pdev, &irqstate);
		IOCTL_RETURN(ret, i_PCI3600_EnableDisableDigitalOutputMemory, pdev, (uint8_t)arg);
		APCI3600_UNLOCK(pdev, irqstate);
	}
	return KAPI2IOCTL(ret);
}

static int do_CMD_APCI3600_Set1DigitalOutputOn(struct pci_dev *pdev, unsigned int cmd, unsigned long arg)
{
	int ret;	
	IOCTL_MARK();

	{
		unsigned long irqstate;
		APCI3600_LOCK(pdev, &irqstate);
		IOCTL_RETURN(ret, i_PCI3600_Set1DigitalOutputOn, pdev, (uint8_t)arg);
		APCI3600_UNLOCK(pdev, irqstate);
	}
	return KAPI2IOCTL(ret);
}

static int do_CMD_APCI3600_Set1DigitalOutputOff(struct pci_dev *pdev, unsigned int cmd, unsigned long arg)
{
	int ret;	
	IOCTL_MARK();

	{
		unsigned long irqstate;
		APCI3600_LOCK(pdev, &irqstate);
		IOCTL_RETURN(ret, i_PCI3600_Set1DigitalOutputOff, pdev, (uint8_t)arg);
		APCI3600_UNLOCK(pdev, irqstate);
	}
	return KAPI2IOCTL(ret);
}

static int do_CMD_APCI3600_Set8DigitalOutputsOn(struct pci_dev *pdev, unsigned int cmd, unsigned long arg)
{
	int ret;	
	IOCTL_MARK();

	{
		unsigned long irqstate;
		APCI3600_LOCK(pdev, &irqstate);
		IOCTL_RETURN(ret, i_PCI3600_Set8DigitalOutputsOn, pdev, (uint8_t)arg);
		APCI3600_UNLOCK(pdev, irqstate);
	}
	return KAPI2IOCTL(ret);
}

static int do_CMD_APCI3600_Set8DigitalOutputsOff(struct pci_dev *pdev, unsigned int cmd, unsigned long arg)
{
	int ret;	
	IOCTL_MARK();

	{
		unsigned long irqstate;
		APCI3600_LOCK(pdev, &irqstate);
		IOCTL_RETURN(ret, i_PCI3600_Set8DigitalOutputsOff, pdev, (uint8_t)arg);
		APCI3600_UNLOCK(pdev, irqstate);
	}
	return KAPI2IOCTL(ret);
}


/* BASE */
static void apci3600_init_vtable_base (void)
{
	apci3600_dummyze_vtable (apci3600_vtable_base,  apci3600_do_dummy); 
	IOCTL_DPRINTK("%s:\tNumber of vtable elements: %d sizeof(vtable_t) %d\n", __FUNCTION__,VTABLE_ELEMENT_NB(apci3600_vtable_base), sizeof(apci3600_vtable_base));
	
	__APCI_3600_DECLARE_IOCTL_HANDLER (apci3600_vtable_base, CMD_APCI3600_CheckAndGetPCISlotNumber, do_CMD_APCI3600_CheckAndGetPCISlotNumber);
	__APCI_3600_DECLARE_IOCTL_HANDLER (apci3600_vtable_base, CMD_APCI3600_GetHardwareInformation, do_CMD_APCI3600_GetHardwareInformation);
	__APCI_3600_DECLARE_IOCTL_HANDLER (apci3600_vtable_base, CMD_APCI3600_SetBoardInformation, do_CMD_APCI3600_SetBoardInformation);
	__APCI_3600_DECLARE_IOCTL_HANDLER (apci3600_vtable_base, CMD_APCI3600_InitBoard, do_CMD_APCI3600_InitBoard);
	__APCI_3600_DECLARE_IOCTL_HANDLER (apci3600_vtable_base, CMD_APCI3600_QueryAnalogInputCyclicAcquisitionBufferSize, do_CMD_APCI3600_QueryAnalogInputCyclicAcquisitionBufferSize);
	__APCI_3600_DECLARE_IOCTL_HANDLER (apci3600_vtable_base, CMD_APCI3600_GetDMATransferStatus, do_CMD_APCI3600_GetDMATransferStatus);
	__APCI_3600_DECLARE_IOCTL_HANDLER (apci3600_vtable_base, CMD_APCI3600_DumpDMAController, do_CMD_APCI3600_DumpDMAController);
	__APCI_3600_DECLARE_IOCTL_HANDLER (apci3600_vtable_base, CMD_APCI3600_GetBoardInitialisation, do_CMD_APCI3600_GetBoardInitialisation);
	__APCI_3600_DECLARE_IOCTL_HANDLER (apci3600_vtable_base, CMD_APCI3600_EventEmpty, do_CMD_APCI3600_EventEmpty);
	__APCI_3600_DECLARE_IOCTL_HANDLER (apci3600_vtable_base, CMD_APCI3600_EventWait, do_CMD_APCI3600_EventWait);
	__APCI_3600_DECLARE_IOCTL_HANDLER (apci3600_vtable_base, CMD_APCI3600_ResetAcquisitionBuffer, do_CMD_APCI3600_ResetAcquisitionBuffer);
	__APCI_3600_DECLARE_IOCTL_HANDLER (apci3600_vtable_base, CMD_APCI3600_QueryAnalogInputCyclicAcquisitionNumberOfData, do_CMD_APCI3600_QueryAnalogInputCyclicAcquisitionNumberOfData);

	/* Is an analog input function but has to be called by using the main device node (is not module dependent) */
	__APCI_3600_DECLARE_IOCTL_HANDLER (apci3600_vtable_base, CMD_APCI3600_StartMoreAnalogInputModuleCyclicAcquisition, do_CMD_APCI3600_StartMoreAnalogInputModuleCyclicAcquisition);
}
static void apci3600_init_vtable_digital_input (void)
{
	apci3600_dummyze_vtable (apci3600_vtable_digital_input, apci3600_do_dummy); 
	IOCTL_DPRINTK("%s:\tNumber of vtable elements: %d sizeof(vtable_t) %d\n", __FUNCTION__,VTABLE_ELEMENT_NB(apci3600_vtable_digital_input), sizeof(apci3600_vtable_digital_input));

	__APCI_3600_DECLARE_IOCTL_HANDLER (apci3600_vtable_digital_input, CMD_APCI3600_Read8DigitalInputs, do_CMD_APCI3600_Read8DigitalInputs);
}

static void apci3600_init_vtable_digital_output (void)
{
	apci3600_dummyze_vtable (apci3600_vtable_digital_output, apci3600_do_dummy); 
	IOCTL_DPRINTK("%s:\tNumber of vtable elements: %d sizeof(vtable_t) %d\n", __FUNCTION__,VTABLE_ELEMENT_NB(apci3600_vtable_digital_output), sizeof(apci3600_vtable_digital_output));

	__APCI_3600_DECLARE_IOCTL_HANDLER (apci3600_vtable_digital_output, CMD_APCI3600_EnableDisableDigitalOutputMemory, do_CMD_APCI3600_EnableDisableDigitalOutputMemory);
	__APCI_3600_DECLARE_IOCTL_HANDLER (apci3600_vtable_digital_output, CMD_APCI3600_Set1DigitalOutputOn, do_CMD_APCI3600_Set1DigitalOutputOn);
	__APCI_3600_DECLARE_IOCTL_HANDLER (apci3600_vtable_digital_output, CMD_APCI3600_Set1DigitalOutputOff, do_CMD_APCI3600_Set1DigitalOutputOff);
	__APCI_3600_DECLARE_IOCTL_HANDLER (apci3600_vtable_digital_output, CMD_APCI3600_Set8DigitalOutputsOn, do_CMD_APCI3600_Set8DigitalOutputsOn);
	__APCI_3600_DECLARE_IOCTL_HANDLER (apci3600_vtable_digital_output, CMD_APCI3600_Set8DigitalOutputsOff, do_CMD_APCI3600_Set8DigitalOutputsOff);
}
static void apci3600_init_vtable_analog_input (void)
{
	apci3600_dummyze_vtable (apci3600_vtable_analog_input, apci3600_do_dummy_with_id); 
	IOCTL_DPRINTK("%s:\tNumber of vtable elements: %d sizeof(vtable_t) %d\n", __FUNCTION__,VTABLE_ELEMENT_NB(apci3600_vtable_analog_input), sizeof(apci3600_vtable_analog_input));

	__APCI_3600_DECLARE_IOCTL_HANDLER (apci3600_vtable_analog_input, CMD_APCI3600_InitAnalogInputModule, do_CMD_APCI3600_InitAnalogInputModule);
	__APCI_3600_DECLARE_IOCTL_HANDLER (apci3600_vtable_analog_input, CMD_APCI3600_InitAnalogInputModuleCyclicAcquisition, do_CMD_APCI3600_InitAnalogInputModuleCyclicAcquisition);
	__APCI_3600_DECLARE_IOCTL_HANDLER (apci3600_vtable_analog_input, CMD_APCI3600_AnalogInput_EnableDisableMasterTrigger, do_CMD_APCI3600_AnalogInput_EnableDisableMasterTrigger);
	__APCI_3600_DECLARE_IOCTL_HANDLER (apci3600_vtable_analog_input, CMD_APCI3600_StartAnalogInputModuleCyclicAcquisition, do_CMD_APCI3600_StartAnalogInputModuleCyclicAcquisition );
	__APCI_3600_DECLARE_IOCTL_HANDLER (apci3600_vtable_analog_input, CMD_APCI3600_GetAnalogInputModuleCyclicAcquisitionStatus, do_CMD_APCI3600_GetAnalogInputModuleCyclicAcquisitionStatus ); 
	__APCI_3600_DECLARE_IOCTL_HANDLER (apci3600_vtable_analog_input, CMD_APCI3600_ReleaseAnalogInputModuleCyclicAcquisition, do_CMD_APCI3600_ReleaseAnalogInputModuleCyclicAcquisition );
	__APCI_3600_DECLARE_IOCTL_HANDLER (apci3600_vtable_analog_input, CMD_APCI3600_ReleaseAnalogInputModule, do_CMD_APCI3600_ReleaseAnalogInputModule );
	__APCI_3600_DECLARE_IOCTL_HANDLER (apci3600_vtable_analog_input, CMD_APCI3600_GetAnalogInputModuleCalibrationStatus, do_CMD_APCI3600_GetAnalogInputModuleCalibrationStatus );
	__APCI_3600_DECLARE_IOCTL_HANDLER (apci3600_vtable_analog_input, CMD_APCI3600_AnalogInput_EnableDisableDigitalInputExternalTrigger, do_CMD_APCI3600_AnalogInput_EnableDisableDigitalInputExternalTrigger);	
	__APCI_3600_DECLARE_IOCTL_HANDLER (apci3600_vtable_analog_input, CMD_APCI3600_StopAnalogInputModuleCyclicAcquisition, do_CMD_APCI3600_StopAnalogInputModuleCyclicAcquisition);	
}
static void apci3600_init_vtable_analog_output (void)
{
	apci3600_dummyze_vtable (apci3600_vtable_analog_output, apci3600_do_dummy_with_id); 
	IOCTL_DPRINTK("%s:\tNumber of vtable elements: %d sizeof(vtable_t) %d\n", __FUNCTION__,VTABLE_ELEMENT_NB(apci3600_vtable_analog_output), sizeof(apci3600_vtable_analog_output)); 
}
static void apci3600_init_vtable_chrono (void)
{
	apci3600_dummyze_vtable (apci3600_vtable_chrono, apci3600_do_dummy_with_id); 
	IOCTL_DPRINTK("%s:\tNumber of vtable elements: %d sizeof(vtable_t) %d\n", __FUNCTION__,VTABLE_ELEMENT_NB(apci3600_vtable_chrono), sizeof(apci3600_vtable_chrono)); 
}
static void apci3600_init_vtable_external_trigger (void)
{
	apci3600_dummyze_vtable (apci3600_vtable_external_trigger, apci3600_do_dummy); 
	IOCTL_DPRINTK("%s:\tNumber of vtable elements: %d sizeof(vtable_t) %d\n", __FUNCTION__,VTABLE_ELEMENT_NB(apci3600_vtable_external_trigger), sizeof(apci3600_vtable_external_trigger));
	
	__APCI_3600_DECLARE_IOCTL_HANDLER (apci3600_vtable_external_trigger, CMD_APCI3600_InitDigitalInputExternalTrigger, do_CMD_APCI3600_InitDigitalInputExternalTrigger);

}
//------------------------------------------------------------------------------
void apci3600_init_vtables(void)
{
	apci3600_init_vtable_base();
	apci3600_init_vtable_digital_output();
	apci3600_init_vtable_digital_input();
	apci3600_init_vtable_analog_output();
	apci3600_init_vtable_analog_input();
	apci3600_init_vtable_chrono();
	apci3600_init_vtable_external_trigger();
}
//------------------------------------------------------------------------------
/* IOCTL dispatcher for base board */
int apci3600_do_ioctl_base (struct pci_dev *pdev, unsigned int cmd, unsigned long arg)
{
	/* boundaries check
	 * 
	 * VTABLE_ELEMENT_NB(vtable_t) = __APCI3600_UPPER_IOCTL_CMD +1
	 * maximum index value = __APCI3600_UPPER_IOCTL_CMD = VTABLE_ELEMENT_NB(vtable_t) -1
	 * 
	 * the idea here is to favorize compilation-time 
	 * 
	 * */
	 
	IOCTL_MARK();	 
	 
	if (_IOC_NR (cmd) > (VTABLE_ELEMENT_NB (apci3600_vtable_base) - 1))
		return apci3600_do_dummy (pdev, cmd, arg);

	/* call actual ioctl handler - should be safe now */
	return (apci3600_vtable_base[_IOC_NR (cmd)]) (pdev, cmd, arg);
}

//------------------------------------------------------------------------------
/* IOCTL dispatcher for analog input */
int apci3600_do_ioctl_analog_input (struct pci_dev *pdev, unsigned int cmd, unsigned long arg, uint8_t module)
{
	/* boundaries check
	 * 
	 * VTABLE_ELEMENT_NB(vtable_t) = __APCI3600_UPPER_IOCTL_CMD +1
	 * maximum index value = __APCI3600_UPPER_IOCTL_CMD = VTABLE_ELEMENT_NB(vtable_t) -1
	 * 
	 * the idea here is to favorize compilation-time 
	 * 
	 * */
	 
	IOCTL_MARK();
	 
	if (_IOC_NR (cmd) > (VTABLE_ELEMENT_NB (apci3600_vtable_analog_input) - 1))
		return apci3600_do_dummy_with_id (pdev, cmd, arg, module);

	/* call actual ioctl handler - should be safe now */
	return (apci3600_vtable_analog_input[_IOC_NR (cmd)]) (pdev, cmd, arg, module);
}
//------------------------------------------------------------------------------
/* IOCTL dispatcher for external trigger */
int apci3600_do_ioctl_external_trigger(struct pci_dev* pdev, unsigned int cmd, unsigned long arg)
{
	IOCTL_MARK();	
	
	if (_IOC_NR (cmd) > (VTABLE_ELEMENT_NB (apci3600_vtable_external_trigger) - 1))
		return apci3600_do_dummy (pdev, cmd, arg);
	return (apci3600_vtable_external_trigger[_IOC_NR (cmd)]) (pdev, cmd, arg);
}
//------------------------------------------------------------------------------
/* IOCTL dispatcher for digital inputs */
int apci3600_do_ioctl_digital_input(struct pci_dev* pdev, unsigned int cmd, unsigned long arg)
{
	IOCTL_MARK();	
	
	if (_IOC_NR (cmd) > (VTABLE_ELEMENT_NB (apci3600_vtable_digital_input) - 1))
		return apci3600_do_dummy (pdev, cmd, arg);
	return (apci3600_vtable_digital_input[_IOC_NR (cmd)]) (pdev, cmd, arg);
}
//------------------------------------------------------------------------------
/* IOCTL dispatcher for digital outputs */
int apci3600_do_ioctl_digital_output(struct pci_dev* pdev, unsigned int cmd, unsigned long arg)
{
	IOCTL_MARK();	
	
	if (_IOC_NR (cmd) > (VTABLE_ELEMENT_NB (apci3600_vtable_digital_output) - 1))
		return apci3600_do_dummy (pdev, cmd, arg);
	return (apci3600_vtable_digital_output[_IOC_NR (cmd)]) (pdev, cmd, arg);
}
