/** @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 "msxbox_kdebug.h"
#include "apci3600-private.h"
#include "vtable.h"


MODULE_LICENSE ("GPL");

MODULE_AUTHOR ("ADDI-DATA GmbH <info@addi-data.com>");

MODULE_DESCRIPTION ("APCI-3600 IOCTL driver");

#ifndef __devinit
#define __devinit
#define __devexit
#define __devinitdata
#define __devexit_p
#endif

/* int buffer_size=5; */

/*can be overriden */
/* #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
	MODULE_PARM (buffer_size,"i");
#else
	module_param(buffer_size, int, S_IRUGO);
#endif */

EXPORT_NO_SYMBOLS;

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
#include <linux/sysfs.h>
#include <linux/pci.h>
#endif

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
static struct class * apci3600_class = NULL;

#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,26)
	#define CLASS_DEVICE_CREATE(__minor,__namestr) class_device_create(apci3600_class, NULL, MKDEV(apci3600_majornumber, base_minor + __minor), NULL, 	__namestr, 	__APCI3600_DRIVER_NAME,base_minor + __minor );
#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27)
	#define CLASS_DEVICE_CREATE(__minor,__namestr) 	device_create(apci3600_class, NULL, MKDEV(apci3600_majornumber, base_minor + __minor), __namestr, 	__APCI3600_DRIVER_NAME, base_minor + __minor );
#else
	#define CLASS_DEVICE_CREATE(__minor,__namestr) 	device_create(apci3600_class, NULL, MKDEV(apci3600_majornumber, base_minor + __minor), NULL, 	__namestr, 	__APCI3600_DRIVER_NAME, base_minor + __minor );
#endif

#endif

#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,26)
	#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
		#define CLASS_DEVICE_DESTROY(__base_minor, __i) class_device_destroy (apci3600_class, MKDEV(apci3600_majornumber, __base_minor + __i));
	#endif
#else
	#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
		#define CLASS_DEVICE_DESTROY(__base_minor, __i) device_destroy (apci3600_class, MKDEV(apci3600_majornumber, __base_minor + __i));
	#endif
#endif


#ifdef MAIN_DEBUG
//#define MAIN_DPRINTK(fmt, args...)	{ printk(": %s: " fmt, __FUNCTION__ , ## args); }
#define MAIN_DPRINTK(fmt, args...)	printk("%s: %s:%i: " fmt,__FILE__,__FUNCTION__,__LINE__, ## args)
#define MAIN_MARK					printk( "%s: %s:%i\n",__FILE__, __FUNCTION__ , __LINE__)
#else
#define MAIN_DPRINTK(fmt, args...)
#define MAIN_MARK
#endif

char __APCI3600_DRIVER_NAME[] = "apci3600";

/*Only use for hard debugging*/
// void dump(struct
// 		pci_dev *dev){
// 	int i,j;
// 	//uint32_t dw_ReadValue;
// 	printk(__APCI3600_DRIVER_NAME": dev->resource[0].start=%lX\tdev->resource[3].start=%lX\n",dev->resource[0].start,dev->resource[3].start);
//
//
//
// 	printk(__APCI3600_DRIVER_NAME": BAR0\n");
// 	for (i=0;i<256;i+=32){
// 		printk(__APCI3600_DRIVER_NAME": %04x:\t",i);
// 		for (j=0;j<32;j+=4){
// 			uint32_t dw_ReadValueIJ;
// 			/*Lecture BAR 3 i+j*/
// 			INPDW_MEM (GET_MEM_BAR0(dev),i+j,&dw_ReadValueIJ);
// 			printk("%08x\t",(unsigned int)dw_ReadValueIJ);
// 		}
// 		printk("\n");
// 	}
//
//
// 	printk(__APCI3600_DRIVER_NAME": BAR3\n");
// 	for (i=0;i<296;i+=32){
// 		printk(__APCI3600_DRIVER_NAME": %04x:\t",i);
// 		for (j=0;j<32;j+=4){
// 			uint32_t dw_ReadValueIJ;
// 			/*Lecture BAR 3 i+j*/
// 			INPDW_MEM (GET_MEM_BAR3(dev),i+j,&dw_ReadValueIJ);
// 			printk("%08x\t",(unsigned int)dw_ReadValueIJ);
// 		}
// 		printk("\n");
// 	}
// }



//------------------------------------------------------------------------------
atomic_t apci3600_count = ATOMIC_INIT (0);

unsigned int apci3600_majornumber = 0;
//------------------------------------------------------------------------------
static int __devinit apci3600_probe_one (struct pci_dev *dev, const struct pci_device_id *ent);
static void __devexit apci3600_remove_one (struct pci_dev *dev);
//------------------------------------------------------------------------------
/** The ID table is an array of struct pci_device_id ending with a all-zero entry.
Each entry consists of:

	- vendor, device	Vendor and device ID to match (or PCI_ANY_ID)
	- subvendor,	Subsystem vendor and device ID to match (or PCI_ANY_ID)
	- subdevice class,		Device class to match. The class_mask tells which bits
	- class_mask	of the class are honored during the comparison.
	- driver_data	Data private to the driver.
*/

static struct pci_device_id apci3600_pci_tbl[] =
	{
		{
			APCI3600_BOARD_VENDOR_ID,
			APCI3600_BOARD_DEVICE_ID,
			PCI_ANY_ID,
			PCI_ANY_ID,
			0,
			0,
			0
		},

		{0}, /* terminate list */
	};



MODULE_DEVICE_TABLE (pci, apci3600_pci_tbl);


//----------------------------------------------------------------------------------------------------------------------
static struct pci_driver apci3600_pci_driver = {
	.name		=	__APCI3600_DRIVER_NAME,
	.probe		=	apci3600_probe_one,
	.remove		=	__devexit_p (apci3600_remove_one),
	.id_table	=	apci3600_pci_tbl,
};
//BEGIN 19/04/2006 FB
// Move to fs.c
// //------------------------------------------------------------------------------
// //BEGIN 8/11/2005 CC
// // Adds a Signal listener to the list (USR program who has requested to be notified by
// // this module with signals - SIGIO) - This allows signal communication. See dev_fops
// int
// apci3600_fasync_lookup (int fd, struct file *filp, int mode)
// {
//
// 	return fasync_helper (fd, filp, mode, &fa);
//
// }
////END 8/11/2005 CC
//END 19/04/2006 FB




//------------------------------------------------------------------------------
static struct file_operations apci3600_fops = {
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36)
	.ioctl			= apci3600_fs_ioctl,
#else
	.unlocked_ioctl = apci3600_fs_ioctl,
#endif
	.fasync		=	apci3600_fs_fasync,
	.open		=	apci3600_fs_open,
	.release	=	apci3600_fs_release,
	.read		=	apci3600_fs_read,
};



//------------------------------------------------------------------------------
/** when module is unloaded, stop all board activities (cf interrupt)*/
static void apci3600_stop_board (struct pci_dev *pdev)
{
	MAIN_MARK;
	// FILL THE BLANK
	//BEGIN 26/09/05 CG

	{
		unsigned long irqstate;
		APCI3600_LOCK(pdev,&irqstate);
		{
			i_PCI3600_Intern_CloseHardware (pdev);
		}
		APCI3600_UNLOCK(pdev,irqstate);
	}
	//END 26/09/05 CG
}


//BEGIN 15/09/2005 CG

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

/** Hook function called at init module time.
 *
 * This function is used to:<br>
 * o initialize the wait state of the APCI-3600 board.<br>
 * o Get the board information (Size of the RAM).
 *
 * @retval	0	No error.
 * @retval  -1	Can't allocate memory.
 */
int __needlock apci3600_initialize (struct pci_dev *pdev)
{
	uint32_t dw_InportValue = 0;
	uint32_t dw_AlignShift = 0;

	//uint32_t dw_ReadRegister = 0;
	MAIN_MARK;

	/********************************/
	/* Get the version of the board */
	/********************************/
	INPDW (GET_BAR2 (pdev) + PCI3600_POWER_MONITOR_STATUS_OFFSET,

		   &dw_InportValue);


	APCI3600_PRIVDATA (pdev)->

	s_PCI3600_BoardInformation.
	s_HardwareInformations.
	b_BoardVersion =
		(uint8_t) ((dw_InportValue >> 3) & 1);

	APCI3600_PRIVDATA (pdev)->
	s_PCI3600_BoardInformation.
	b_BoardRevision = (uint8_t)((dw_InportValue >> 4) & 1);

	MAIN_DPRINTK ("APCI-3600 : BoardVersion : %u\n", dw_InportValue);



	/**************************/
	/* Test the board version */
	/**************************/
	if (APCI3600_PRIVDATA (pdev)->
			s_PCI3600_BoardInformation.
			s_HardwareInformations.
			b_BoardVersion == 1)
	{

		APCI3600_PRIVDATA (pdev)->
		s_PCI3600_BoardInformation.
		b_AnalogInModuleNumber = 4;


		APCI3600_PRIVDATA (pdev)->
		s_PCI3600_BoardInformation.
		b_AnalogOutChannelNumber = 2;


		APCI3600_PRIVDATA (pdev)->
		s_PCI3600_BoardInformation.
		b_ChronometerNumber = 4;


	} else
	{

		APCI3600_PRIVDATA (pdev)->
		s_PCI3600_BoardInformation.
		b_AnalogInModuleNumber = 4;


		APCI3600_PRIVDATA (pdev)->
		s_PCI3600_BoardInformation.
		b_AnalogOutChannelNumber = 0;


		APCI3600_PRIVDATA (pdev)->
		s_PCI3600_BoardInformation.
		b_ChronometerNumber = 0;


	}


	/***********************************/
	/* Get the SDRAM Size of the board */
	/***********************************/
	INPDW (GET_BAR2 (pdev) + PCI3600_POWER_MONITOR_STATUS_OFFSET + 4,

		   &dw_InportValue);


	MAIN_DPRINTK ("APCI-3600 : RAM Size Info : 0x%x \n", dw_InportValue);


	/***************************/
	/* Test if RAM is detected */
	/***************************/
	if (dw_InportValue != 0xFFFF00FE)
	{

		/*********************************/
		/* Test if RAM detection problem */
		/*********************************/
		if ((dw_InportValue & 0xFF) > 0x1D) {

			APCI3600_PRIVDATA (pdev)->
			s_PCI3600_BoardInformation.
			s_HardwareInformations.
			dw_OnBoardRAMSize = 0;


		} else {

			APCI3600_PRIVDATA (pdev)->
			s_PCI3600_BoardInformation.
			s_HardwareInformations.
			dw_OnBoardRAMSize = (uint32_t) (1 << (dw_InportValue & 0xFF));


		}

	} else
	{

		APCI3600_PRIVDATA (pdev)->
		s_PCI3600_BoardInformation.
		s_HardwareInformations.
		dw_OnBoardRAMSize = 0;


	}

	MAIN_DPRINTK ("APCI-3600 : After RAM Info \n");


	/*********************************************************/
	/* Update the on board Free Buffer structure information */
	/*********************************************************/
	APCI3600_PRIVDATA (pdev)->
	s_PCI3600_BoardInformation.
	b_NbrOfFreeBuffer = 1;


	APCI3600_PRIVDATA (pdev)->
	s_PCI3600_BoardInformation.
	ps_OnBoardRAMFreeBuffer[0].
	dw_Address = PCI3600_ON_BOARD_RAM_BASE_ADDRESS;


	APCI3600_PRIVDATA (pdev)->
	s_PCI3600_BoardInformation.
	ps_OnBoardRAMFreeBuffer[0].
	dw_BufferSize = APCI3600_PRIVDATA (pdev)->
					s_PCI3600_BoardInformation.
					s_HardwareInformations.
					dw_OnBoardRAMSize;


	/********************************************************************/
	/* Reserve the memory for the descriptor list for the DMA transfer  */
	/********************************************************************/

	if (i_ADDIUTILS_AllocDescriptorsMemory (pdev, PCI3600_DESCRIPTOR_MEM_SIZE) != 0)
	{
		int i_Cpt = 0;
		for (i_Cpt = 0;
				i_Cpt <
				(PCI3600_NBR_OF_ANALOG_OUTPUT_CHANNEL +
				 PCI3600_NBR_OF_ANALOG_INPUT_MODULE +
				 PCI3600_NBR_OF_CHRONOMETER); i_Cpt++)
		{

			/* Set the descriptor list address by using the descriptor memory address */
			APCI3600_PRIVDATA (pdev)->
			s_PCI3600_BoardInformation.
			ps_DescriptorsInfos[i_Cpt].
			ps_PCI9054DMADescriptor = APCI3600_PRIVDATA (pdev)->s_PCI3600_BoardInformation.descriptorMemory.pv_DescriptorMemoryAddress +
									  (i_Cpt * (((sizeof (str_PCI9054DMADescriptor) * PCI3600_MAX_NUMBER_OF_DESCRIPTOR) + 0x10)));

			/*physical address*/
			APCI3600_PRIVDATA (pdev)->
			s_PCI3600_BoardInformation.
			ps_DescriptorsInfos[i_Cpt].
			dw_DescriptorListPhysicalAddress = APCI3600_PRIVDATA (pdev)->s_PCI3600_BoardInformation.
												descriptorMemory.dw_DescriptorMemoryPhysicalAddress +
											(i_Cpt *
											 (((sizeof (str_PCI9054DMADescriptor) *
												PCI3600_MAX_NUMBER_OF_DESCRIPTOR) + 0x10)));

			/*************************************************/
			/* Initialise the number of descriptor available */
			/*************************************************/
			APCI3600_PRIVDATA (pdev)->
			s_PCI3600_BoardInformation.
			ps_DescriptorsInfos[i_Cpt].
			dw_NbrOfDescriptorAllocated = PCI3600_MAX_NUMBER_OF_DESCRIPTOR;



			APCI3600_PRIVDATA (pdev)->
			s_PCI3600_BoardInformation.
			ps_DescriptorsInfos[i_Cpt].
			pv_DescriptorAddressForFree = NULL;


			/*to ensure that the descriptors are aligned, we have reserved sizeof(descriptor)+0x10 at base address.
			Then we are looking for the aligned address between base address and base address+0x10*/
			dw_AlignShift =
				0x10 -
				(APCI3600_PRIVDATA (pdev)->
				 s_PCI3600_BoardInformation.
				 ps_DescriptorsInfos[i_Cpt].
				 dw_DescriptorListPhysicalAddress & 0xF);


			APCI3600_PRIVDATA (pdev)->
			s_PCI3600_BoardInformation.
			ps_DescriptorsInfos[i_Cpt].
			ps_PCI9054DMADescriptor =
				(pstr_PCI9054DMADescriptor)(APCI3600_PRIVDATA (pdev)->
											 s_PCI3600_BoardInformation.
											 ps_DescriptorsInfos[i_Cpt].
											 ps_PCI9054DMADescriptor +
											 dw_AlignShift);

			APCI3600_PRIVDATA (pdev)->
			s_PCI3600_BoardInformation.
			ps_DescriptorsInfos[i_Cpt].
			dw_DescriptorListPhysicalAddress =
				APCI3600_PRIVDATA (pdev)->
				s_PCI3600_BoardInformation.
				ps_DescriptorsInfos[i_Cpt].
				dw_DescriptorListPhysicalAddress + dw_AlignShift;

			//BEGIN 7/6/06 BF151
			MAIN_DPRINTK ("Descriptor %d\n\tvirt_addr: %08lX\n",i_Cpt,(unsigned long)APCI3600_PRIVDATA (pdev)->s_PCI3600_BoardInformation.ps_DescriptorsInfos[i_Cpt].ps_PCI9054DMADescriptor);
			MAIN_DPRINTK ("\tAPCI3600_PRIVDATA (pdev)->s_PCI3600_BoardInformation.ps_DescriptorsInfos[%d].dw_DescriptorListPhysicalAddress: %X\n",i_Cpt,
			APCI3600_PRIVDATA (pdev)->
			s_PCI3600_BoardInformation.
			ps_DescriptorsInfos[i_Cpt].
			dw_DescriptorListPhysicalAddress);
			//END 7/6/06 BF151
		}
	}
	else
	{
		printk (KERN_WARNING "APCI-3600: Can't allocate DMA descriptor memory.\n");
		return (-1);
	}


	/*****************************************************************/
	/* Allocation of the contiguous memory (using Bigphysarea) */
	/*****************************************************************/
	i_ADDIUTILS_GetDMABufferAllocationInformation (pdev,&(APCI3600_PRIVDATA (pdev)->s_PCI3600_BoardInformation.s_BoardInfos.s_PCAllocatedMemoryBuffer));

	if	(
			(
				APCI3600_PRIVDATA (pdev)->
				s_PCI3600_BoardInformation.
				s_BoardInfos.
				s_PCAllocatedMemoryBuffer.
				ul_BufferNbr != 0
			)
			&&
			(
				APCI3600_PRIVDATA (pdev)->
				s_PCI3600_BoardInformation.
				s_BoardInfos.
				s_PCAllocatedMemoryBuffer.
				ul_BufferSize >
				PCI3600_DESCRIPTOR_MEM_SIZE
			)
		)
	{
		MAIN_DPRINTK ("APCI-3600 : Before FOR for the descriptor \n");
		MAIN_DPRINTK ("APCI-3600 : Number of Descriptor: %d\n",PCI3600_NBR_OF_ANALOG_OUTPUT_CHANNEL + PCI3600_NBR_OF_ANALOG_INPUT_MODULE + PCI3600_NBR_OF_CHRONOMETER);
		MAIN_DPRINTK ("APCI-3600 : After FOR for the descriptor \n");
	}
	else
	{
		MAIN_DPRINTK ("APCI3600_PRIVDATA (pdev)->s_PCI3600_BoardInformation.s_BoardInfos.s_PCAllocatedMemoryBuffer.ul_BufferNbr=%d\n",
				APCI3600_PRIVDATA (pdev)->
				s_PCI3600_BoardInformation.
				s_BoardInfos.
				s_PCAllocatedMemoryBuffer.
				ul_BufferNbr);

		MAIN_DPRINTK ("APCI3600_PRIVDATA (pdev)->s_PCI3600_BoardInformation.s_BoardInfos.s_PCAllocatedMemoryBuffer.ul_BufferSize=%d\n",
				APCI3600_PRIVDATA (pdev)->
				s_PCI3600_BoardInformation.
				s_BoardInfos.
				s_PCAllocatedMemoryBuffer.
				ul_BufferSize);

		MAIN_DPRINTK ("PCI3600_DESCRIPTOR_MEM_SIZE=%d\n",PCI3600_DESCRIPTOR_MEM_SIZE);

		printk (KERN_WARNING "APCI-3600: Can't allocate DMA memory.\n");
		return (-1);
	}

	i_ADDIUTILS_ResetDMABuffer(pdev);

	return (0);

}

//END 15/09/2005 CG

static int apci3600_initialize_PCI_Controller(struct pci_dev *pdev)
{
	uint32_t dw_IntCSReg = 0;

	MAIN_MARK;
	/**********************************/
	/* Initialise the PCI Controller */
	/*********************************/

	INPDW_MEM (GET_MEM_BAR0 (pdev), 0x68, &dw_IntCSReg);

	OUTPDW_MEM (GET_MEM_BAR0 (pdev), 0x68,
				dw_IntCSReg | (1 << 18) | (1 << 16));

	return 0;
}
//-------------------------------------------------------------------
static void apci3600_iounmap_bar(struct pci_dev * pdev)
{
	MAIN_MARK;
	iounmap(GET_MEM_BAR0(pdev));
	iounmap(GET_MEM_BAR3(pdev));
}

//-------------------------------------------------------------------
/** event: a new card is detected.
 *
 * Historically - in former ADDI-DATA drivers - data about a board has been stored
 * in a static structure.
 * This led to huge duplication of information since most of these information
 * are already present in the pci_dev struct.
 *
 * Now we manipulate this standard structure provided by the OS and we attach
 * private data using the field driver_data, if necessary.
 *
 *  */
static int __devinit apci3600_probe_one (struct pci_dev *dev, const struct pci_device_id *ent)
{
	struct apci3600_str_BoardInformations *newboard_data = NULL;

	MAIN_MARK;
	if (pci_enable_device(dev)!=0)
	{
		printk(KERN_WARNING "pci_enable_device failed !\n");
		goto ignore_this_device;
	}

	/* enable PCI bus-mastering */
	pci_set_master(dev);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
	if (dma_set_mask (&dev->dev, 0xFFFFFFFF)) {
#else
	if (pci_set_dma_mask (dev, 0xFFFFFFFF)) {
#endif
 		printk(KERN_WARNING "APCI3600: No suitable DMA available.\n");
		goto ignore_this_device;
 	}


	/* 2.4 only : check_region is deprecated in 2.6 */
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)

	/* check if BAR 0 is free */
	if (check_mem_region (dev->resource[0].start, /*256*/pci_resource_len(dev,0))){

		printk (KERN_WARNING "%s: BAR 0 (%lX) is already in use\n",
				__APCI3600_DRIVER_NAME, dev->resource[0].start);
		return -EBUSY;

	}

	/* check if BAR 1 is free */
	if (check_region (dev->resource[1].start, /*256*/pci_resource_len(dev,1))){

		printk (KERN_WARNING "%s: BAR 1 (%lX) is already in use\n",
				__APCI3600_DRIVER_NAME, dev->resource[1].start);
		return -EBUSY;

	}

	/* check if BAR 2 is free */
	if (check_region (dev->resource[2].start, /*56*/pci_resource_len(dev,2))){

		printk (KERN_WARNING "%s: BAR 2 (%lX) is already in use\n",
				__APCI3600_DRIVER_NAME, dev->resource[2].start);
		return -EBUSY;

	}


	/* check if BAR 3 is free */
	if (check_mem_region (dev->resource[3].start, /*296*/pci_resource_len(dev,3))){

		printk (KERN_WARNING "%s: BAR 3 (%lX) is already in use\n",
				__APCI3600_DRIVER_NAME, dev->resource[3].start);
		return -EBUSY;

	}

#endif // 2.4

	/* allocate a new data structure containing board private data */
	MAIN_DPRINTK ("Struct Size : %u\n",sizeof (struct apci3600_str_BoardInformations));

	newboard_data = kmalloc (sizeof (struct apci3600_str_BoardInformations), GFP_ATOMIC);
	MAIN_DPRINTK("kmalloc done\n");

	if (!newboard_data){
		printk (KERN_CRIT "Can't allocate memory for new board %s\n",
				pci_name(dev));
		return -ENOMEM;
	}

	/* link standard data structure to the board's private one */
	pci_set_drvdata(dev, newboard_data);
	MAIN_DPRINTK("pci_set_drvdata done\n");

	apci3600_init_priv_data (newboard_data);
	MAIN_DPRINTK("apci3600_init_priv_data\n");


	/* lock BAR IO ports ressources */
	if ( pci_request_regions (dev, __APCI3600_DRIVER_NAME) )
	{
		printk(KERN_ERR "%s: pci_request_regions failed\n",__APCI3600_DRIVER_NAME);
		kfree(APCI3600_PRIVDATA(dev));
		goto ignore_this_device;
	}


	//BEGIN 14/09/2005 CG
	/* Map the memory Address 0 */
	APCI3600_PRIVDATA (dev)->
	s_PCI3600_BoardInformation.
	s_HardwareInformations.
	dw_MemBaseAddress0 = ioremap (dev->resource[0].start, pci_resource_len(dev,0)/*256*/);
	MAIN_DPRINTK("ioremap1 done\n");


	/* Map the memory Address 3 */
	APCI3600_PRIVDATA (dev)->
	s_PCI3600_BoardInformation.
	s_HardwareInformations.
	dw_MemBaseAddress3 = ioremap (dev->resource[3].start, pci_resource_len(dev,3)/*296*/);
	MAIN_DPRINTK("ioremap2 done\n");

	/* register interrupt */
	if (apci3600_register_interrupt (dev))
	{
		printk(KERN_CRIT "Registering interrupt FAILED !! (apci3600_register_interrupt)");

		apci3600_iounmap_bar(dev);

		/* deallocate BAR IO Ports ressources */
		pci_release_regions (dev);
		//END 26/09/05 CG

		kfree (APCI3600_PRIVDATA (dev));
		//BEGIN 26/09/05 CG
		// pci_release_regions(dev);
		//END 26/09/05 CG
	}
	MAIN_DPRINTK("register_interrupt done\n");

 	/* create /proc entry */
 	/* deactivated */
 	//apci3600_proc_create_device (dev, atomic_read (&apci3600_count) - 1);
	MAIN_DPRINTK("apci3600_proc_create_device done\n");

	{
		unsigned long irqstate;
		APCI3600_LOCK(dev,&irqstate);
		{
			if (apci3600_initialize (dev) != 0)
			{
				APCI3600_UNLOCK(dev, irqstate);
				apci3600_iounmap_bar(dev);
				pci_release_regions (dev);
				kfree (APCI3600_PRIVDATA (dev));
				return -ENOMEM;
			}
		}
		APCI3600_UNLOCK(dev, irqstate);
	}
	MAIN_DPRINTK("apci3600_initialize done\n");

	/* increase the global board count */
	atomic_inc (&apci3600_count);

	{
		unsigned long tmp = atomic_read (&apci3600_count);
		printk (KERN_INFO "%s: board %s managed (base minor number will be %u) \n",
			__APCI3600_DRIVER_NAME, pci_name(dev),
			APCI3600_COMPUTE_BASE_MINOR_NUMBER(tmp));
	}

	/*The handler is installed, now allow interrupt*/
	apci3600_initialize_PCI_Controller(dev);
	MAIN_DPRINTK("apci3600_initialize_PCI_Controller done\n");

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
	while(1)
    {
    #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,26)
		struct class_device *cdev;
	#else
		struct device *cdev;
	#endif

        int base_minor = (atomic_read(&apci3600_count)-1);

		MAIN_MARK;

		/* don't execute if class not exists */
		if (IS_ERR(apci3600_class))
			break;

        MAIN_DPRINTK("major=%i\n",apci3600_majornumber);

        /* create an entry per subsystem minor for base_minor + ( 0 to 10 ) */
        while(1)
        {
	        /* main board */
	        cdev = CLASS_DEVICE_CREATE( APCI3600_DEV_MINOR_BASE, "%s_%d");
	        if (IS_ERR(cdev)) goto on_cdev_error;
			/* Analog Output 0 */
			cdev = CLASS_DEVICE_CREATE( APCI3600_DEV_MINOR_ANALOG_OUTPUT_0, "%s_%d_ao0");
			if (IS_ERR(cdev)) goto on_cdev_error;
			/* Analog Output 1 */
			cdev = CLASS_DEVICE_CREATE( APCI3600_DEV_MINOR_ANALOG_OUTPUT_1, "%s_%d_ao1");
			if (IS_ERR(cdev)) goto on_cdev_error;
			/* Analog Input Module 0 */
			cdev = CLASS_DEVICE_CREATE( APCI3600_DEV_MINOR_ANALOG_INPUT_0, "%s_%d_ai0");
			if (IS_ERR(cdev)) goto on_cdev_error;
			/* Analog Input Module 1 */
			cdev = CLASS_DEVICE_CREATE( APCI3600_DEV_MINOR_ANALOG_INPUT_1, "%s_%d_ai1");
			if (IS_ERR(cdev)) goto on_cdev_error;
			/* Analog Input Module 2 */
			cdev = CLASS_DEVICE_CREATE( APCI3600_DEV_MINOR_ANALOG_INPUT_2, "%s_%d_ai2");
			if (IS_ERR(cdev)) goto on_cdev_error;
			/* Analog Input Module 3 */
			cdev = CLASS_DEVICE_CREATE( APCI3600_DEV_MINOR_ANALOG_INPUT_3, "%s_%d_ai3");
			if (IS_ERR(cdev)) goto on_cdev_error;
			/* Chronometer 0 */
			cdev = CLASS_DEVICE_CREATE( APCI3600_DEV_MINOR_CHRONO_0, "%s_%d_chrono0");
			if (IS_ERR(cdev)) goto on_cdev_error;
			/* Chronometer 1 */
			cdev = CLASS_DEVICE_CREATE( APCI3600_DEV_MINOR_CHRONO_1, "%s_%d_chrono1");
			if (IS_ERR(cdev)) goto on_cdev_error;
			/* Chronometer 2 */
			cdev = CLASS_DEVICE_CREATE( APCI3600_DEV_MINOR_CHRONO_2, "%s_%d_chrono2");
			if (IS_ERR(cdev)) goto on_cdev_error;
			/* Chronometer 3 */
			cdev = CLASS_DEVICE_CREATE( APCI3600_DEV_MINOR_CHRONO_3, "%s_%d_chrono3");
			if (IS_ERR(cdev)) goto on_cdev_error;
			/* Digital inputs */
			cdev = CLASS_DEVICE_CREATE( APCI3600_DEV_MINOR_DIGITAL_INPUT, "%s_%d_di");
			if (IS_ERR(cdev)) goto on_cdev_error;
			/* Digital outputs */
			cdev = CLASS_DEVICE_CREATE( APCI3600_DEV_MINOR_DIGITAL_OUTPUT, "%s_%d_do");
			if (IS_ERR(cdev)) goto on_cdev_error;
			/* external trigger */
			cdev = CLASS_DEVICE_CREATE( APCI3600_DEV_MINOR_EXTERNAL_TRIGGER, "%s_%d_et");
			if (IS_ERR(cdev)) goto on_cdev_error;
			break;
			on_cdev_error:
				printk (KERN_WARNING "%s: class_device_create error\n", __APCI3600_DRIVER_NAME);
				break;
        }

        if (IS_ERR(cdev))
        {
        	printk (KERN_WARNING "%s: class_device_create error\n", __APCI3600_DRIVER_NAME);
        }
        MAIN_DPRINTK("class_device_create done\n");
        break;
    }
#endif

	return 0;


ignore_this_device:
	printk(KERN_WARNING "Device ignored !\n");

 	return 0;
}


//------------------------------------------------------------------------------
/** This function registers the driver with register_chrdev.
 *
 * @todo For kernel 2.6, use udev
 *  */

static int apci3600_register (void)
{

	MAIN_MARK;

	//Registration of driver
	apci3600_majornumber = register_chrdev (0, __APCI3600_DRIVER_NAME, &apci3600_fops);


	if (apci3600_majornumber < 0) {

		printk (KERN_ERR "%s: register_chrdev returned %d ... aborting\n",
				__APCI3600_DRIVER_NAME, apci3600_majornumber);

		return -ENODEV;

	}

	return 0;

}



//------------------------------------------------------------------------------
/** Called when module loads. */
static int __init apci3600_init (void)

// int init_module_kgdb (void)
{

	//BEGIN 18/04/2006 BF
	/*create a real time FIFO of size 30 000*/
// 	int retval = rtf_create (3, 30000);
// 	if (retval)
//
// 		printk (KERN_INFO "%s: FIFO ERROR", __APCI3600_DRIVER_NAME);
//
// 	rtf_reset (3);

	//BEGIN 18/04/2006 BF
	MAIN_MARK;

	apci3600_init_vtables();


	if (apci3600_register ()) {
		return -ENODEV;
	}

	/* registred, now create root /proc entry */
	/* deactivated */
	//apci3600_proc_init ();
	//MAIN_DPRINTK("apci3600_proc_init\n");

	printk (KERN_INFO "%s: loaded\n", __APCI3600_DRIVER_NAME);

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
    apci3600_class = ADDI_CLASS_CREATE(THIS_MODULE, __APCI3600_DRIVER_NAME);
    if (IS_ERR(apci3600_class))
    {
            printk (KERN_WARNING "%s: class_create error\n",__APCI3600_DRIVER_NAME );
    }
    MAIN_DPRINTK("class_create done\n");
    MAIN_DPRINTK("apci3600_class=%p\n",apci3600_class);
#endif

	/* now, subscribe to PCI bus subsystem  */
	/** @note The module will load anyway even if the call to pci_module_init() fails */
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)
	if (pci_module_init (&apci3600_pci_driver) != 0)
#else
	if ( pci_register_driver (&apci3600_pci_driver) != 0)
#endif
	{
		printk (KERN_INFO "%s: no device found\n", __APCI3600_DRIVER_NAME);
		apci3600_pci_driver.name = "";
	}

	MAIN_DPRINTK("pci_register_driver\n");


	return 0;

}



//-------------------------------------------------------------------
/** event: a card is removed (also called when module is unloaded) */
static void __devexit apci3600_remove_one (struct pci_dev *dev)
{
	unsigned long addr;
	//BEGIN 19/04/06 FB
	//printk ("1: Destroy the FIFO\n");
	MAIN_MARK;

	/* /sys management */
	while(1)
    {
            int base_minor = 0;

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
			/* don't execute if class not exists */
			if(IS_ERR(apci3600_class))
				break;
#endif

            base_minor = (atomic_read(&apci3600_count)-1);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
            {
            	int i;
            	/* delete class devices */
            	for(i=0; i < APCI3600_DEV_MINOR_MAX; i++)
            		CLASS_DEVICE_DESTROY(base_minor,i);
            }
#endif
            break;
    }

	//rtf_destroy (3);
	//END 19/04/06 FB

	/* stop board activities */
	MAIN_DPRINTK ("2: stop board activities\n");
	apci3600_stop_board (dev);

	MAIN_DPRINTK ("3: deregister interrupt\n");
	/* deregister interrupt */
	apci3600_deregister_interrupt (dev);

	MAIN_DPRINTK("4: disable device\n");
	/* do OS-dependant thing we don't really want to know of :) */
	pci_disable_device (dev);

	MAIN_DPRINTK("5: free Bigphsarea pages\n");
	//BEGIN 14/09/2005 CG

	addr = (unsigned long)APCI3600_PRIVDATA (dev)->
			s_PCI3600_BoardInformation.
			s_BoardInfos.
			s_PCAllocatedMemoryBuffer.
			BigphysareaHandle;
	/* Free the DMA Buffer */
	if (addr){

#if defined(CONFIG_BIGPHYS_AREA)
		bigphysarea_free_pages (APCI3600_PRIVDATA (dev)->
								s_PCI3600_BoardInformation.
								s_BoardInfos.
								s_PCAllocatedMemoryBuffer.
								BigphysareaHandle );
		MAIN_DPRINTK ("bigphysarea : free pages !!!\n");
#else
#warning TODO: actually free the allocated memory
        //dma_free_coherent(&dev->dev, size, cpu_addr, dma_handle);
		MAIN_DPRINTK ("dma_free_coherent : free pages !!!\n");
#endif
	} else {
#if defined(CONFIG_BIGPHYS_AREA)
		MAIN_DPRINTK ("bigphysarea : don't free pages !!!\n");
#else
		MAIN_DPRINTK ("dma_free_coherent : don't free pages !!!\n");
#endif
	}

	apci3600_iounmap_bar(dev);

	/* deallocate BAR IO Ports ressources */
	pci_release_regions (dev);

	/* free private device data (uncomment) */
	kfree (APCI3600_PRIVDATA (dev));

	/* delete associated /proc entry */
	/* deactivated */
	//apci3600_proc_release_device (dev);


	/* decrement global board count */
	atomic_dec (&apci3600_count);

	printk (KERN_INFO "%s: board %s unloaded \n", __APCI3600_DRIVER_NAME, pci_name(dev));
}


//------------------------------------------------------------------------------
/** This function unregisters the driver with unregister_chrdev.
 *
 * @todo For kernel 2.6, use udev
 *  */

static void apci3600_unregister (void)
{
	MAIN_MARK;
	unregister_chrdev (apci3600_majornumber, __APCI3600_DRIVER_NAME);

}


//-------------------------------------------------------------------
/** Called when module is unloaded. */
static void __exit apci3600_exit (void)
//  void cleanup_module_kgdb (void)
{

	MAIN_MARK;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
	 if ( ! IS_ERR(apci3600_class) )
        class_destroy (apci3600_class);
#endif

	/* unsubscribe to PCI bus subsystem */
	if (apci3600_pci_driver.name[0])
	{
		pci_unregister_driver (&apci3600_pci_driver);
	}

	/* unsubscribe to /dev VFS */
	apci3600_unregister ();

	/* delete /proc root */
	/* deactivated */
	//apci3600_proc_release ();
}


//------------------------------------------------------------------------------
module_exit (apci3600_exit);

module_init (apci3600_init);

