/*
 * Copyright (c) 2003 Endace Technology Ltd, Hamilton, New Zealand.
 * All rights reserved.
 *
 * This source code is proprietary to Endace Technology Ltd and no part
 * of it may be redistributed, published or disclosed except as outlined
 * in the written contract supplied with this product.
 *
 * $Id: dag_main.c 15779 2012-06-20 04:44:01Z sfd $
 */
/* #define DEBUG 1 */
#include "config.h"
#include <linux/kernel.h>
#include <linux/version.h>

#include <linux/module.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/ioport.h>
#include <linux/poll.h>
#include <linux/delay.h>
#include <linux/sched.h>
#include <linux/interrupt.h>
#include <linux/mm.h>
#include <linux/file.h>
#include <linux/highmem.h>
#include <asm/tlb.h>
#include <linux/pagemap.h>

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26)
#include <linux/pci-aspm.h>
#endif

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

#include "dagcompat.h"
#include "dagdebug.h"
#include "dagnew.h"

#include "dagdrv.h"
#include "dagpci.h"
#include "dagmem.h"
#include "dagduck.h"
#include "dagmon.h"
#include "dagreg.h"

#ifndef __devexit_p
//Define for kernels which just expect pointer and does not have the translation define
#define __devexit_p(x) x

#endif 

#ifndef pcibus_to_node
// Older kernel may have numa but pcibus_to_node is undefined
#define pcibus_to_node(node) (-1)
#endif

extern const char* const kDagReleaseVersion;

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
#define DAGUNIT(X)	(iminor(X)&DAGMINORMASK)
#define DAGMINOR(X)	(iminor(X)>>DAGMINORBITS)
#else
#define	DAGUNIT(X)	(MINOR(X->i_rdev)&DAGMINORMASK)
#define	DAGMINOR(X)	(MINOR(X->i_rdev)>>DAGMINORBITS)
#endif

#define DAG_MAX_BOARDS	(1<<DAGMINORBITS)

typedef enum dagregister {
	ToHM0	= 	0x00,
	ToHM1	= 	0x04,
	ToHM2	= 	0x08,
	ToHM3	= 	0x0c,

	ToAM0	= 	0x40,
	ToAM1	= 	0x44,
	ToAM2	= 	0x48,
	ToAM3	= 	0x4c,

	ToHDB	= 	0x80,
	Ctrl	= 	0x88,
	IntEn	= 	0x8c,
} dagresister_t;

/**
 * Bits with the DAG Ctrl / reset register (0x88), used if the
 * new PCI-e programming method is to be used.
 *
 */
#define PROG_NEXT_LINKDOWN_BIT      (1 << 27)
#define USE_NEW_PROGRAMMING_BIT     (1 << 26)
#define PROG_CURRENT_BIT            (1 << 25)
#define PROG_STABLE_BIT             (1 << 24)
#define NEW_PROGRAMMING_AVAIL_BIT   (1 << 20)
#define LINK_CAPABILITIES_REGISTER   0x0C
#define LINK_STATUS_REGISTER  0x12
#define LINK_CONTROL_REGISTER 0x10

#define MIN(a, b)   ((a)<(b)?(a):(b))

 
#define dag_iowrite(X,Y) 	writel(X, sc->mmio + Y)
#define dag_ioread(X) 	 	readl(sc->mmio + X)

enum {
	DAGF_ATTACHED	= 0x0001,
};
/* Driver global array of data structures holding state information per card */
static dag_softc_t	dag_array[DAG_MAX_BOARDS];

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
static dev_t	dag_dev;
#else
static int	dag_major;
#endif
static int msi_enabled ;
/* 	
	added parameter msi_use at kernel level so to be able to add 
	at load up driver time to choose if you wnat to use the msi interrupts
	if at modprobe or insmod type the msi_use 1 then the driver will try to use msi 
	note: msi_use does not mean that always interrupts will be used 
	the kernel has to be enabled, the bios also and some times the pci=msi on some kernels 
	PCI X cards 
*/

static int msi_use = 0;
int issue_retrain_link = 0;

extern struct file_operations	dag_fops;
extern struct file_operations	dag_mem_fops;
extern struct file_operations	dag_iom_fops;
extern struct file_operations	dag_arm_fops;

struct file_operations	*dag_fops_array[DAGMINOR_MAX] = {
	&dag_fops,
	&dag_mem_fops,
	&dag_iom_fops,
	&dag_arm_fops
};

static int
dag_find_unattached(void)
{
	int c;
	for(c=0; c<DAG_MAX_BOARDS; c++)
		if (!(dag_array[c].flags & DAGF_ATTACHED))
			return c;
	return -1;
}

/*This function parses the extended capability list and returns the board revision if applicable.*/
static int
dag_get_board_revision(struct pci_dev *dev_p)
{
	uint32_t value = 0;
	uint32_t attempts= 0;
	uint16_t address = 0x100; /*Extended Capabilities in device configuration space always begin at offset 100h.*/
	int return_value = 0;

	while( attempts < 10 )
	{
		return_value = pci_read_config_dword(dev_p,address,&value);
		if(return_value != 0) {
			DAG_ERROR("pci_read_config_dword(%x) returned an error: %d\n", address, return_value);
			goto error;
		}
		if (value == 0xffffffff) {
			DAG_DEBUG("Not a PCI-e card. read %x\n",value);
			goto error;
		}
		DAG_DEBUG(" address %.3x value %.8x \n",address,value);
		if(((value & 0xffff) == 0x03 )) {
			/* This is the Device Serial Number capability register set.
			   Offset eight contains the relavent information.*/
			address +=8;
			return_value = pci_read_config_dword(dev_p,address,&value);
			if(return_value != 0)
				goto error;

			DAG_DEBUG(" address %.3x value %.8x \n",address,value);
			
			/*1. any xilinx pci express image before November 2008 where the flash-rom serial number 
			     cannot be read will return 0x556e6b00 or 0x55ee6b00 [i.e 0x006b6e55 or 0x006bee55 [31:0] after byte swapping]
			*/
			
			if ((value == 0) || (value == 0x006b6e55) || (value == 0x006bee55)) {
				goto error;
			} else {
				/*
				  2. any xilinx pci express image after November 2008  where the flash-rom serial number 
				  cannot be read will return 0x0R556e6b or 0x0R55ee6b [i.e 0x6b6e550R or 0x6bee550R [31:0] after byte swapping], but the revision code IS still valid.

				  For all xilinx pci express images after November 2008, BIT 3:0 of register [Device Serial Number + 0x8]
				  will contain the correct board revision.
				*/
				DAG_DEBUG(" board revision %x \n",(value & 0xf));
				return (value & 0xf);
			}

		}
		else {
			/*This is not the right element.*/
			address = ((value & 0xfff00000) >> 20);
			if(address == 0x000) 
				goto error;
			attempts++;
		}
	}
error:
	DAG_ERROR("pci_read_config_word failed to read board revision\n");
	return 0;
}

/* Save PCI config space registers, need for reset procedures */
static int dag_pciconfig_save(dag_softc_t *sc)
{
	int return_value = 0;
	int cap_ptr;

	if ((return_value = pci_read_config_dword(sc->device, PCI_BASE_ADDRESS_0, &sc->pciconfig.ba0)))
		goto error;
	if ((return_value = pci_read_config_dword(sc->device, PCI_BASE_ADDRESS_1, &sc->pciconfig.ba1)))
		goto error;
	if ((return_value = pci_read_config_dword(sc->device, PCI_BASE_ADDRESS_2, &sc->pciconfig.ba2)))
		goto error;
	if ((return_value = pci_read_config_dword(sc->device, PCI_COMMAND, &sc->pciconfig.com)))
		goto error;
	if ((return_value = pci_read_config_byte(sc->device, PCI_INTERRUPT_LINE, &sc->pciconfig.lin)))
		goto error;
	if ((return_value = pci_read_config_byte(sc->device, PCI_LATENCY_TIMER, &sc->pciconfig.lat)))
		goto error;
	if ((return_value = pci_read_config_byte(sc->device, PCI_CLASS_REVISION, &sc->pciconfig.brd_rev)))
		goto error;

	if (!sc->pciconfig.brd_rev)
		sc->pciconfig.brd_rev = dag_get_board_revision(sc->device);
	
	DAG_DEBUG("dag%d: ba0 0x%x\n", sc->unit, sc->pciconfig.ba0);
	DAG_DEBUG("dag%d: ba1 0x%x\n", sc->unit, sc->pciconfig.ba1);
	DAG_DEBUG("dag%d: ba2 0x%x\n", sc->unit, sc->pciconfig.ba2);
	DAG_DEBUG("dag%d: com 0x%x\n", sc->unit, sc->pciconfig.com);
	DAG_DEBUG("dag%d: lin 0x%x\n", sc->unit, sc->pciconfig.lin);
	DAG_DEBUG("dag%d: lat 0x%x\n", sc->unit, sc->pciconfig.lat);
	DAG_DEBUG("dag%d: brd_rev 0x%x\n", sc->unit, sc->pciconfig.brd_rev);

	if ((cap_ptr = pci_find_capability(sc->device, PCI_CAP_ID_EXP))) {
		if ((return_value = pci_read_config_dword(sc->device,(cap_ptr + PCI_EXP_DEVCTL), &sc->pciconfig.dev_ctrl)))
			goto error;
		if ((return_value = pci_read_config_dword(sc->device,(cap_ptr + PCI_EXP_LNKCTL), &sc->pciconfig.link_ctrl)))
			goto error;
		DAG_DEBUG("dag%d: dev_ctl 0x%x\n", sc->unit, sc->pciconfig.dev_ctrl);
		DAG_DEBUG("dag%d: link_ctl 0x%x\n", sc->unit, sc->pciconfig.link_ctrl);
	}

	return return_value;
error:
	DAG_DEBUG("dag%d: pci_read_config_xxx returned an error: %d\n", sc->unit, return_value);
	return return_value;
}


/* Restore PCI config space registers, need for reset procedures */
int dag_pciconfig_restore(dag_softc_t *sc)
{
	int cap_ptr;
	int return_value;

	DAG_DEBUG("dag_pciconfig_restore: RESTORE\n");

	if ((return_value = pci_write_config_dword(sc->device, PCI_BASE_ADDRESS_0, sc->pciconfig.ba0)))
		goto error;
	if ((return_value = pci_write_config_dword(sc->device, PCI_BASE_ADDRESS_1, sc->pciconfig.ba1)))
		goto error;
	if ((return_value = pci_write_config_dword(sc->device, PCI_BASE_ADDRESS_2, sc->pciconfig.ba2)))
		goto error;
	if ((return_value = pci_write_config_dword(sc->device, PCI_COMMAND, sc->pciconfig.com)))
		goto error;
	if ((return_value = pci_write_config_byte(sc->device, PCI_INTERRUPT_LINE, sc->pciconfig.lin)))
		goto error;
	if ((return_value = pci_write_config_byte(sc->device, PCI_LATENCY_TIMER, sc->pciconfig.lat)))
		goto error;
	if ((return_value = pci_write_config_byte(sc->device, PCI_CLASS_REVISION, sc->pciconfig.brd_rev)))
		goto error;

	if ((cap_ptr = pci_find_capability(sc->device, PCI_CAP_ID_EXP))) {
		if ((return_value = pci_write_config_dword(sc->device, cap_ptr + PCI_EXP_DEVCTL, sc->pciconfig.dev_ctrl)))
			goto error;
		if ((return_value = pci_write_config_dword(sc->device, cap_ptr + PCI_EXP_LNKCTL, sc->pciconfig.link_ctrl)))
			goto error;
	}
	return return_value;
error:
	DAG_DEBUG("dag%d: pci_write_config_xxx returned an error: %d\n", sc->unit, return_value);
	return return_value;

}


/* This function waits till pci configuration space of the card is accesible. 
   Failure means device did not come up after the reset */
static int dag_poll_card(dag_softc_t *sc)
{
	unsigned long timeo = jiffies + HZ;
	uint32_t val;
	int ret;

	for(;;) {
		/* expect some read accesses to fail here */
		ret = pci_read_config_dword(sc->device, PCI_BASE_ADDRESS_0, &val);
		if((ret == 0) && (val != 0xFFFFFFFF))
			break;

		schedule_timeout(HZ/100);
		set_current_state(TASK_UNINTERRUPTIBLE);
		if (time_after(jiffies, timeo)) {
			DAG_ERROR("dag%d: card is not on the bus after 200 msec. Power cycle to recover \n", sc->unit);
			return -EFAULT;
		}
	}
	return 0;
}

/* This function waits till link width of the parent device become more than 0 in order to make sure that card is on the bus, 
   Failure means device did not come up after the reset, and platform needs a reboot */
static int dag_poll_parent_link(dag_softc_t *sc)
{
	unsigned long timeo = jiffies + HZ / 5;
	struct pci_dev *p_dev = sc->device->bus->self;
	uint16_t nlw = 0, cap_offt = 0;
	int ret;

	/* In guest mode we should never get here */
	BUG_ON(!p_dev);

	cap_offt = pci_find_capability(p_dev, PCI_CAP_ID_EXP);
	if (!cap_offt) {
		DAG_ERROR("Could not get valid capability list offset. Please Check if Card is plugged in PCI-e slot.\n");
		return -ENODEV;
	}
	/* This is to make sure that the card comes back on the bus as retrain link is called after George/Dave.
	The time taken for the card to come back on the bus varies depending on the card , motherboard , if we are trying to program 
	a valid user image etc.However The worst time is expected to be less than 200 milliseconds.We are polling every 10 milliseconds.
	*/
	for(;;) {
		if ((ret = pci_read_config_word(p_dev, cap_offt + PCI_EXP_LNKSTA, &nlw))) {
			DAG_ERROR("dag%d: pci_read_config_word returned an error: %d\n", sc->unit, ret);
			return -EFAULT;
		}

		nlw = ((nlw & PCI_EXP_LNKSTA_NLW) >> 4);
		if (nlw)
			break;

		schedule_timeout(HZ/100);
		set_current_state(TASK_UNINTERRUPTIBLE);
		if (time_after(jiffies, timeo)) {
			DAG_ERROR("dag%d: card is not on the bus after 200 msec. Power cycle to recover \n", sc->unit);
			return -EFAULT;
		}
	}

	return nlw;
}

/*This function waits till card is back on the bus and then retrains 
 * the link under the following condition: If the Negotiated Link is not 
 * the same as maximum supported, so retrain is needed.
 * Note: this code attempts to solve issues seen on chipset i5400. 
 * Note: comparing to previous impolementation his function does retrain 
 * procedure only, it assumes that card is fully on the bus after reset
*/
static int dag_retrain_link(dag_softc_t *sc)
{
	struct pci_dev *parent_p;
	int i;
	int16_t parent_cap_offset, card_cap_offset, value;
	int parent_max_width, card_max_width, negotiated_width;

	/* get a device pointer to the parent device */
	if (!sc->device->bus || !sc->device->bus->self) {
		DAG_DEBUG("dag%d: Bus device is not represented, virtual machine?\n", sc->unit);
		return 0;
	}

	parent_p = sc->device->bus->self;
	DAG_DEBUG("dag%d: parent device [%04x:%04x] name %s\n", sc->unit, parent_p->vendor, parent_p->device, sc->device->bus->name);

	parent_cap_offset = pci_find_capability(parent_p, PCI_CAP_ID_EXP);
	card_cap_offset = pci_find_capability(sc->device, PCI_CAP_ID_EXP);
	if (!card_cap_offset) {
		DAG_DEBUG("Could not get valid capability list offset. Please Check if Card is plugged in PCI-e slot.\n");
		return 0;
	}

	for (i = 0; i < 3; i++) {
		pci_read_config_word(parent_p, parent_cap_offset + PCI_EXP_LNKCAP, &value);
		parent_max_width = (value & PCI_EXP_LNKCAP_MLW) >> 4;

		pci_read_config_word(sc->device, card_cap_offset + PCI_EXP_LNKCAP, &value);
		card_max_width = (value & PCI_EXP_LNKCAP_MLW) >> 4;

		pci_read_config_word(parent_p, parent_cap_offset + PCI_EXP_LNKSTA, &value);
		negotiated_width = (value & PCI_EXP_LNKSTA_NLW) >> 4;

		if (negotiated_width == parent_max_width || negotiated_width == card_max_width)
			break;

		DAG_DEBUG("dag%d: link width mistmatch, retrain the link \n", sc->unit);

		pci_write_config_word (parent_p, parent_cap_offset + PCI_EXP_LNKCTL, PCI_EXP_LNKCTL_LD | PCI_EXP_LNKCTL_ES);
		msleep(125);
		pci_write_config_word (parent_p, parent_cap_offset + PCI_EXP_LNKCTL, PCI_EXP_LNKCTL_RL | PCI_EXP_LNKCTL_ES);
		msleep(125);
	}
	DAG_DEBUG("dag%d: negotiated link width is %d \n", sc->unit, negotiated_width);
	return i;
}

/* Reset George method, link down - wait - retrain link */
static int dag_do_reset_george(struct pci_dev *parent_dev_p)
{
	int16_t parent_cap_offset;

	DAG_DEBUG("DAG parent device [%04x:%04x]\n", parent_dev_p->vendor, parent_dev_p->device);

	parent_cap_offset = pci_find_capability(parent_dev_p, PCI_CAP_ID_EXP);
	if (!parent_cap_offset) {
		DAG_ERROR("DAG: The george method is not applicable for non PCI-E devices \n");
		return -ENODEV;
	}
	pci_write_config_word (parent_dev_p, parent_cap_offset + PCI_EXP_LNKCTL, PCI_EXP_LNKCTL_LD | PCI_EXP_LNKCTL_ES);
	msleep(1500);
	pci_write_config_word (parent_dev_p, parent_cap_offset + PCI_EXP_LNKCTL, PCI_EXP_LNKCTL_RL | PCI_EXP_LNKCTL_ES);
	return 0;
}

/* Reset DAVE method, toggle hot burn reset bit on the bridge (parent) */
static int dag_do_reset_dave(struct pci_dev *parent_dev_p)
{
	int ret;
	/* a bit of debugging output, requires the driver to be built without the NDEBUG option */
	DAG_DEBUG("DAG Parent device [%04x:%04x]\n", parent_dev_p->vendor, parent_dev_p->device);

	/* toggle the hot-burn reset bit in the PCI bridge register (the minimum is 1ms, see Trst in the PCI spec 3.0) */
	if ((ret = pci_write_config_word (parent_dev_p, PCI_CB_BRIDGE_CONTROL, PCI_CB_BRIDGE_CTL_CB_RESET))) {
		DAG_ERROR("DAG failed to write reset to PCI_CB_BRIDGE_CONTROL register : %d\n", ret);
		return ret;
	}

	/* Card is reseting itself we need to wait for a while */
	msleep(1500);

	if ((ret = pci_write_config_word (parent_dev_p, PCI_CB_BRIDGE_CONTROL, 0))) {
		DAG_ERROR("DAG failed to clean PCI_CB_BRIDGE_CONTROL register : %d\n", ret);
		return ret;
	}

	return 0;
}

/* This function handles the DAG_RESET ioctl, different reset procedures are called depending on reset type */
/* TODO: DAG card interrupts are disabled from user space - it is not good because we enable them in kernel, 
    we must move intrs disbling code to kernel */
static int dag_reset(dag_softc_t *sc, int type)
{
	struct pci_dev *parent_dev_p; 
	uint32_t value;
	int ret = 0;

	/* get a device pointer to the parent device */
	if (!sc->device->bus || !sc->device->bus->self) {
		DAG_DEBUG("dag%d: Bus device is not represented, virtual machine?\n", sc->unit);
		return -ENODEV;
	}
	parent_dev_p = sc->device->bus->self;

	switch(type) {
		/* Full reset for PCI-X cards */
		case DAGRESET_FULL:
			value = dag_ioread(Ctrl);
			if ((value & NEW_PROGRAMMING_AVAIL_BIT) && (value & USE_NEW_PROGRAMMING_BIT) && (parent_dev_p != NULL)) {
				if ((value & PROG_NEXT_LINKDOWN_BIT) &&
				    !(value & (PROG_STABLE_BIT | PROG_CURRENT_BIT))) {
					value |= PROG_STABLE_BIT;
					dag_iowrite(value, Ctrl);
				}
				ret = dag_do_reset_dave(parent_dev_p);
			}
			else {
				value = dag_ioread(Ctrl);
				value |= 0x40000000;
				dag_iowrite(value, Ctrl);
				msleep(1);
				value &= ~0x40000000;
				dag_iowrite(value, Ctrl);
				msleep(1000);
			}
			break;
		/* Reset DAVE default for PCI-E cards */
		case DAGRESET_DAVE:
			ret = dag_do_reset_dave(parent_dev_p);
			break;
		/* Reset DAVE alternative for PCI-E cards */
		case DAGRESET_GEORGE:
			ret = dag_do_reset_george(parent_dev_p);
			break;
		/* Reset reboot just restores PCI config space */
		case DAGRESET_REBOOT:
			break;
		/* Reset Dag Unified Clock Kit it is not a card reset */
		case DAGRESET_DUCK:
			duck_init(sc);
			return 0;
		default:
			DAG_ERROR("dag%d: Unknown IOCRESET type %d\n",
			       sc->unit, type);
			break;
	}

	dag_poll_parent_link(sc);		/* wait till card appear on the bus */
	dag_poll_card(sc);			/* wait till card registers can be read */
	dag_retrain_link(sc);			/* retrain link if we need to */
	ret = dag_pciconfig_restore(sc);	/* rewrite pci conf */
	dag_iowrite(0xffff, IntEn);		/* Re-enable ints */
	duck_init(sc);				/* restart duck */
	dagmon_init(sc);
	return ret;
}

#if defined(CONFIG_HUGETLBFS) && defined(AS_HUGETLB)
static int dag_fill_hugetlb_host(struct file *h_file, dma_addr_t start, 
				uint32_t size, unsigned long hpage_size)
{
	int cnt = 0, ret = 0;
	struct page *page;

	/* Size is aligned - otherwise a failure returned before */
	while(size) {
		DAG_DEBUG("add to page cache %p offt %llx \n",h_file->f_mapping,start);
		page = pfn_to_page(start >> PAGE_SHIFT);
		ret = add_to_page_cache(page, h_file->f_mapping, cnt, GFP_KERNEL);
		if (ret) 
			goto out_failed;
		SetPageUptodate(page);
		unlock_page(page);
		start += hpage_size;
		size -= hpage_size;
		cnt++;
	}
	return 0;

out_failed:
	invalidate_inode_pages(h_file->f_mapping);
	return ret;
}

struct file *dag_get_hugetlb_host(const char* name, uint32_t size, dma_addr_t phy)
{
	unsigned long hpage_size;
	struct file *h_file;

	h_file = dag_create_hugetlb_file(name, size, HUGETLB_DEVBACK_INODE);
	
	if (!h_file)
		return NULL;
#ifdef hstate_file
	hpage_size = huge_page_size(hstate_file(h_file));
#else
	hpage_size = HPAGE_SIZE;
#endif

	if ((DAG_ALIGN(phy, hpage_size) != phy)
	 ||(DAG_ALIGN(phy + size, hpage_size) != phy + size)) {
		DAG_DEBUG("buffers are not alligned to huge page size %luK\n",hpage_size>>10);
		goto out_failed;
	}
	/* Let fill page cache with the data */
	if (dag_fill_hugetlb_host(h_file, phy, size, hpage_size))
		goto out_failed;
	DAG_DEBUG("Huge page mappings are enabled. Page size is %ldK\n",hpage_size>>10);
	return h_file;

out_failed:
	DAG_DEBUG("Huge page mappings are disabled Page size is %ldK\n", PAGE_SIZE>>10);
	fput(h_file);
	return NULL;

}
#else
#define dag_get_hugetlb_host(name, size, phy) (NULL)
#endif

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
static irqreturn_t
#else
#define IRQ_NONE
#define IRQ_HANDLED
static void
#endif
dag_interrupt(int irq, void *dev
#if ( LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,18) )
	      , struct pt_regs *regs
#endif
	      )
{
	dag_softc_t	*sc = dev;
	int		type;
	uint32_t 	val ;
	int counter;
	int return_value;

	//CHECK for DRB BAR if is UP and configured 
	//Protection for shared interrupts not to access the CARD in case 
	// shared interrupt and the cared is off the BUS 
	return_value = pci_read_config_dword(sc->device, PCI_BASE_ADDRESS_0, &val);
	if(return_value != 0) {
		DAG_DEBUG("dag%d: %s line %d: pci_read_config_word returned an error: %d\n", sc->unit, __FUNCTION__, __LINE__, return_value);
		return IRQ_NONE;
	}
	
	if( (val == 0) || (val == 0xFFFFFFFF ))
	{
		DAG_DEBUG("dagint v1\n");
		return IRQ_NONE;
	}

	type = dag_ioread(ToHDB) & 0xffff;

	if ((type == 0xffff) || (type == 0x0)) {
		/* defined invalid, probably FPGA resetting */
		/* OR shared int but not for us */
		DAG_DEBUG("dagint: v2\n");
		return IRQ_NONE;
	}
	counter = 0;
	/* clear and process interrupt */
	while (type) 
	{
		/* clear current interrupt */
		DAG_DEBUG("dag%d: intr 0x%04x\n", sc->unit, type);
		dag_iowrite(type, ToHDB);


		/* duck interrupt */
		if (type & 0x110) {
			DAG_DEBUG("dag%d: duck interrupt\n", sc->unit);
			duck_intr(sc);
		}
	
		if(type & 0x1000) {
			/* PBM safety-net-reached */
			DAG_INFO("dag%d: pbm safety net reached\n", sc->unit);
		}

		if(type & 0x2000) {
			/* phy status change */
			DAG_INFO("dag%d: Phy status change\n", sc->unit);
		}

		if(type & 0x0001) {
			/* thermal overload in PP chip - DAG6.2RevC */
			DAG_ERROR("dag%d: thermal overload\n", sc->unit);
		}
		type = dag_ioread(ToHDB) & 0xffff;
		if( type == 0xFFFF )
		{
			DAG_INFO("dag%d: Possible problem with interrupts or firmware read; should not happen\n", sc->unit);
			break;
		};
		counter++;
		if(counter > 100 ) {
			/* disable interrupts  */
			dag_iowrite(0x0000, IntEn);
			/* clear all interrupts */
			dag_iowrite(0x0000, ToHDB);
			DAG_INFO("dag%d: Problem with interrupts probably flooding. Interupts have been disabled in the firmware interrupt mask register\n", sc->unit);
			break;
		}
		
	}
	return IRQ_HANDLED;
}

/* MUST hold lock_sem before calling */
static int
dag_card_is_locked(struct file *fp)
{
	dag_softc_t *sc;
	dag_lock_t *lock;
	
	if ( (!fp) || (!fp->private_data) ) {
		return -EINVAL;
	}
	
	sc = fp->private_data;
	lock = &sc->card_lock;

	if (lock->pid)
		return -EACCES;

	return 0;
}

/* MUST hold lock_sem before calling */
static int
dag_mode_is_locked(struct file *fp, int mode)
{
	dag_softc_t *sc;
	dag_lock_t *lock;
	
	if ( (!fp) || (!fp->private_data) || (mode != DAG_NORMAL_MODE) ) {
		return -EINVAL;
	}

	sc = fp->private_data;
	lock = &sc->mode_lock;

	if (lock->pid)
		return -EACCES;

	return 0;
}

/* MUST hold lock_sem before calling */
static int
dag_stream_is_locked(struct file *fp, int stream, int mode)
{
	dag_softc_t *sc;
	dag_lock_t *lock;
	
	if ( (!fp) || (!fp->private_data) || (mode != DAG_NORMAL_MODE)) {
		return -EINVAL;
	}
	
	sc = fp->private_data;
	lock = &sc->stream_lock[stream];

	if (lock->pid)
		return -EACCES;

	return 0;
}

/* MUST hold lock_sem before calling */
static int
dag_card_lock(struct file *fp, int set)
{
	dag_softc_t *sc;
	dag_lock_t *lock;
	int error, stream;

	if ( (!fp) || (!fp->private_data) ) {
		return -EINVAL;
	}
	
	sc = fp->private_data;
	lock = &sc->card_lock;

	if (set) {
		/* cant get card lock if any lower level locks held */

		/* test mode lock */
		if ( (error = dag_mode_is_locked(fp, DAG_NORMAL_MODE)) )
			return error;

		/* test stream locks */
		for(stream = 0; stream < DAG_STREAM_MAX; stream++) {
			if ( (error = dag_stream_is_locked(fp, stream, DAG_NORMAL_MODE)) )
				return error;			
		}
		
		if(lock->pid == 0) {
			lock->pid = current->pid;
			lock->tgid = current->tgid;
			lock->locked_file = fp;
		} else if (lock->pid == current->pid) {
			/* multiple aquire lock, fine */
		} else {
			return -EACCES;
		}

	} else { /* unlock */
		if( (lock->tgid == current->tgid) &&
		    (lock->locked_file == fp) )
		{	
			lock->pid = 0;
			lock->tgid = 0;
			lock->locked_file = 0;
		} else { /* someone elses lock */
			return -EACCES;
		}
	}

	DAG_DEBUG("dag_ioctl: unit %u card lock now held by %d (group %d), current pid %d (group %d)\n", sc->unit, lock->pid, lock->tgid, current->pid, current->tgid);

	return 0;
}

/* MUST hold lock_sem before calling */
static int
dag_mode_lock(struct file *fp, int mode, int set)
{
	dag_softc_t *sc;
	dag_lock_t *lock;
	int error, stream;

	if ( (!fp) || (!fp->private_data) || (mode != DAG_NORMAL_MODE) ) {
		return -EINVAL;
	}
	
	sc = fp->private_data;
	lock = &sc->mode_lock;

	/* test card lock first */
	if ( (error = dag_card_is_locked(fp)) )
		return error;

	if (set) {
		/* cant get card lock if any lower level locks held */

		/* test stream locks */
		for(stream = 0; stream < DAG_STREAM_MAX; stream++) {
			if ( (error = dag_stream_is_locked(fp, stream, DAG_NORMAL_MODE)) )
				return error;
		}
		
		if(lock->pid == 0) {
			lock->pid = current->pid;
			lock->tgid = current->tgid;
			lock->locked_file = fp;
		} else if (lock->pid == current->pid) {
			/* multiple aquire lock, fine */
		} else {
			return -EACCES;
		}

	} else { /* unlock */
		if( (lock->tgid == current->tgid) &&
		    (lock->locked_file == fp) )
		{	
			lock->pid = 0;
			lock->tgid = 0;
			lock->locked_file = 0;
		} else { /* someone elses lock */
			return -EACCES;
		}
	}

	DAG_DEBUG("dag_ioctl: unit %u %s mode lock now held by %d (group %d), current pid %d (group %d)\n", sc->unit, mode?"REVERSE":"NORMAL", lock->pid, lock->tgid, current->pid, current->tgid);

	return 0;
}

/* MUST hold lock_sem before calling */
static int
dag_stream_lock(struct file *fp, int stream, int mode, int set)
{
	dag_softc_t *sc;
	dag_lock_t *lock;
	int error;

	if ( (!fp) || (!fp->private_data) || (mode != DAG_NORMAL_MODE) || (stream >= DAG_STREAM_MAX) ) {
		return -EINVAL;
	}
	
	sc = fp->private_data;

	/* test card lock first */
	if ( (error = dag_card_is_locked(fp)) )
		return error;

	/* test mode lock second */
	if ( (error = dag_mode_is_locked(fp, mode)) )
		return error;

	/* can do stream op */

	lock = &sc->stream_lock[stream];
	
	if (set) {
		if(lock->pid == 0) {
			lock->pid = current->pid;
			lock->tgid = current->tgid;
			lock->locked_file = fp;
		} else if (lock->pid == current->pid) {
			/* multiple aquire lock, fine */
		} else {
			return -EACCES;
		}

	} else {
		if( (lock->tgid == current->tgid) &&
		    (lock->locked_file == fp) )
		{	
			lock->pid = 0;
			lock->tgid = 0;
			lock->locked_file = 0;
		} else {
			return -EACCES;
		}

	}

	DAG_DEBUG("dag_ioctl: unit %u stream %d %s mode lock now held by %d (group %d), current pid %d (group %d)\n", sc->unit, stream, mode?"REVERSE":"NORMAL", lock->pid, lock->tgid, current->pid, current->tgid);

	return 0;
}

#if defined(CONFIG_HUGETLBFS) && defined(AS_HUGETLB)
static unsigned long dag_get_unmapped_area(struct file *fp,
	unsigned long addr, unsigned long len, unsigned long pgoff,
	unsigned long flags)
{
	dag_softc_t	*sc = fp->private_data;
	/* We can't get here if hugetlb host_file is NULL*/
	BUG_ON(!sc->host_file);
	return get_unmapped_area(sc->host_file, addr, len, pgoff, flags);
}
#endif
static int
dag_open(struct inode *inode, struct file *file)
{
	int		unit = DAGUNIT(inode);
	dagminor_t	minor = DAGMINOR(inode);
	dag_softc_t	* sc;
	
	dag_reg_t	result[DAG_REG_MAX_ENTRIES];
	int 		error;

	DAG_DEBUG("dag_open i_rdev %d unit %d minor %d priv %p\n", inode->i_rdev, unit, minor, file->private_data);
	/* if file->private_data is not valid, then we are not using devfs.
	 * This means that we must decode the minor number to attach the
	 * correct fops for the entry point.
	 */
	error = 0;

	if(!file->private_data && minor) {
		/* check minor number is valid */
		if (minor >= DAGMINOR_MAX) {
			DAG_INFO("dag%u: open: illegal minor %u\n", unit, minor);
			return -ENODEV;
		}

		file->f_op = dag_fops_array[minor];
	}

	sc = (dag_softc_t *)file->private_data;
	

	/* if file->private_data is not valid, then we are not using devfs.
	 * This means we need to check that the unit number is valid, and
	 * fill in private_data for other methods
	 */
	if (!sc) {
		/* Unit spans too high */
		if(unit > DAG_MAX_BOARDS ) {
			DAG_ERROR("dag%u: open: unit number too high, does not exist\n", unit);
			return  -ENODEV;
		}
		
		/* Unit in valid range, but card may has been removed */
		if(!(dag_array[unit].flags & DAGF_ATTACHED)) {
			DAG_ERROR("dag%u: open: unit does not exist\n", unit);
			return -ENODEV;
		}
		
		file->private_data = &dag_array[unit];
		sc = (dag_softc_t *)file->private_data;
	}
	
	if(down_interruptible(&sc->lock_sem))
	{
			return  -ERESTARTSYS;
	};

	/* In a case of hugetlb we must redefine file->mapping as well as we 
	 * must define get_unmapped_area in order to gaurantee virtual address to 
         * be aligned (we can't define get_unmapped_area in non-hugetlb cases 
	 * as there is no calls like generic_get_unampped_area in kernel)*/
#if defined(CONFIG_HUGETLBFS) && defined(AS_HUGETLB)
	if (sc->host_file && ((minor == DAGMINOR_DAG) || (minor == DAGMINOR_MEM))) {
		file->f_mapping = sc->host_file->f_mapping;
		(dag_fops_array[minor])->get_unmapped_area = dag_get_unmapped_area;
	}
#endif
	if (minor == DAGMINOR_ARM)
		if(dag_reg_find(sc->mmio, DAG_REG_ARM, result) < 1)
		{	
			error = -ENODEV;
			goto dag_open_exit;
		};
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
	MOD_INC_USE_COUNT;
#endif
dag_open_exit: 
	up(&sc->lock_sem);
	return error;
}

static int
dag_release(struct inode *inode, struct file * file)
{
	int		stream;


	/*
	 * If the device is locked, and the process releasing the device has the lock,
	 * then it has not exited correctly.
	 *
	 * Might be safest to reset the card as well, but what if other streams are
	 * locked? Leave it for now and trust the PBM to cope.
	 */

	if (DAGMINOR(inode) == DAGMINOR_DAG) {

		dag_card_lock(file, DAG_LOCK_OP_RELEASE);
		
		dag_mode_lock(file, DAG_NORMAL_MODE, DAG_LOCK_OP_RELEASE);
		
		for(stream=0; stream<DAG_STREAM_MAX; stream++) {
			dag_stream_lock(file, stream, DAG_NORMAL_MODE, DAG_LOCK_OP_RELEASE);
		}
	}

#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
	MOD_DEC_USE_COUNT;
#endif
	/* Recover address space, f_mapping and i_mapping should be 
	 * the same if not hugetlb */
#if defined(CONFIG_HUGETLBFS) && defined(AS_HUGETLB)
	file->f_mapping = inode->i_mapping;
#endif
	return 0;
}

static ssize_t
dag_mem_read(struct file *fp, char *buf, size_t count, loff_t *ppos)
{
	dag_softc_t	*sc = fp->private_data;
	size_t		remain, cplen, pgoff, full_cnt;
	struct page *pg;
	uint8_t *kbuf = NULL;
	
	if(!sc->phys_addr)
		return -ENXIO;
	if(*ppos >= sc->pmem_size)
		return 0;
	if((*ppos + count) > sc->pmem_size)
		count = sc->pmem_size - *ppos;
	if(count == 0)
		return 0;
	
	full_cnt = count;
	/* Since we now operate phys pages directly to read 
	 * or write something we must map phys the area to kernel space first */
	while (count > 0) {		
		pgoff = *ppos & (PAGE_SIZE - 1); 
		cplen = min((unsigned long)count, (unsigned long)(PAGE_SIZE - pgoff));

		if (pfn_valid((sc->phys_addr + *ppos) >> PAGE_SHIFT)) {
			pg = pfn_to_page((sc->phys_addr + *ppos) >> PAGE_SHIFT);
			kbuf = kmap(pg);
			remain = copy_to_user(buf, &kbuf[pgoff], cplen);
			kunmap(pg);
		} else {
			kbuf = ioremap( (sc->phys_addr + *ppos), cplen);
			remain = copy_to_user(buf, kbuf, cplen);
			iounmap(kbuf);
		}
		if(remain == cplen)
			return -EINVAL;
		*ppos += cplen - remain;
		buf += cplen - remain;
		count -= cplen - remain;
	}
	return full_cnt - count;
}

static ssize_t
dag_iom_read(struct file *fp, char *buf, size_t count, loff_t *ppos)
{
	dag_softc_t	*sc = fp->private_data;
	size_t		remain;
	int error;
#ifdef __ia64__
	uint32_t tmp_data;
#endif
	error = 0;
	if(*ppos > sc->mmio_size)
		return 0;
	
	if(down_interruptible(&sc->lock_sem))
	{
			return  -ERESTARTSYS;
	};
	

	
	if((*ppos + count) > sc->mmio_size)
		count = sc->mmio_size - *ppos;
	if(count == 0)
	{
		error = 0;
		goto dag_iom_read_exit;
	};
	if((*ppos & 0x03) != 0){
		error = -EFAULT;		/* only support word aligned access */
		goto dag_iom_read_exit;
	};
	if((count & 0x03) != 0){
		error = -EFAULT;		/* only support word sized access */
		goto dag_iom_read_exit;
	};
#ifdef __ia64__
//error chnaging to 32 bit access 
	DAG_DEBUG("count,*ppos %d, %d \n",count,*ppos);
	for(remain = count ;remain  >= 4;remain -= 4){
		tmp_data = dag_ioread(*ppos);
		if( copy_to_user(buf+*ppos,tmp_data,4) != 0 )
			DAG_DEBUG("error dag_iom_read\n");
		*ppos += 4;
	}
#else 	
	remain = copy_to_user(buf, sc->mmio + *ppos, count);
	*ppos += count - remain;
#endif
	DAG_DEBUG("remain %zd, count %zd, *ppos %lld\n",remain, count,*ppos); 
	if(remain == count) {
		error = -EINVAL;
		goto dag_iom_read_exit;
	} else {
		error = count-remain;
		goto dag_iom_read_exit;
	};
	
dag_iom_read_exit: 
	up(&sc->lock_sem);
	return error;
}

static ssize_t
dag_arm_read(struct file *fp, char *buf, size_t count, loff_t *ppos)
{
	dag_softc_t	*sc = fp->private_data;
	size_t		remain;
	int		params[16], error=0;

	DAG_DEBUG("dag_arm_read count %zd ppos %zd\n", count, (size_t)*ppos);
	if(count == 0)
		return 0;
	if((*ppos & 0x03) != 0)
		return -EFAULT;		/* only support word aligned access */
	if((count & 0x03) != 0)
		return -EFAULT;		/* only support word sized access */
	
	for( remain = count >> 2; remain >= 12 ; remain -=12) {
		params[0] = *ppos;	/* auto modulo 2^32 */
		error = dagmon(sc, DAGMON_READ_12_WORDS, params);
		if(error)
			return error;
		error = copy_to_user(buf+count-(remain<<2), &params[1], 12*sizeof(int));
		*ppos += 12*sizeof(int) - error;
		if (error)
			return -EFAULT;
	}
	
	for( ; remain > 0 ; remain--) {
		params[0] = *ppos;	/* auto modulo 2^32 */
		error = dagmon(sc, DAGMON_READ_WORD, params);
		if(error)
			return error;
		error = copy_to_user(buf+count-(remain<<2), &params[1], sizeof(int));
		*ppos += sizeof(int) - error;
		if (error)
			return -EFAULT;
	}
	return count-remain;
}

static ssize_t
dag_mem_write(struct file *fp, const char *buf, size_t count, loff_t *ppos)
{
	dag_softc_t	*sc = fp->private_data;
	size_t		remain, cplen, pgoff, full_cnt;
	struct page *pg;
	uint8_t *kbuf = NULL;

	if(!sc->phys_addr)
		return -ENXIO;
	if(*ppos >= sc->pmem_size)
		return -ENOSPC;
	if((*ppos + count) > sc->pmem_size)
		count = sc->pmem_size - *ppos;
	if(count == 0)
		return -ENOSPC;
		
	full_cnt = count;
	
	while (count > 0) {		
		pgoff = *ppos & (PAGE_SIZE - 1); 
		cplen = min((unsigned long)count, (unsigned long)PAGE_SIZE - pgoff);

		if (pfn_valid((sc->phys_addr + *ppos) >> PAGE_SHIFT)) {
			pg = pfn_to_page((sc->phys_addr + *ppos) >> PAGE_SHIFT);
			kbuf = kmap(pg);
			remain = copy_from_user(&kbuf[pgoff], buf, cplen);
			kunmap(pg);
		} else {
			kbuf = ioremap( (sc->phys_addr + *ppos), cplen);
			remain = copy_from_user(kbuf, buf, cplen);
			iounmap(kbuf);
		}
		if(remain == cplen)
			return -EINVAL;
		*ppos += cplen - remain;
		buf += cplen - remain;
		count -= cplen - remain;
	}

	return full_cnt - count;
}

static ssize_t
dag_iom_write(struct file *fp, const char *buf, size_t count, loff_t *ppos)
{
	dag_softc_t	*sc = fp->private_data;
	size_t		remain;

	if(*ppos > sc->mmio_size)
		return 0;
	if((*ppos + count) > sc->mmio_size)
		count = sc->mmio_size - *ppos;
	if(count == 0)
		return 0;
	if((*ppos & 0x03) != 0)
		return -EFAULT;		/* only support word aligned access */
	if((count & 0x03) != 0)
		return -EFAULT;		/* only support word sized access */
	remain = copy_from_user(sc->mmio + *ppos, buf, count);
	*ppos += count - remain;
	if(remain == count)
		return -EINVAL;
	else
		return count-remain;
}

static ssize_t
dag_arm_write(struct file *fp, const char *buf, size_t count, loff_t *ppos)
{
	dag_softc_t	*sc = fp->private_data;
	size_t		remain;
	int		params[16], error=0;

	DAG_DEBUG("dag_arm_write count %zd ppos %zd\n", count, (size_t)*ppos);
	if(count == 0)
		return 0;
	if((*ppos & 0x03) != 0)
		return -EFAULT;		/* only support word aligned access */
	if((count & 0x03) != 0)
		return -EFAULT;		/* only support word sized access */
	
	for( remain = count >> 2; remain >= 12 ; remain -=12) {
		error = copy_from_user(&params[1], buf+count-(remain<<2), 12*sizeof(int));
		if (error)
			return -EFAULT;
		params[0] = *ppos;	/* auto modulo 2^32 */
		error = dagmon(sc, DAGMON_WRITE_12_WORDS, params);
		if(error)
			return error;
		*ppos += 12*sizeof(int);
	}
	
	for( ; remain > 0 ; remain--) {
		error = copy_from_user(&params[1], buf+count-(remain<<2), sizeof(int));
		if (error)
			return -EFAULT;
		params[0] = *ppos;	/* auto modulo 2^32 */
		error = dagmon(sc, DAGMON_WRITE_WORD, params);
		if(error)
			return error;
		*ppos += sizeof(int);
	}
	return count-remain;
}

static void dag_get_mem_info(dag_softc_t *sc, dag_meminfo_t *mem_info)
{
	uint32_t	nid = 0;
	uint64_t	pos;
	
	memset(mem_info,0x0, sizeof(dag_meminfo_t));
	pos = 0;

	mem_info->page_size = PAGE_SIZE;
	if (sc->host_file) {
#ifdef hstate_file
		mem_info->page_size = huge_page_size(hstate_file(h_file));
#else
		mem_info->page_size = HPAGE_SIZE;
#endif
	}
	while (pos < sc->pmem_size) {
            if (!pfn_valid((sc->phys_addr + pos) >> PAGE_SHIFT))
	        return;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
	    nid = dag_page_to_nid(pfn_to_page((sc->phys_addr + pos) >> PAGE_SHIFT));
#endif
	    if (nid >= DAG_MAX_NODES)
		DAG_ERROR("DAG Error returned node Id %u is out of range!\n",nid);
	    else
		mem_info->nid_pages[nid]++;
	    pos += mem_info->page_size;
	}
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34)
static long dag_unlocked_ioctl(struct file *fp, u_int cmd, u_long arg)
#else
static int dag_ioctl(struct inode *inode, struct file *fp, u_int cmd, u_long arg)
#endif
{
	dag_softc_t	*sc = fp->private_data;	
	int		error;
	u_long		larg;
	int		params[16];
	daginf_t	info;
	duckinf_t	duckinf;
	int 		tmp_state;
	uint64_t	phyaddr;
	duck_irq_time_t duck_irq_time;
	int		mode, op, stream;
	uint32_t	dev_nid;
	dag_meminfo_t	mem_info;

	DAG_DEBUG("dag%d: IOCTL 0x%08x\n", sc->unit, cmd);
	error = 0;
	if (_IOC_TYPE(cmd) != DAG_IOC_MAGIC) return -ENOTTY;
	if (_IOC_NR(cmd) > DAG_IOC_MAXNR) return -ENOTTY;
	if (_IOC_DIR(cmd) & _IOC_READ)
		error = !access_ok(VERIFY_WRITE, (void *)arg, _IOC_SIZE(cmd));
	else if (_IOC_DIR(cmd) & _IOC_WRITE)
		error = !access_ok(VERIFY_READ, (void *)arg, _IOC_SIZE(cmd));
	if (error) return -EFAULT;
	
	if(down_interruptible(&sc->lock_sem))
			return  -ERESTARTSYS;

	//store the cuurrent task state 
	//and put in uninterruptable state in case the dangorus operations 
	tmp_state = current->state;

	switch(cmd) {
	case DAGIOCRESET:
		__get_user(larg, (int*)arg);
		error = dag_reset(sc, larg);
		break;
	case DAGIOCMONITOR:
		if(copy_from_user(&params, (void*)arg, sizeof(monparams_t))) {
			error = -EFAULT;
			break;
		}
		error = dagmon(sc, params[0], &params[1]);
		if(copy_to_user((void*)arg, &params, sizeof(monparams_t)))
			error = -EFAULT;
		break;
	case DAGIOCINFO:
		if (sc->device)
			info.device_code = sc->device->device;
		else
			info.device_code = 0;
		info.id = sc->unit;
		info.phy_addr = (uint32_t)sc->bus_addr;
		info.buf_size = sc->pmem_size;
		info.iom_size = sc->mmio_size;
		info.brd_rev  = sc->pciconfig.brd_rev; 
		strncpy(info.bus_id, pci_name(sc->device), DAG_ID_SIZE);
		if(copy_to_user((void*)arg, &info, sizeof(daginf_t)))
			error = -EFAULT;
		break;
	case DAGIOCPHYADDR:
		phyaddr = (uint64_t)sc->bus_addr;
		if(copy_to_user((void*)arg, &phyaddr, sizeof(phyaddr)))
			error = -EFAULT;
		break;
	case DAGIOCDEVNID:
		dev_nid = 0;
		if (sc->device) {
		    dev_nid = pcibus_to_node(sc->device->bus);
		}
		if(copy_to_user((void*)arg, &dev_nid, sizeof(dev_nid)))
			error = -EFAULT;
		break;
	case DAGIOCMEMINFO:
		dag_get_mem_info(sc, &mem_info);
		if(copy_to_user((void*)arg, &mem_info, sizeof(mem_info)))
			error = -EFAULT;
		break;
	case DAGIOCLOCK:
		__get_user(larg, (int*)arg);
        	mode = (larg >> 8) & 0x1;
		op = larg & DAG_LOCK_OP;
		
		/* Locks are hierarchial
		 * ONLY do the highest precedence op requested
		 */

		if (larg & DAG_LOCK_CARD) {
			error = dag_card_lock(fp, op);

		} else if (larg & DAG_LOCK_MODE) {
			error = dag_mode_lock(fp, mode, op);

		} else {
			stream = (larg >>16) & 0xffff;
			error = dag_stream_lock(fp, stream, mode, op);
		}

		break;

	case DAGIOCDUCK:
		
		if(copy_from_user(&duckinf, (void*)arg, sizeof(duckinf_t))) {
			error = -EFAULT;
			break;
		}
		error = duck_ioctl(sc->duck, &duckinf);
		if(copy_to_user((void*)arg, &duckinf, sizeof(duckinf_t)))
			error = -EFAULT;
		break;
	case DAGIOCIRQTIME:
		error = duck_get_irq_time_ioctl(sc->duck, &duck_irq_time);
		if(copy_to_user((void*)arg, &duck_irq_time, sizeof(duck_irq_time_t)))
			error = -EFAULT;
		break;
	case DAGIOCSTREAMSTAT:
		/* does nothing for hardware dag driver */
		break;
	default:
		error = -ENOTTY;
		break;
	}

	//restore the original task state 
	set_current_state(tmp_state);
	
	up(&sc->lock_sem);
	return error;
}

#ifdef HAVE_VM_INSERT_PAGE
static int dag_remap_pfn_range(struct vm_area_struct * vma, unsigned long start, unsigned long pfn, unsigned long size, pgprot_t prot);
#endif

static int
dag_mmap(struct file *fp, struct vm_area_struct * vma)
{
	dag_softc_t	*sc = fp->private_data;
	loff_t	off, physical, vsize, psize;

	off = (off_t)vma->vm_pgoff << PAGE_SHIFT;
	vsize = vma->vm_end - vma->vm_start;

	if(!sc->phys_addr) {
		DAG_ERROR("dag%u: mmap: no memory assigned to this dag\n", sc->unit);
		return -ENOMEM;
	}
	physical = sc->phys_addr + off;
	psize = sc->pmem_size - off;
	
	if (off & (PAGE_SIZE-1))
		return -ENXIO; /* needs aligned offsets */
	if (vsize > psize)
		return -EINVAL; /* spans too high */

	/*
	 * Don't dump addresses that are not real memory to a core file.
	 */
	if (off >= __pa(high_memory) || (fp->f_flags & O_SYNC))
		vma->vm_flags |= VM_IO;

	vma->vm_flags |= VM_RESERVED;
#if defined(CONFIG_HUGETLBFS) && defined(AS_HUGETLB)
	if (sc->host_file) {
		vma->vm_flags |= VM_NORESERVE;
		return sc->host_file->f_op->mmap(sc->host_file, vma);
	}
#endif

	DAG_DEBUG("dag_mmap: phys 0x%llx high_mem 0x%lx pfn 0x%llx valid %d\n",
	    physical, __pa(high_memory), physical >> PAGE_SHIFT, pfn_valid(physical >> PAGE_SHIFT));

	if(!pfn_valid(physical>>PAGE_SHIFT)) {
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,3) && !defined(tlb_vma)
		if (remap_page_range(vma->vm_start, physical, vsize, vma->vm_page_prot)) {
#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,11)
			
		if (remap_page_range(vma, vma->vm_start, physical, vsize, vma->vm_page_prot)) {
#else
		if (io_remap_pfn_range(vma, vma->vm_start, physical >> PAGE_SHIFT, vsize, vma->vm_page_prot)) {
#endif
			DAG_ERROR("dag%u: Failed remap!\n", sc->unit);
			return -EAGAIN;
		}		
		return 0;
	}

	/* do the remap */
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,3) && !defined(tlb_vma)
	if (remap_page_range(vma->vm_start, physical, vsize, vma->vm_page_prot)) {
#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,11)
	if (remap_page_range(vma, vma->vm_start, physical, vsize, vma->vm_page_prot)) {
#else
#  ifdef HAVE_VM_INSERT_PAGE
	if (dag_remap_pfn_range(vma, vma->vm_start, physical >> PAGE_SHIFT, vsize, vma->vm_page_prot)) {
#  else
	if (remap_pfn_range(vma, vma->vm_start, physical >> PAGE_SHIFT, vsize, vma->vm_page_prot)) {
#  endif
#endif
		DAG_ERROR("dag%u: Failed remap!\n", sc->unit);
		return -EAGAIN;
	}
	return 0;
}

#ifdef HAVE_VM_INSERT_PAGE
/**<dag_remap_pfn_range> Use vm_insert_page to map all dag memories to user space page by page.
 * This is due to a kernel change in vm between 2.6.15 and 2.6.16.
 * The new remap_pfn_range function set a new flag VM_PFNMAP in the vma, and get_user_page will 
 * fail on such vma.
 * This will lead to a failure in O_DIRECT write issued directly from the memory hole.
 * Gdb will fail too on displaying memory on the memory hole.
 * Using the new vm_insert_page cures all these.
 * The new function requires the page to be allocated in order 0 only. It does this by checking the count of the page struct is not zero.
 * And dagmem are allocating at a very high order (typically 10). We cheat vm_insert_page by manually setting the count of each page
 * struct to 1 (except for 1st page of each order) in the init routine of dagmem.ko. All the page counts are set back to oringinal value
 * when dagmem is unloaded. See function dagmem_mark_ptable/dagmem_unmark_ptable.
 * jeff 2006-11-30
 */
static int dag_remap_pfn_range(struct vm_area_struct * vma, unsigned long start, unsigned long pfn, unsigned long size, pgprot_t prot)
{
	unsigned long left_size, curr_start;
	int ret;
	struct page* 	p_page;
	
	left_size = PAGE_ALIGN(size);
	curr_start = start;
	vma->vm_page_prot = prot;

	while(size>0)
	{
		p_page = pfn_to_page(pfn);
		get_page(p_page); /* increment page count */
		ret = vm_insert_page(vma, start, p_page);
		if(ret)
		{
			DAG_ERROR("mmap failed %ld, ret :%d\n",pfn,ret);
			return -EAGAIN;
		}
		pfn++;
		size -=PAGE_SIZE;
		start +=PAGE_SIZE;
	}
	return 0;
}
#endif


static int
dag_iom_mmap(struct file *fp, struct vm_area_struct * vma)
{
	dag_softc_t	*sc = fp->private_data;
	loff_t		off, physical, vsize, psize,physical2;

	off = (off_t)vma->vm_pgoff << PAGE_SHIFT;
	vsize = vma->vm_end - vma->vm_start;

	if(!sc->mmio_size) {
		DAG_ERROR("dag%u: mmap: no IO memory assigned to this dag\n", sc->unit);
		return -ENOMEM;
	}
	physical2 = pci_resource_start(sc->device, 0) + off;
	physical = (uint32_t)physical2;
	psize = pci_resource_len(sc->device, 0) - off;
	
	if (off & (PAGE_SIZE-1))
		return -ENXIO; /* needs aligned offsets */
	if (vsize > psize)
		return -EINVAL; /* spans too high */
	
        /*
	 * Don't dump addresses that are not real memory to a core file.
	 */
	vma->vm_flags |= VM_IO;

	vma->vm_flags |= VM_RESERVED;
	DAG_DEBUG("iommap: phys 0x%llx,physorg %llx, size %llx\n",physical,physical2, vsize);
	/* do the remap */
#ifdef pgprot_noncached
	vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
#endif
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,3) && !defined(tlb_vma)
	if (remap_page_range(vma->vm_start, physical, vsize, vma->vm_page_prot)) {
#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,11)

	if (remap_page_range(vma, vma->vm_start, physical, vsize, vma->vm_page_prot)) {
#else
	if (io_remap_pfn_range(vma, vma->vm_start, physical >> PAGE_SHIFT, vsize, vma->vm_page_prot)) {
#endif
		DAG_ERROR("dag%u: Failed remap!\n", sc->unit);
		return -EAGAIN;
	}

	return 0;
}

int __devinit
dag_init_one(struct pci_dev *dev, const struct pci_device_id *ent)
{
	int		rc;
	int		unit, error;
	dag_softc_t	*sc=NULL;
	unsigned long	iom;
	uint32_t	iom_size;
	size_t		pmem_size;
	char		rev;
	int		c;
	uint8_t		lat;
	int		dev_cap_offset = 0;
	int		return_value;
#if 0
	int 	itest;
volatile uint64_t a,b;
#endif
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
	char		devname[8];
#endif

	DAG_DEBUG("dag: dag_init_one entry\n");

	unit = dag_find_unattached();
	DAG_DEBUG("dag: discovered unit %d\n", unit);

	if (unit < 0) {
		DAG_ERROR("Too many DAG devices loaded (>%d)\n", DAG_MAX_BOARDS);
		return -ENOMEM;
	}
	sc = &dag_array[unit];
	sc->unit = unit;
	sc->flags |= DAGF_ATTACHED;
	sc->device = dev;
	return_value = pci_read_config_byte(sc->device, PCI_CLASS_REVISION,&sc->pciconfig.brd_rev);	/**read in the board revision ID*/
	if(return_value != 0) {
		DAG_ERROR("dag%d: %s line %d: pci_read_config_word returned an error: %d\n", sc->unit, __FUNCTION__, __LINE__, return_value);
		return return_value;
	}


#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32)
	dev_set_drvdata(&dev->dev, sc);
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
	dev->dev.driver_data = sc;
#else
	dev->driver_data = sc;
#endif
	for(c=0;c<DAG_STREAM_MAX;c++) {
		sc->stream_lock[c].pid=0;
		sc->stream_lock[c].tgid=0;
		sc->stream_lock[c].locked_file=0;
	}
	sema_init(&sc->lock_sem, 1);

	sc->duck = duck_new();

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26)
        /* Disable ASPM features for DAG cards */
	pci_disable_link_state(dev, PCIE_LINK_STATE_L0S |
                               PCIE_LINK_STATE_L1 | PCIE_LINK_STATE_CLKPM);	
#endif

	rc = pci_enable_device(dev);
	if (rc) return rc;
	DAG_DEBUG("dag%d: device enabled\n", unit);

	pci_set_master(dev); /* returns void */
	DAG_DEBUG("dag%d: master enabled\n", unit);

	pci_read_config_byte(dev, PCI_LATENCY_TIMER, &lat);
	if (lat < DAG_MIN_LATENCY) {
		DAG_INFO("dag%d: Setting latency timer to %d\n", unit, DAG_MIN_LATENCY);
		return_value = pci_write_config_byte(dev, PCI_LATENCY_TIMER, 64);
		if(return_value != 0) {
			DAG_ERROR("dag%d: %s line %d: pci_write_config_byte returned an error: %d\n", sc->unit, __FUNCTION__, __LINE__, return_value);
			return return_value;
		}
	}
	
	/* Save PCI Regs for later resets/reloads */
  	return_value = dag_pciconfig_save(sc);
	if(return_value != 0) {
		DAG_ERROR("dag%d: %s line %d: dag_pciconfig_save returned an error: %d\n", sc->unit, __FUNCTION__, __LINE__, return_value);
		return return_value;
	}
	DAG_DEBUG("dag%d: saved pci config\n", unit);

  	/**
	* if dev_cap_offset != -1 it is a pci-e card.can issue a link retrain.we use card rather than the parent as pci-X slot may have PCI-e  functionality.
	*/
	if ((dev_cap_offset = pci_find_capability(dev, PCI_CAP_ID_EXP))) {
		dag_retrain_link(sc);
		return_value = dag_pciconfig_restore(sc);
	}
	if(dev_cap_offset != -1) 
	{
		dag_retrain_link(sc);
		return_value = dag_pciconfig_restore(sc);
		if(return_value != 0) {
			DAG_ERROR("dag%d: %s line %d: dag_pciconfig_restore returned an error: %d\n", sc->unit, __FUNCTION__, __LINE__, return_value);
			return return_value;
		}
   	}
#ifdef HAVE_PCI_SET_MWI
	DAG_DEBUG("dag%d: setting mwi\n", unit);
	rc = pci_set_mwi(dev);
	if (rc) {
		/* This isn't a reason to fail the pci initialization, just output a notification */
		DAG_INFO("dag%d: mwi disabled\n", unit);
	} else {
		DAG_DEBUG("dag%d: mwi enabled\n", unit);
	}
#endif

	/* PCI IO memory */
	iom = pci_resource_start(dev, 0);
	iom_size = pci_resource_len(dev, 0);

	if(check_mem_region(iom, iom_size)) {
		DAG_ERROR("dag%d: Fatal - IO memory 0x%08lx already in use\n", unit, iom);
		return -EBUSY;
	}

	request_mem_region(iom, iom_size, "dag");
	DAG_DEBUG("dag%d: iom requested 0x%08lx\n", unit, iom);

	sc->mmio = ioremap_nocache(iom, iom_size);
	sc->mmio_size = iom_size;
	DAG_DEBUG("dag%d: iom mmapped 0x%08lx\n", unit, (long)sc->mmio);

	/* Sanity check, attempt to read from enumeration table base */
	if ((dag_ioread(DAG_REG_BASE)&0xffff) != DAG_REG_ADDR_START) {
		/* Could be 2.4.x card */
		if ((dag_ioread(CAPS_BASE) & CAPS_MAGIC_MASK) != CAPS_MAGIC) {
			DAG_ERROR("dag%d: Fatal - Could not access DAG card. If the DAG card is a PCI-X card, check it is in a PCI-X slot, motherboard jumpers and BIOS are set to PCI-X for the slot, and that no PCI cards share the bus.\n", unit);
			iounmap(sc->mmio);
			release_mem_region(pci_resource_start(dev,0), pci_resource_len(dev,0));
			return -EIO;
		}
	}
#if 0 
	for(itest=0; itest <0x1000; itest+=8) {
		DAG_DEBUG("dag%d: test access the ENUM TABLE(0x%04x)= 0x%08lx, 0x%08lx\n",unit , itest, 
			*(volatile uint32_t*)(sc->mmio + itest),*(volatile uint32_t*)(sc->mmio + itest+4));
	}
#endif


	/* Large PC Memory buffer */
	pmem_size = 0;
	if(pci_resource_len(dev, 1) >= 0x100000) {
		DAG_DEBUG("dag%d: appears virtual\n", unit);
		/* If bar1 exists, may be vdag/dock */
		sc->phys_addr = pci_resource_start(dev, 1);
		/* sanity check */
		/* Read PBM stream_counts register at 0x200c, check that number of rxstreams is non-zero. This guarantees that stream0 base at 0x2040 is valid. Does not check PBM version or location (assumes PBM v3/MkIV at 0x2000). */
		if(readl(sc->mmio+0x200c) & 0xffff ) {
			sc->bus_addr =
				readl(sc->mmio+0x2040);
			if (sc->bus_addr) {
				pmem_size = pci_resource_len(dev, 1);
				request_mem_region(pci_resource_start(dev, 1), pci_resource_len(dev, 1), "dag");
				sc->pci_bar_memory = 1;
				DAG_INFO("dag%d: Found 0x%x (%uMB) at BUS 0x%08lx PHYS 0x%08lx\n", unit, (unsigned int)pmem_size, (unsigned int)pmem_size >> 20, (unsigned long)sc->bus_addr, (unsigned long)sc->phys_addr);
			} else {
				DAG_ERROR("dag%d: PCI BAR 1 found, but PBM not configured.\n", unit);
				sc->phys_addr = 0;
			}
		} else {
			DAG_ERROR("dag%d: PCI BAR 1 found, but no PBM found\n", unit);
			sc->phys_addr = 0;
		}
	} else {
		if(!(sc->bus_addr = (unsigned long)dagmem_malloc(&sc->phys_addr, &pmem_size, 0, unit))) {
			/*DAG_ERROR("dag%d: Fatal - dagmem_malloc failed\n", unit);
			return -ENOMEM;*/
			pmem_size = 0;
			DAG_DEBUG("dag%d: has been allocated zero memory\n", unit);
		}
	}
	sc->pmem_size = pmem_size;
	DAG_DEBUG("dag%d: stream buffer memory allocated bus: 0x%08llx phys: 0x%08llx size: %u\n", unit, sc->bus_addr, sc->phys_addr, sc->pmem_size);

	/* IRQ line and handler */
	if (dev->irq) {
#ifndef IRQF_SHARED
//#warning "using SA_SHIRQ"
#define IRQF_SHARED SA_SHIRQ
#else 
//#warning "using IRQF_SHARED"
#endif 
		msi_enabled=0;
		
		if(msi_use) {
			
#ifdef CONFIG_PCI_MSI
			if (pci_find_capability(dev, PCI_CAP_ID_MSI)) {
				if (!pci_enable_msi(dev)) {
					DAG_DEBUG("dag%d: MSI IRQ %d \n",unit, dev->irq);
					msi_enabled = 1;
				} else {
					DAG_ERROR("dag%d: MSI init failed, using old style IRQ\n", unit);
				}
			}
#endif
		} else {
			DAG_DEBUG("dag%d: Using standard IRQ\n", unit);
		}

		error = request_irq(dev->irq, dag_interrupt,
				    IRQF_SHARED, "dag", sc);
		if (error < 0) {
			DAG_ERROR("dag%d: Fatal - IRQ request Error %d: ", unit, error);
			if (error == -EBUSY)
				DAG_ERROR("IRQ Busy %d\n", dev->irq);
			if (error == -EINVAL)
				DAG_ERROR("Bad irq number or handler %d\n", dev->irq);
			iounmap(sc->mmio);
			release_mem_region(pci_resource_start(dev,0), pci_resource_len(dev,0));
			return error;
		}
		sc->irq = dev->irq;
		DAG_DEBUG("dag%d: irq mapped %d\n", unit, sc->irq);
	} else {
		DAG_DEBUG("dag%d: no irq allocated\n",
		       unit);
		sc->irq = 0;
	}
	
	udelay(10000);
	udelay(10000);

#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
	/* create devfs entries */
	sprintf(devname, "dag%i", unit);
	sc->devfs_dag=devfs_register(NULL, devname,
					  DEVFS_FL_AUTO_DEVNUM | DEVFS_FL_DEFAULT,
					  0,0, S_IFCHR | S_IRUGO | S_IWUGO,
					  dag_fops_array[DAGMINOR_DAG], sc);
	if(sc->devfs_dag<0) {
		DAG_ERROR("dag%d: devfs %s register failed (%p)\n",
		       unit, devname, sc->devfs_dag);
	}

	sprintf(devname, "dagmem%i", unit);
	sc->devfs_mem=devfs_register(NULL, devname,
					  DEVFS_FL_AUTO_DEVNUM | DEVFS_FL_DEFAULT,
					  0,0, S_IFCHR | S_IRUGO | S_IWUGO,
					  dag_fops_array[DAGMINOR_MEM], sc);
	if(sc->devfs_mem<0) {
		DAG_ERROR("dag%d: devfs %s register failed (%p)\n",
		       unit, devname, sc->devfs_mem);
	}

	sprintf(devname, "dagiom%i", unit);
	sc->devfs_iom=devfs_register(NULL, devname,
					  DEVFS_FL_AUTO_DEVNUM | DEVFS_FL_DEFAULT,
					  0,0, S_IFCHR | S_IRUGO | S_IWUGO,
					  dag_fops_array[DAGMINOR_IOM], sc);
	if(sc->devfs_iom<0) {
		DAG_ERROR("dag%d: devfs %s register failed (%p)\n",
		       unit, devname, sc->devfs_iom);
	}

	sprintf(devname, "dagarm%i", unit);
	sc->devfs_arm=devfs_register(NULL, devname,
					  DEVFS_FL_AUTO_DEVNUM | DEVFS_FL_DEFAULT,
					  0,0, S_IFCHR | S_IRUGO | S_IWUGO,
					  dag_fops_array[DAGMINOR_ARM], sc);
	if(sc->devfs_arm<0) {
		DAG_ERROR("dag%d: devfs %s register failed (%p)\n",
		       unit, devname, sc->devfs_arm);
	}
	DAG_DEBUG("dag%d: devfs done\n", unit);
#endif

	/* Save PCI Regs for later resets/reloads */
	return_value = dag_pciconfig_save(sc);
	if(return_value != 0) {
		DAG_ERROR("dag%d: %s line %d: dag_pciconfig_save returned an error: %d\n", sc->unit, __FUNCTION__, __LINE__, return_value);
		iounmap(sc->mmio);
		release_mem_region(pci_resource_start(dev,0), pci_resource_len(dev,0));
		return return_value;
	}
	DAG_DEBUG("dag%d: saved pci config\n", unit);

	return_value = pci_read_config_byte(dev, PCI_REVISION_ID, &rev);
	if(return_value != 0) {
		DAG_ERROR("dag%d: %s line %d: pci_read_config_byte returned an error: %d\n", sc->unit, __FUNCTION__, __LINE__, return_value);
		iounmap(sc->mmio);
		release_mem_region(pci_resource_start(dev,0), pci_resource_len(dev,0));
		return return_value;
	}
	
	/* Success: Inform the user */
	DAG_INFO("dag%d: %s Rev %d at 0x%p irq %d buf %luMB\n",
	       unit, dag_device_name(dev->device, 1), rev, (int*)iom, sc->irq, (unsigned long)pmem_size>>20);

	duck_init(sc);
	dagmon_init(sc);

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
	cdev_init(&sc->cdev_dag, dag_fops_array[DAGMINOR_DAG]);
       	sc->cdev_dag.owner = THIS_MODULE;
	kobject_set_name(&sc->cdev_dag.kobj, "dag%d", unit);

	cdev_init(&sc->cdev_mem, dag_fops_array[DAGMINOR_MEM]);
       	sc->cdev_mem.owner = THIS_MODULE;
	kobject_set_name(&sc->cdev_mem.kobj, "dagmem%d", unit);

	cdev_init(&sc->cdev_iom, dag_fops_array[DAGMINOR_IOM]);
       	sc->cdev_iom.owner = THIS_MODULE;
	kobject_set_name(&sc->cdev_iom.kobj, "dagiom%d", unit);

	cdev_init(&sc->cdev_arm, dag_fops_array[DAGMINOR_ARM]);
       	sc->cdev_arm.owner = THIS_MODULE;
	kobject_set_name(&sc->cdev_arm.kobj, "dagarm%d", unit);

	if((error=cdev_add(&sc->cdev_dag, dag_dev|(DAGMINOR_DAG*DAG_MAX_BOARDS+unit), 1))) {
		DAG_ERROR("dag%d: cdev dag register failed (%d)\n",
		       unit, error);		
	}
	DAG_DEBUG("Added dag%d mtype %d major %d minor %d dev_t 0x%08x\n", unit, DAGMINOR_DAG, dag_dev, DAGMINOR_DAG*DAG_MAX_BOARDS+unit, dag_dev|(DAGMINOR_DAG*DAG_MAX_BOARDS+unit));

	if((error=cdev_add(&sc->cdev_mem, dag_dev|(DAGMINOR_MEM*DAG_MAX_BOARDS+unit), 1))) {
		DAG_ERROR("dag%d: cdev dagmem register failed (%d)\n",
		       unit, error);		
	}
	DAG_DEBUG("Added dag%d mtype %d minor %d dev_t 0x%08x\n", unit, DAGMINOR_MEM, DAGMINOR_MEM*DAG_MAX_BOARDS+unit, dag_dev|(DAGMINOR_MEM*DAG_MAX_BOARDS+unit));

	if((error=cdev_add(&sc->cdev_iom, dag_dev|(DAGMINOR_IOM*DAG_MAX_BOARDS+unit), 1))) {
		DAG_ERROR("dag%d: cdev dagiom register failed (%d)\n",
		       unit, error);		
	}
	DAG_DEBUG("Added dag%d mtype %d minor %d dev_t 0x%08x\n", unit, DAGMINOR_IOM, DAGMINOR_IOM*DAG_MAX_BOARDS+unit, dag_dev|(DAGMINOR_IOM*DAG_MAX_BOARDS+unit));

	if((error=cdev_add(&sc->cdev_arm, dag_dev|(DAGMINOR_ARM*DAG_MAX_BOARDS+unit), 1))) {
		DAG_ERROR("dag%d: cdev dagarm register failed (%d)\n",
		       unit, error);		
	}
	DAG_DEBUG("Added dag%d mtype %d minor %d dev_t 0x%08x\n", unit, DAGMINOR_ARM, DAGMINOR_ARM*DAG_MAX_BOARDS+unit, dag_dev|(DAGMINOR_ARM*DAG_MAX_BOARDS+unit));

#endif
	if(sc->irq) {
		dag_iowrite(0xffff, IntEn);
		DAG_DEBUG("dag%d: irqs enabled\n", unit);
	}
	
	return 0;
}

void __devexit
dag_remove_one(struct pci_dev *dev)
{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32)
	dag_softc_t *sc = dev_get_drvdata(&dev->dev);
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
	dag_softc_t *sc = dev->dev.driver_data;
#else
	dag_softc_t *sc = dev->driver_data;	
#endif
	int unit;

	if(!sc)	/* shouldn't be possible, paranoia */
		return;

	unit = sc->unit;

	/* IRQ For some cards reboot has to disable the interrupts first */
	
	if(sc->irq) {
		dag_iowrite(0x0, IntEn);
		//delay required for PCI Express due from the time of diable to the time free the handlere there may be a oustanding IRQ message 
		udelay(10000);
		udelay(10000);
		
		free_irq(sc->irq, sc);
#ifdef CONFIG_PCI_MSI
        if (msi_enabled) {
                pci_disable_msi(dev);
	}
#endif /* CONFIG_PCI_MSI */

	}

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
	cdev_del(&sc->cdev_dag);

	cdev_del(&sc->cdev_mem);

	cdev_del(&sc->cdev_iom);

	cdev_del(&sc->cdev_arm);

#else
	/* devfs entries */
	if(sc->devfs_dag)
		devfs_unregister(sc->devfs_dag);

	if(sc->devfs_mem)
		devfs_unregister(sc->devfs_mem);

	if(sc->devfs_iom)
		devfs_unregister(sc->devfs_iom);

	if(sc->devfs_arm)
		devfs_unregister(sc->devfs_arm);
#endif


	/* Destroy duck after irq disabled */
	if(sc->duck) {
		duck_destroy(sc);
	}
#if defined(CONFIG_HUGETLBFS) && defined(AS_HUGETLB)
	if (sc->host_file) {
		invalidate_inode_pages(sc->host_file->f_mapping);
		fput(sc->host_file);
	}
#endif
	/* Large PC buffer */
	if(sc->bus_addr) {
		if(sc->pci_bar_memory) {
			release_mem_region(pci_resource_start(dev,1), pci_resource_len(dev,1));
		} else {
			dagmem_free(sc->bus_addr);
		}
	}

	/* PCI IO memory */
	if(sc->mmio) {
		iounmap(sc->mmio);
		release_mem_region(pci_resource_start(dev,0), pci_resource_len(dev,0));
	}

	/* clear stored parameters */
	memset(sc, 0, sizeof(dag_softc_t));

	DAG_INFO("dag%d: removed\n", unit);
}

struct file_operations dag_fops = {
	/* dag entry */
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
	owner:  THIS_MODULE,
#endif
	read:	dag_mem_read, /* use mem entry for now */
	write:	dag_mem_write,/* use mem entry for now */
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34)
	unlocked_ioctl:	dag_unlocked_ioctl,
#else
	ioctl:	dag_ioctl,
#endif
	mmap:	dag_mmap,
	open:	dag_open,
	release: dag_release,
};

struct file_operations dag_mem_fops = {
	/* mem entry */
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
	owner:  THIS_MODULE,
#endif
	read:	dag_mem_read,
	write:	dag_mem_write,
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34)
	unlocked_ioctl:	dag_unlocked_ioctl,
#else
	ioctl:	dag_ioctl,
#endif
	mmap:	dag_mmap,
	open:	dag_open,
	release: dag_release,
};

struct file_operations dag_iom_fops = {
	/* iom entry */
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
	owner:  THIS_MODULE,
#endif
	read:	dag_iom_read,
	write:	dag_iom_write,
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34)
	unlocked_ioctl:	dag_unlocked_ioctl,
#else
	ioctl:	dag_ioctl,
#endif
	mmap:	dag_iom_mmap,
	open:	dag_open,
	release: dag_release
};

struct file_operations dag_arm_fops = {
	/* arm entry */
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
	owner:  THIS_MODULE,
#endif
	read:	dag_arm_read,
	write:	dag_arm_write,
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34)
	unlocked_ioctl:	dag_unlocked_ioctl,
#else
	ioctl:	dag_ioctl,
#endif
	open:	dag_open,
	release: dag_release
};

static struct pci_driver dag_pci_driver = {
       name:           "dag",
       probe:          dag_init_one,
       remove:	       __devexit_p(dag_remove_one),
       id_table:       dag_pci_tbl,
};

void
dag_cleanup(void)
{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
	if(dag_dev)
		unregister_chrdev_region(dag_dev, DAG_MAX_BOARDS*DAGMINOR_MAX);
#else
	if (dag_major)
		devfs_unregister_chrdev(dag_major, "dag");
#endif
	pci_unregister_driver(&dag_pci_driver);
}

int __init
dag_init(void)
{
	int	err=0;

#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
	SET_MODULE_OWNER(&dag_fops);
	SET_MODULE_OWNER(&dag_mem_fops);
	SET_MODULE_OWNER(&dag_iom_fops);
	SET_MODULE_OWNER(&dag_arm_fops);
#endif
	DAG_INFO("dag: Version %s\n", kDagReleaseVersion);

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
	if (alloc_chrdev_region( &dag_dev, 0, DAG_MAX_BOARDS*DAGMINOR_MAX, "dag" ) < 0) {
		DAG_ERROR("dag: Couldn't get major error %d\n", err);
		goto fail_ch;
	}

	DAG_DEBUG("Got major %d\n", dag_dev);

#endif
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22)
	err = pci_module_init(&dag_pci_driver);
#else 
        err = pci_register_driver(&dag_pci_driver);
#endif 
	if (err) {
		DAG_ERROR("dag: Init failure %d\n", err);
		goto fail_pci;
	}

#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
	/* request a major, does nothing if devfs is used */
	if ((dag_major = devfs_register_chrdev(0, "dag", &dag_fops)) < 0) {
		DAG_ERROR("dag: Couldn't get major error %d\n", err);
		goto fail_ch;
	}
#endif
	return 0;

 fail_ch:
	dag_cleanup();
 fail_pci:
	return err;
}

MODULE_AUTHOR("DAG Team <support@endace.com>");
MODULE_DESCRIPTION("device driver module for Endace Measurement Systems DAG network capture cards");
MODULE_SUPPORTED_DEVICE("dag");
MODULE_LICENSE("Proprietary Endace Measurement Systems Ltd");

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,4)
MODULE_VERSION(PACKAGE_VERSION);
#endif

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
 module_param(msi_use, int, 0);
 MODULE_PARM_DESC(msi_use, "Specifies if the DAG cards will try to use MSI interrupts. Default 0 means disabled ");
#endif 


module_init(dag_init);
module_exit(dag_cleanup);
