/*
 * Copyright (c) 2002-2006 Endace Measurement Systems Ltd, Hamilton, New Zealand.
 * All rights reserved.
 *
 * This source code is proprietary to Endace Measurement Systems and no part
 * of it may be redistributed, published or disclosed except as outlined in
 * the written contract supplied with this product.
 *
 * $Id: dag.c 15610 2012-04-09 21:26:51Z sfd $
 */

/* FreeBSD headers. */
#include <sys/types.h>
#include <sys/module.h>
#include <sys/systm.h>
#include <sys/errno.h>
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/conf.h>
#include <sys/uio.h>
#include <sys/malloc.h>
#include <sys/mman.h>
#include <sys/proc.h>
#include <sys/ioccom.h>

#if __FreeBSD_version < 600000
#include <machine/bus_memio.h>
#include <machine/bus_pio.h>
#endif
#include <machine/bus.h>
#include <machine/resource.h>
#include <machine/stdarg.h>
#include <machine/clock.h>
#include <machine/md_var.h>

#include <vm/vm.h>
#include <vm/pmap.h>

#include <sys/bus.h>
#define __RMAN_RESOURCE_VISIBLE
#include <sys/rman.h>

#if __FreeBSD_version > 500000
#include	<dev/pci/pcivar.h>
#include	<dev/pci/pcireg.h>
#else
#include <pci/pcivar.h>
#include <pci/pcireg.h>
#endif

/* Endace headers. */
#include "dagdebug.h"
#include "dagnew.h"
#include "dagdrv.h"
#include "dagmem.h"
#include "dagduck.h"
#include "dagmon.h"
#include "dagreg.h"
#include "dagpci.h"  

/* Driver status. */
#if __FreeBSD_version > 800000
#define DAGUNIT(X)      (dev2unit(X)>>DAGMINORBITS)
#define DAGMINOR(X)     (dev2unit(X)&DAGMINORMASK)
#else
 #define DAGUNIT(X)      (minor(X)>>DAGMINORBITS)
 #define DAGMINOR(X)     (minor(X)&DAGMINORMASK)
#endif
#define DAGDEVICE(U,M)  (((U)<<DAGMINORBITS)|(M))

#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)

static devclass_t dag_devclass;

enum
{
	DAGF_ARM = 0x0001,
	DAGF_PBM = 0x0002,
	DAGF_GPP = 0x0004
};

/* System call entry point declarations. */
d_open_t    dag_open;
d_close_t   dag_close;
d_ioctl_t   dag_ioctl;
d_read_t    dag_read;
d_write_t   dag_write;
d_poll_t    dag_poll;
d_mmap_t    dag_mmap;

/* Interrupt entry declaration. */
driver_intr_t dag_intr;

#if __FreeBSD_version < 700000
/* Access macros. */
#define	ToHDB	(*(unsigned int *)((u_char *)((struct dag_softc *)sc)->iomres->r_virtual + 0x80))
#define	IntEn	(*(unsigned int *)((u_char *)((struct dag_softc *)sc)->iomres->r_virtual + 0x8c))
#define	Ctrl	(*(unsigned int *)((u_char *)((struct dag_softc *)sc)->iomres->r_virtual + 0x88))

#define	pbmwptr	(*(unsigned int *)((u_char *)((struct dag_softc *)sc)->iomres->r_virtual + 0x128))

#define	dag_ioread(X) (*(unsigned int *)((u_char *)((struct dag_softc *)sc)->iomres->r_virtual + (X)))
#else
/* Access macros. */
#define ToHDB   (*(unsigned int *)((u_char *)rman_get_virtual(((struct dag_softc *)sc)->iomres) + 0x80))
#define IntEn   (*(unsigned int *)((u_char *)rman_get_virtual(((struct dag_softc *)sc)->iomres) + 0x8c))
#define Ctrl    (*(unsigned int *)((u_char *)rman_get_virtual(((struct dag_softc *)sc)->iomres) + 0x88))


#define pbmwptr (*(unsigned int *)((u_char *)rman_get_virtual(((struct dag_softc *)sc)->iomres) + 0x128))

#define dag_ioread(X) (*(unsigned int *)((u_char *)rman_get_virtual(((struct dag_softc *)sc)->iomres) + (X))) 
#endif
 
static u_long dagmem_size = DAGMEM_DEFAULT_SIZE;

/* Add macro that is only defined in FreeBSD 4.9 and higher. 
 * This should enable dag.c to compile on earlier FreeBSD systems.
 */
#ifndef PCIR_BAR
#define PCIR_BAR(x) (PCIR_MAPS + (x) * 4)
#endif /* PCIR_BAR */


/* PCI configuration save/restore, as configured by BIOS on system startup. */
static void
dag_pciconfig_save(dag_softc_t * sc)
{
	sc->pciconfig.ba0 = pci_read_config(sc->device, PCIR_BAR(0), 4);
	sc->pciconfig.ba1 = pci_read_config(sc->device, PCIR_BAR(1), 4);
	sc->pciconfig.ba2 = pci_read_config(sc->device, PCIR_BAR(2), 4);
	sc->pciconfig.com = pci_read_config(sc->device, PCIR_COMMAND, 4);
	sc->pciconfig.lin = pci_read_config(sc->device, PCIR_INTLINE, 1);
	sc->pciconfig.lat = pci_read_config(sc->device, PCIR_LATTIMER, 1);

	DD(("dag: ba0 0x%x\n", sc->pciconfig.ba0));
	DD(("dag: ba1 0x%x\n", sc->pciconfig.ba1));
	DD(("dag: ba2 0x%x\n", sc->pciconfig.ba2));
	DD(("dag: com 0x%x\n", sc->pciconfig.com));
	DD(("dag: lin 0x%x\n", sc->pciconfig.lin));
	DD(("dag: lat 0x%x\n", sc->pciconfig.lat));
}

int
dag_pciconfig_restore(dag_softc_t * sc)
{
	int error = 0;
	int loop;

	/* Waiting for the Xilinx chip to become accessible. */
	for (loop = 0; loop < 10*1000; loop++)
	{
		if( (pci_read_config(sc->device, PCIR_BAR(0), 4) == 0) | (pci_read_config(sc->device, PCIR_BAR(0), 4) != 0xffffffff ) ) 
			goto ready;
		
		error = tsleep(& sc->pciconfig.ba0, (PUSER|PCATCH), "dagpciconfig", hz/100);
		if (EAGAIN != error)
		{
			return error;
		}
	}
	
	DA(("dag: Xilinx not accessible for pciconfig restore\n"));
	return EIO;
	
ready:
	pci_write_config(sc->device, PCIR_BAR(0), sc->pciconfig.ba0, 4);
	pci_write_config(sc->device, PCIR_BAR(1), sc->pciconfig.ba1, 4);
	pci_write_config(sc->device, PCIR_BAR(2), sc->pciconfig.ba2, 4);
	pci_write_config(sc->device, PCIR_COMMAND, sc->pciconfig.com, 4);
	pci_write_config(sc->device, PCIR_INTLINE, sc->pciconfig.lin, 1);
	pci_write_config(sc->device, PCIR_LATTIMER, sc->pciconfig.lat, 1);
	IntEn = 0xffff;

	return 0;
}
#if 0 
static int
dag_reset_dave(dag_softc_t *sc)
{
	//struct pci_dev *parent_dev_p; 
	device_t parent_dev_p;
	
	/* get a device pointer to the parent device */
	parent_dev_p = device_get_parent(sc->device);
	//parent_dev_p =        sc->device->bus->self;
	
	/* a bit of debugging output, requires the driver to be built without the NDEBUG option */
	DD (( "dag%d: parent device [%08x]\n", sc->unit, pci_get_devid(parent_dev_p) ) );
	DD ( ("dag%d: parent device [%s]\n", sc->unit, device_get_name(parent_dev_p) ) );
		
	/* sanity check that the if bit 27 (program on next link down) is set, then either bit
	 * 25 (program current half) or bit 24 (program the stable half) are also set.
	 */
		
	/* toggle the hot-burn reset bit in the PCI bridge register (the minimum is 1ms, see Trst in the PCI spec 3.0) */
	//pci_write_config_word (parent_dev_p, 0x3E, 0x0040);
	/* Enable Hot burn Reset  */
//	pci_write_config (parent_dev_p, PCIR_BRIDGECTL_2, 0x0040,2);
	
//	udelay(2000);

	DELAY(10000);
	
	//schedule_timeout(1*HZ+HZ/2);
	//set_current_state(TASK_UNINTERRUPTIBLE);

	
//	pci_write_config_word (parent_dev_p, 0x3E, 0x0000);
	/* Clear hot Burn reset */
	// pci_write_config (parent_dev_p, PCIR_BRIDGECTL_2, 0x0000,2);
	
	return 0;
}
#endif 

static int
dagreset(dag_softc_t * sc)
{
	unsigned int temp;
	int error;

	
	
		/* check if we have the new reprogramming mechanism available and we managed to get our parent bridge device */
	temp = Ctrl;
	
	if ( (temp & NEW_PROGRAMMING_AVAIL_BIT) && (temp & USE_NEW_PROGRAMMING_BIT)  ) {
		/* a bit of debugging output, requires the driver to be built without the NDEBUG option */
		DD ( ("dag%d: New reset mechanism  \n", sc->unit) );
		
	
		/* sanity check that the if bit 27 (program on next link down) is set, then either bit
		 * 25 (program current half) or bit 24 (program the stable half) are also set.
		 */
		if ( (temp & PROG_NEXT_LINKDOWN_BIT) && !(temp & (PROG_STABLE_BIT | PROG_CURRENT_BIT)) )
		{
			temp |= PROG_STABLE_BIT;
			Ctrl = temp;
		};
	        	// temp force to ringlo 
		Ctrl = temp & 0xf3ffffff; 
		//dag_reset_dave(sc);
	} ;
	{
		
		temp = Ctrl;
		Ctrl = (temp | 0x40000000);
		DELAY(10000);
		Ctrl = temp;
	}
	
	
	error = dag_pciconfig_restore(sc);
	if (error)
		return error;

	dagmon_init(sc);
	duck_init(sc);
	
	return 0;
}


#if 0
static int
daghalt(dag_softc_t * sc)
{
	unsigned int temp;

	temp = Ctrl;
	Ctrl = (temp | 0x80000000);
	DELAY(10000);
	Ctrl = temp;
	
	return 0;
}
#endif /* 0 */

/* System call entry point functions.*/
int
#if __FreeBSD_version < 500000
dag_open(dev_t dev, int oflags, int devtype, struct proc *p)
#else
dag_open(struct cdev *dev, int oflags, int devtype, struct thread *th)
#endif
{
	dag_softc_t * sc = devclass_get_softc(dag_devclass, DAGUNIT(dev));
	dag_reg_t result[DAG_REG_MAX_ENTRIES];

//	DD(("dag_open 0x%x minor %u\n", dev->si_udev, minor(dev)));
	if (NULL == sc)
		return ENXIO;
	
	dev->si_drv1 = sc;

	switch (DAGMINOR(dev))
	{
		case DAGMINOR_ARM:
		#if __FreeBSD_version < 700000	
			if (dag_reg_find(sc->iomres->r_virtual, DAG_REG_ARM, result) < 1)
				return ENXIO;
		#else
                       if (dag_reg_find(rman_get_virtual(sc->iomres), DAG_REG_ARM, result) < 1)
                                return ENXIO;

		#endif
			break;
		
		default:
			break;
	}
	
	return 0;
}

int
#if __FreeBSD_version < 500000
dag_close(dev_t dev, int fflag, int devtype, struct proc *p)
#else
dag_close(struct cdev *dev, int fflag, int devtype, struct thread *th)
#endif
{
	dag_softc_t * sc = devclass_get_softc(dag_devclass, DAGUNIT(dev));
	int c;

	DD(("dag_close\n"));

	/*
	 * If the device is locked, and the process releasing the device has the lock,
	 * then it has not exited correctly. Reset the card and clear the lock.
	 */
	for (c = 0; c < DAG_STREAM_MAX; c++)
	{
		#if __FreeBSD_version < 500000
		if ((sc->lock[c].pgid == p->p_pgid) && (DAGMINOR(dev) == DAGMINOR_DAG))
		{
			DA(("dag_close: PID %d (group %d) released unit %u stream %d while still locked, resetting\n ", p->p_pid, p->p_pgid, DAGUNIT(dev), c));
		#else
		if((sc->lock[c].pgid == th->td_proc->p_pgid) && (DAGMINOR(dev) == DAGMINOR_DAG)) {
			DA(("dag_close: PID %d (group %d) released unit %u stream %d while still locked, resetting\n ", th->td_proc->p_pid, th->td_proc->p_pgid, DAGUNIT(dev), c));
		#endif

#if 0
			daghalt(sc); /* XXX How to reset/halt one stream? */
#endif /* 0 */
			sc->lock[c].pid = 0;
			sc->lock[c].pgid = 0;
		}
	}

	return 0;
}

int
#if __FreeBSD_version < 500000
dag_ioctl(dev_t dev, u_long cmd, caddr_t data, int fflag, struct proc *p)
#else
dag_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, struct thread *th)
#endif
{
	dag_softc_t * sc = devclass_get_softc(dag_devclass, DAGUNIT(dev));
	unsigned int * words;
	daginf_t * daginf;
	int * lockp;
	int error = 0;
	duckinf_t * duckinf;
#if 0
	int * secp;
	int loop;
#endif /* 0 */

	DD(("dag_ioctl 0x%08lx inf:0x%08lx\n", cmd, DAGIOCINFO));

	switch (cmd)
	{
		case DAGIOCRESET:
			words = (u_int *) data;
			switch (*words)
			{
				case DAGRESET_FULL:/* reset magic */
					error = dagreset(sc);
					break;
				
				case DAGRESET_REBOOT:
					DA(("dagreset reboot\n"));
					error = dag_pciconfig_restore(sc);
					duck_init(sc);
					break;
				
				case DAGRESET_DUCK:
					duck_init(sc); /* restart duck */
					break;
				
				default:
					/* ignore */
					break;
			}
			break;
		
		case DAGIOCMONITOR:
			words = (u_int *) data;
			error = dagmon(sc, words[0], &words[1]);
			break;

		case DAGIOCINFO:
			daginf = (daginf_t *) data;
			bzero(daginf, sizeof(daginf_t));
			daginf->device_code = pci_get_device(sc->device);
			daginf->phy_addr = sc->bus_addr;
			daginf->buf_size = sc->bufsize;
			daginf->id = sc->unit;
			daginf->iom_size = sc->iomsize;
			break;

		case DAGIOCLOCK:
			lockp = (int *)data;
			if ((*lockp>>16) > DAG_STREAM_MAX)
			{
				error = -EINVAL;
				break;
			}
			
			switch (*lockp&0xffff)
			{
				case 0: /* unlock */
					#if __FreeBSD_version < 500000
					if (p->p_pid == sc->lock[*lockp>>16].pid)
					#else
					if(th->td_proc->p_pid == sc->lock[*lockp>>16].pid)
					#endif
					{
						sc->lock[*lockp>>16].pid = 0;
						sc->lock[*lockp>>16].pgid = 0;
					}
					else
					{
						error = -EACCES;
					}
					break;
				  
		case 1: /* lock */
			if(sc->lock[*lockp>>16].pid == 0) 
			{
				#if __FreeBSD_version < 500000
				sc->lock[*lockp>>16].pid = p->p_pid;
				sc->lock[*lockp>>16].pgid = p->p_pgid;
			} 
			else if (sc->lock[*lockp>>16].pid == p->p_pid)
			{
				/* multiple aquire lock, fine */;
				 #else 
				sc->lock[*lockp>>16].pid = th->td_proc->p_pid;
				sc->lock[*lockp>>16].pgid = th->td_proc->p_pgid;
			} 
			else if (sc->lock[*lockp>>16].pid == th->td_proc->p_pid)
			{
				/* multiple aquire lock, fine */;
				#endif
			}
			else
			{
				error = -EACCES;
			}
			
			break;
				
		default:	/* share a lock already held */
				/* XXX not implemented yet */
			break;

			}
			#if __FreeBSD_version < 500000
			DA(("dag_ioctl: unit %u stream %ld lock now held by %d (group %d), current pid %d (group %d)\n", DAGMINOR(dev), *lockp>>16, sc->lock[*lockp>>16].pid, sc->lock[*lockp>>16].pgid, p->p_pid, p->p_pgid));
			#else
			DN(("dag_ioctl: unit %u stream %ld lock now held by %d (group %d), current pid %d (group %d)\n", DAGMINOR(dev), *lockp>>16, sc->lock[*lockp>>16].pid, sc->lock[*lockp>>16].pgid, th->td_proc->p_pid, th->td_proc->p_pgid));
			#endif
			break;
		
#if 0
		case DAGIOCPPSWAIT:
			secp = (u_int *)data;
			for ( loop = 0 ; loop < 100 ; loop++ )
			{
				error = tsleep(&sc->duck->pulses, (PUSER|PCATCH), "pps_wait", hz);
				if (EAGAIN != error)
				{
					*secp = (u_int)(sc->duck->last & 0xffffffff);
					return error;
				}
			}
			break;
#endif /* 0 */

		case DAGIOCDUCK:
			duckinf = (duckinf_t *)data;
			error = duck_ioctl(sc->duck, duckinf);
			break;
		
		default:
			error = ENOTTY;
			break;
	}
	
	return error;
}

int
#if __FreeBSD_version < 500000
dag_read(dev_t dev, struct uio *uio, int ioflag)
#else
dag_read(struct cdev *dev, struct uio *uio, int ioflag)
#endif
{
	dag_softc_t * sc = dev->si_drv1;
	size_t count, remain;
	unsigned int words[13]; /* for passing ARM data */
	int error = 0;

	DN(("dag_read\n"));

	switch (DAGMINOR(dev))
	{
		case DAGMINOR_DAG: /* currently will behave like dagmem */
		case DAGMINOR_MEM:
			if (uio->uio_offset >= sc->bufsize)
				return 0;
	
			count = uio->uio_resid;
			if ((uio->uio_offset + count) > sc->bufsize)
				count = sc->bufsize - uio->uio_offset;
			if (!sc->guest)
				return uiomove((caddr_t)sc->buf+uio->uio_offset, count, uio);
			else
#if __FreeBSD_version < 700000
				return uiomove((caddr_t)sc->dmares->r_virtual+uio->uio_offset, count, uio);
#else
				return uiomove((caddr_t)rman_get_virtual(sc->dmares)+uio->uio_offset, count, uio);
#endif 

		case DAGMINOR_IOM:
			/*
			 * Misaligned or byte access will return garbage, but
			 * not cause other problems.
			 */
			if (uio->uio_offset >= sc->iomsize)
				return 0;
	
			count = uio->uio_resid;
			if ((uio->uio_offset + count) > sc->iomsize)
				count = sc->iomsize - uio->uio_offset;
#if __FreeBSD_version < 700000
			return uiomove((caddr_t)sc->iomres->r_virtual+uio->uio_offset, count, uio);
#else
			return uiomove((caddr_t)rman_get_virtual(sc->iomres)+uio->uio_offset, count, uio);
#endif 

		case DAGMINOR_ARM:
			/*
			 * XXX what about blocking on ARM busy ???
			 */
			/*
			 * We only support 4-byte aligned word transfers
			 */
			if ((uio->uio_offset & 0x03) || (uio->uio_resid & 0x03))
				return EFAULT;
			
			remain = uio->uio_resid >> 2;
			for (; remain >= 12; remain -= 12)
			{
				words[0] = uio->uio_offset;	/* auto modulo 2^32 */
				error = dagmon(sc, DAGMON_READ_12_WORDS, words);
				if (error)
					return error;
				
				error = uiomove((caddr_t)&words[1], 12*sizeof(unsigned int), uio);
				if (error)
					return error;
			}
			
			for (; remain > 0; remain--)
			{
				words[0] = uio->uio_offset; /* auto modulo 2^32 */
				error = dagmon(sc, DAGMON_READ_WORD, words);
				if (error)
					return error;
				
				error = uiomove((caddr_t)&words[1], sizeof(unsigned int), uio);
				if (error)
					return error;
			}
			return error; /* none */
			
#if 0
		case DAGMINOR_RAM:
#endif /* 0 */

		default:
			break;
	}
	
	return 0;
}

int
#if __FreeBSD_version < 500000
dag_write(dev_t dev, struct uio *uio, int ioflag)
#else
dag_write(struct cdev *dev, struct uio *uio, int ioflag)
#endif
{
	dag_softc_t * sc = dev->si_drv1;
	size_t count;
	size_t remain;
	unsigned int words[13]; /* for passing ARM data */
	int error = 0;

	DN(("dag_write\n"));

	switch (DAGMINOR(dev))
	{
		case DAGMINOR_DAG: /* currently will behave like dagmem */
		case DAGMINOR_MEM:
			if (uio->uio_offset >= sc->bufsize)
				return 0;
			
			count = uio->uio_resid;
			if ((uio->uio_offset + count) > sc->bufsize)
				count = sc->bufsize - uio->uio_offset;
			if (!sc->guest)
				return uiomove((caddr_t)sc->buf+uio->uio_offset, count, uio);
			else
#if __FreeBSD_version < 700000
				return uiomove((caddr_t)sc->dmares->r_virtual+uio->uio_offset, count, uio);
#else 
 				return uiomove((caddr_t)rman_get_virtual(sc->dmares)+uio->uio_offset, count, uio);
#endif 

		case DAGMINOR_IOM:
			/*
			 * Misaligned or byte access will return garbage, but
			 * not cause other problems.
			 */
			if (uio->uio_offset >= sc->iomsize)
				return 0;
	
			count = uio->uio_resid;
			if ((uio->uio_offset + count) > sc->iomsize)
				count = sc->iomsize - uio->uio_offset;
#if __FreeBSD_version < 700000
			return uiomove((caddr_t)sc->iomres->r_virtual+uio->uio_offset, count, uio);
#else 
 			return uiomove((caddr_t)rman_get_virtual(sc->iomres)+uio->uio_offset, count, uio);
#endif 
		case DAGMINOR_ARM:
			/* XXX what about blocking on ARM busy ??? */
			
			/* We only support 4-byte aligned word transfers. */
			if ((uio->uio_offset & 0x03) || (uio->uio_resid & 0x03))
				return EFAULT;
			
			remain = uio->uio_resid >> 2;
			for (; remain >= 12; remain -= 12)
			{
				words[0] = uio->uio_offset; /* auto modulo 2^32 */
				error = uiomove((caddr_t)&words[1], 12*sizeof(unsigned int), uio);
				if (error)
					return error;
				
				error = dagmon(sc, DAGMON_WRITE_12_WORDS, words);
				if (error)
					return error;
			}
			
			for (; remain > 0; remain--)
			{
				words[0] = uio->uio_offset; /* auto modulo 2^32 */
				error = uiomove((caddr_t)&words[1], sizeof(unsigned int), uio);
				if (error)
					return error;
				
				error = dagmon(sc, DAGMON_WRITE_WORD, words);
				if (error)
					return error;
			}
			return error; /* none */
		
#if 0
	  case DAGMINOR_RAM:
#endif /* 0 */

		default:
			break;
	}
	return 0;
}

int
#if __FreeBSD_version < 500000
dag_poll(dev_t dev, int events, struct proc *p)
#else
dag_poll(struct cdev *dev, int events, struct thread *th)
#endif
{
	DD(("dag_poll\n"));

	return 0;
}

int
#if __FreeBSD_version < 500000
dag_mmap(dev_t dev, vm_offset_t offset, int nprot)
#elif __FreeBSD_version < 900000
dag_mmap(struct cdev *dev, vm_offset_t offset, vm_paddr_t *paddr, int nprot)
#else
dag_mmap(struct cdev *dev, vm_ooffset_t offset, vm_paddr_t *paddr, int nprot, vm_memattr_t *memattr)
#endif
{
	dag_softc_t *sc = (dag_softc_t *)dev->si_drv1;

	DD(("dag_mmap\n"));

	if (nprot & PROT_EXEC)
		return -1;

	switch (DAGMINOR(dev))
	{ 
		case DAGMINOR_DAG:
		case DAGMINOR_MEM:

			if (offset >= sc->bufsize)
				return -1;
			if (!sc->guest) {
#if __FreeBSD_version < 500000
				return i386_btop(vtophys(sc->buf)+offset);
#else
				*paddr = vtophys(sc->buf)+offset;
				return 0;
#endif
			}
			else {
#if __FreeBSD_version < 500000
				return i386_btop(vtophys(sc->dmares->r_virtual) + offset);
#elif __FreeBSD_version < 700000 
				*paddr = vtophys(sc->dmares->r_virtual)+offset;
				return 0;
#else 

				*paddr = vtophys(rman_get_virtual(sc->dmares))+offset;
				return 0;
#endif
			}
		case DAGMINOR_IOM:
			DD(("dag_mmap IOM offset 0x%x\n", (unsigned int) offset));
			if (offset >= sc->iomsize)
				return -1;
			#if __FreeBSD_version < 500000
				return i386_btop(vtophys(sc->iomres->r_virtual) + offset);
			#elif __FreeBSD_version < 700000 
				*paddr = vtophys(sc->iomres->r_virtual)+offset;
				return 0;
			#else 
                                *paddr = vtophys(rman_get_virtual(sc->iomres))+offset;
                                return 0;
	
			#endif
		
		case DAGMINOR_ARM:
			return -1; /* never */
		
# if 0
		case DAGMINOR_RAM:
			return -1; /* not today */
# endif
	}

	return 0;
}

/*
 * Interrupt handler
 */
void
dag_intr(void *ptr)
{
	dag_softc_t * sc = ptr;
	int type;

	type = ToHDB & 0xffff;

	DN(("dag%u: intr 0x%08x\n", sc->unit, type));

	/* clear interrupt */
	if (type)
	{
		DD(("dag%u: intr 0x%08x\n", sc->unit, type));
		ToHDB = type;
	}

	if (type == 0xffff)
	{
		/* Defined invalid! Probably the xilinx reloading */
		return;
	}

	/* test interrupt */
	if (type & 0x8080)
	{
		static struct timespec oldts;
		struct timespec ts;

		nanotime(&ts);
		printf("dag%u: test interrupt 0x%04x, timespec diff %ld ns\n",
			sc->unit, type & 0x8080,
			(ts.tv_sec-oldts.tv_sec)*1000000000+ts.tv_nsec-oldts.tv_nsec);
		oldts.tv_sec = ts.tv_sec;
		oldts.tv_nsec = ts.tv_nsec;
	}

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

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

/*
 * Device driver support functions
 */
static void
dagdebug(char *fmt, ...)
{
	va_list ap;
	
	/* if ( we have proper debugging level ) */
	/* could be changed to call log() */
	va_start(ap, fmt);
	vprintf(fmt, ap);
	va_end(ap);
}

static struct cdevsw dag_cdevsw =
{
#if __FreeBSD_version < 500000
	dag_open,   /* d_open */
	dag_close,  /* d_close */
	dag_read,   /* d_read */
	dag_write,  /* d_write */
	dag_ioctl,  /* d_ioctl */
	dag_poll,   /* d_poll */
	dag_mmap,   /* d_mmap */
	nostrategy, /* d_strategy */
	"dag",      /* d_name */
	115,        /* d_maj */ /* /usr/src/sys/conf/majors */
	nodump,     /* d_dump */
	nopsize,    /* d_psize */
	D_TTY,      /* d_flags */
	-1          /* d_bmaj */
#else
	.d_version = D_VERSION,
	.d_flags   = D_NEEDGIANT | D_TTY,
	.d_open    = dag_open,	/* d_open */
	.d_close   = dag_close,	/* d_close */
	.d_read    = dag_read,	/* d_read */
	.d_write   = dag_write,	/* d_write */
	.d_ioctl   = dag_ioctl,	/* d_ioctl */
	.d_poll    = dag_poll,	/* d_poll */
	.d_mmap    = dag_mmap,	/* d_mmap */
	.d_name    = "dag",	/* d_name */
#endif
};

/*
 * The PCI bus is little endian, so device IDs are product.vendor.
 */

static int
dag_probe(device_t dev)
{
	unsigned int devid = pci_get_devid(dev);

			if( ( (devid & 0xFFFF) == PCI_VENDOR_ID_DAG) ||  ( (devid & 0xFFFF) == PCI_VENDOR_ID_ENDACE) ) {
	
			device_set_desc(dev, dag_device_name(devid>>16,1));
			return 0;
		}
	
	return ENXIO;
}

static int
dag_attach(device_t dev)
{
	dag_softc_t * sc;
	int memid;
	int irqid;
	int error = 0;
	int s;
	int c;
	int unit = device_get_unit(dev);

	DD(("dag_attach\n"));

	s = splimp();

	sc = device_get_softc(dev);

	/*
	 * XXX need to enhance error handling!
	 */
	if (sc == NULL)
	{
		DA(("dag_attach sc == NULL\n"));
		error = ENXIO;
		goto error;
	}

	sc->unit = unit;
	sc->device = dev;
	sc->duck = duck_new();

	for (c = 0; c < DAG_STREAM_MAX; c++)
	{
		sc->lock[c].pid=0;
		sc->lock[c].pgid=0;
	}

	memid = PCIR_BAR(0);
	sc->iomsize = bus_get_resource_count(dev, SYS_RES_MEMORY, memid);
	sc->iomres = bus_alloc_resource(dev, SYS_RES_MEMORY, &memid,
		0UL, ~0UL, sc->iomsize, RF_ACTIVE);

	if (NULL == sc->iomres)
	{
		DA(("dag_attach sc->iomres == NULL\n"));
		error = ENOMEM;
		goto error;
	}

	/* 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)
		{
			DA(("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"));
			error = EIO;
			goto error;
		}
	}

	DD(("dag%u: dag_attach sc->iomsize = 0x%08x\n", unit, (uint32_t) sc->iomsize));
	DD(("dag%u: dag_attach sc->iomres = %p\n", unit,  sc->iomres));
	DD(("dag%u: dag_attach sc->iomres->r_virtual =%p\n", unit, rman_get_virtual(sc->iomres)));

	if (pci_read_config(dev, PCIR_LATTIMER, 1) < DAG_MIN_LATENCY) {
		pci_write_config(dev, PCIR_LATTIMER, DAG_MIN_LATENCY, 1);
		DD( ("dag%u: Setting latency timer to %u\n", unit, DAG_MIN_LATENCY) );
	}

	irqid = 0;
	sc->irqres = bus_alloc_resource(dev, SYS_RES_IRQ, &irqid,
		0UL, ~0UL, 1, RF_ACTIVE|RF_SHAREABLE);
	if (sc->irqres == NULL)
	{
		error = ENXIO;
		goto error;
	}
#if __FreeBSD_version < 700000
	error = bus_setup_intr(dev, sc->irqres, INTR_TYPE_NET, dag_intr,sc, &sc->intrhand); 
#else
	error = bus_setup_intr(dev, sc->irqres, INTR_TYPE_NET, NULL, dag_intr,sc, &sc->intrhand); 
#endif 	
	if (error) {
		DA(("dag%u: attach bus_setup_intr error %d\n", unit, error));
		sc->irqres = NULL;
		goto error;
	}
	else {
		IntEn = 0xffff;
	}

	sc->guest = 0;
	memid = PCIR_BAR(1);
	sc->bufsize = bus_get_resource_count(dev, SYS_RES_MEMORY, memid);
	if (sc->bufsize) {
		/* 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 (dag_ioread(0x200c) & 0xffff) {
			sc->bus_addr = dag_ioread(0x2040);
			if (sc->bus_addr) {
				/* If bar1 exists, may be vdag/dock */
				sc->dmares = bus_alloc_resource(dev, SYS_RES_MEMORY, &memid,
							0UL, ~0UL, sc->bufsize, RF_ACTIVE);
				if(!sc->dmares) {
					DA(("dag%u: dag_attach Failed to mmap PCI BAR1 memory\n", unit));
					error = ENOMEM;
					goto error;
				}
				sc->guest = 1;
				DD(("dag%u: dag_attach sc->dmasize = 0x%08x\n", unit, (uint32_t) sc->bufsize));
				DD(("dag%u: dag_attach sc->dmares = %p\n", unit, sc->dmares));
				DD(("dag%u: dag_attach sc->dmares->r_virtual = %p\n", unit, rman_get_virtual(sc->dmares)));
			}
		}
		if (!sc->guest) {
			DA(("dag%u: BAR 1 found, but PBM is not configured %d\n", unit));
			error = ENXIO;
			goto error;
		}
	}
	/* H/W DAG card */
	else {
		/*sc->bufsize = DAGMEM_DEFAULT_SIZE;*/ /* XXX might want to make this more flexible */
		(void) getenv_int("dagmem_size", (int *) &dagmem_size);
		sc->bufsize = dagmem_size;
		sc->buf = dagmem_malloc(sc->bufsize, M_DEVBUF, M_NOWAIT);
		if (sc->buf == NULL) {
			DA(("dag%u: cannot dagmem_malloc %u\n", unit, sc->bufsize));
			goto error;	/* XXX undo lots of stuff */
		}
		sc->bus_addr = vtophys(sc->buf);
	}

	DD(("dag%u: dag_attach sc->bus_addr = 0x%08x\n", unit, sc->bus_addr));
	sc->dagdev = make_dev(&dag_cdevsw, DAGDEVICE(unit,DAGMINOR_DAG),
				UID_ROOT, GID_WHEEL, 0660, "dag%d", unit);
	DD(("dagN: make_dev dagdev %p\n", sc->dagdev));
	
	sc->dagmemdev = make_dev(&dag_cdevsw, DAGDEVICE(unit,DAGMINOR_MEM),
				UID_ROOT, GID_WHEEL, 0660, "dagmem%d", unit);
	DD(("dagN: make_dev dagmemdev %p\n", sc->dagmemdev));
	
	sc->dagiomdev = make_dev(&dag_cdevsw, DAGDEVICE(unit,DAGMINOR_IOM),
				UID_ROOT, GID_WHEEL, 0660, "dagiom%d", unit);
	DD(("dagN: make_dev dagiomdev %p\n", sc->dagiomdev));
	
	sc->dagarmdev = make_dev(&dag_cdevsw, DAGDEVICE(unit,DAGMINOR_ARM),
				UID_ROOT, GID_WHEEL, 0660, "dagarm%d", unit);
	DD(("dagN: make_dev dagarmdev %p\n", sc->dagarmdev));

#if 0
	sc->dagramdev = make_dev(&dag_cdevsw, DAGDEVICE(unit,DAGMINOR_RAM),
				UID_ROOT, GID_WHEEL, 0660, "dagram%d", unit);
	DD(("dagN: make_dev dagramdev 0x%x\n", (unsigned int) sc->dagramdev));
#endif /* 0 */

	dag_pciconfig_save(sc);
	duck_init(sc);

	/*
	 * Synchronize with the Dag ARM monitor
	 * May consider resetting the card instead
	 */
	dagmon_init(sc);

error:
	splx(s);
	return error;
}

static int
dag_detach(device_t dev)
{
	dag_softc_t * sc;
	int s;

	DD(("dag_detach\n"));

	s = splimp();

	sc = device_get_softc(dev);

	/*
	 * Release resources in reverse order
	 */
	if (sc->dagramdev)
		destroy_dev(sc->dagramdev);
	
	if (sc->dagarmdev)
		destroy_dev(sc->dagarmdev);
	
	if (sc->dagiomdev)
		destroy_dev(sc->dagiomdev);
	
	if (sc->dagmemdev)
		destroy_dev(sc->dagmemdev);
	
	if (sc->dagdev)
		destroy_dev(sc->dagdev);
	
	if (sc->intrhand)
	{
		IntEn = 0x0; /* disable card ints */
		bus_teardown_intr(dev, sc->irqres, sc->intrhand);
	}
	
	if (sc->irqres)
	{
		int irqid = 0;;
		bus_release_resource(dev, SYS_RES_IRQ, irqid, sc->irqres);
	}
	
	if (sc->duck)
	{
		duck_destroy(sc);
	}
	
	if (sc->iomres)
		bus_release_resource(dev, SYS_RES_MEMORY, PCIR_BAR(0), sc->iomres);

	if (sc->guest && sc->dmares)
		bus_release_resource(dev, SYS_RES_MEMORY, PCIR_BAR(1), sc->dmares);
	else if (sc->buf)
		dagmem_free(sc->buf, M_DEVBUF);

	splx(s);

	return 0;
}

static int
dag_shutdown(device_t dev)
{
	DD(("dag_shutdown\n"));

	return 0;
}

static int
dag_suspend(device_t dev)
{
	DD(("dag_suspend\n"));

	return 0;
}

static int
dag_resume(device_t dev)
{
	DD(("dag_resume\n"));

	return 0;
}

/*
 * Driver loading and unloading support
 */
static device_method_t dag_methods[] =
{
	DEVMETHOD(device_probe,     dag_probe),
	DEVMETHOD(device_attach,    dag_attach),
	DEVMETHOD(device_detach,    dag_detach),
	DEVMETHOD(device_shutdown,  dag_shutdown),
	DEVMETHOD(device_suspend,   dag_suspend),
	DEVMETHOD(device_resume,    dag_resume),
	{0, 0}
};

static driver_t dag_driver =
{
	"dag",
	dag_methods,
	sizeof(dag_softc_t)
};

DRIVER_MODULE(dag, pci, dag_driver, dag_devclass, 0, 0);
MODULE_DEPEND(dag, dagmem, 0, 0, 0);

