/****************************************************************************
 * DAG Driver for Solaris
 * Copyright Endace Technology Ltd. 2005
 * Abel Navarro <abel@endace.com>
 * Vladimir Minkov <vladimir@endace.com>
 * $Id: dag.c 8323 2008-02-01 02:24:21Z vladimir $
 * (best viewed with tab size 4)
 ****************************************************************************/


/****************************************************************************
 * Includes
 ****************************************************************************/
#include <sys/modctl.h>

#include <sys/cmn_err.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/file.h>
#include <sys/errno.h>
#include <sys/open.h>
#include <sys/cred.h>
#include <sys/uio.h>
#include <sys/devops.h>
#include <sys/conf.h>
#include <sys/pci.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>

#include "daginf.h"
#include "dagnew.h"
#include "dagduck.h"
#include "dagdrv.h"

/****************************************************************************
 * Macros
 ****************************************************************************/
#define MIN(a, b)   ((a)<(b)?(a):(b))
#define DAG_SUCCESS DDI_SUCCESS
#define DAG_FAILURE DDI_FAILURE

/****************************************************************************
 * Function declarations
 ****************************************************************************/

/* driver autoconfiguration functions */
static int dag_probe(dev_info_t * dip);
static int dag_attach(dev_info_t * dip, ddi_attach_cmd_t cmd);
static int dag_detach(dev_info_t * dip, ddi_detach_cmd_t cmd);
static int dag_getinfo(dev_info_t * dip, ddi_info_cmd_t cmd,
void * arg, void ** result_p);
static int dag_prop_op(dev_t dev, dev_info_t * dip,
ddi_prop_op_t prop_op, int flags, char * name, caddr_t value_p,
int * lenght_p);

/* interrupt handler */
static uint_t dag_interrupt(caddr_t arg);

/* user context functions */
static int dag_open(dev_t * dev_p, int flag, int otyp, cred_t * cred_p);
static int dag_close(dev_t dev, int flag, int otyp	, cred_t * cred_p);
static int dag_read(dev_t dev, struct uio * uio_p, cred_t * cred_p);
static int dag_write(dev_t dev, struct uio * uio_p, cred_t * cred_p);
static int dag_ioctl(dev_t dev, int cmd, intptr_t arg, int mode,
cred_t * cred_p, int * rval_p);
static int dag_devmap(dev_t dev, devmap_cookie_t handle, offset_t off,
size_t len, size_t * maplen, uint_t model);

/* user context helper functions */
static int dag_mem_alloc(dev_info_t * dip, int instance, int size);

//Obsolete done as part of dag_cleanup
//static void dag_mem_free(dev_info_t * dip, int instance);


/* other helper functions */
static int dag_cleanup(struct dag_softstate *softstate);
static int dag_pci_setup(struct dag_softstate *softstate);

static int dag_reset(int instance);
//not used used internel kernel call
//static int dag_pciconfig_save(struct dag_softstate *softstate);
static int dag_pciconfig_restore(struct dag_softstate *softstate);

static void dagmon_init(struct dag_softstate *softstate);
//static void duck_init(struct dag_softstate *softstate) ;

inline uint32_t dag_ioread(struct dag_softstate *softstate, uint32_t offset);
inline void dag_iowrite(struct dag_softstate *softstate, uint32_t offset, uint32_t value);

/****************************************************************************
 * Global variables
 ****************************************************************************/

/* character and block devices operations */
static struct cb_ops dag_cb_ops = {
	dag_open,
	dag_close,
	nodev,			/* no strategy - nodev returns ENXIO */
	nodev,			/* no print */
	nodev,			/* no dump */
	//FIXME Properly the hole read and write 64bit and 32 bit apps
	nodev, //dag_read,
	nodev, //dag_write,
	
	//FIXME Properly the hole ioctl //64 bi and 32 bit apps
	dag_ioctl,
	
	//Only support for 32 and 64 bit apps left
	dag_devmap,
	
	nodev,			/* no mmap dis interface is obsolete */
	nodev,			/* no segmap this is needed for multiple context switching
	 in our case dag_devmap should be enough*/
	nochpoll,		/* returns ENXIO for non-pollable devices */
	ddi_prop_op,			//dag_prop_op,
	NULL,			/* streamtab struct; if !NULL, all above fields ignored */
	//D_NEW | D_MP | D_64BIT | D_DEVMAP,
	//D_DEVMAP is needed to call dag_devmap from mmap user space
	D_NEW | D_MTSAFE | D_64BIT  | D_DEVMAP,
	CB_REV,			/* cb_ops revision number */
	nodev,			/* no aread */
	nodev,			/* no awrite */
};

/* general device operations (autoconfiguration) */
static struct dev_ops dag_dev_ops = {
	DEVO_REV,
	0,				/* reference count */
	
	dag_getinfo,
	nulldev,		/* no identify - nulldev returns 0 */
	nulldev,		/* for PCI devices no need for probe unless hotplug*/
	dag_attach,
	dag_detach,
	nodev,			/* no reset - nodev returns ENXIO */
	
	&dag_cb_ops,
	NULL,			/* devo_bus_ops */
	NULL,			/* devo_power */
};

/* module linkage structure for loadable modules */
static struct modldrv dag_modldrv = {
	&mod_driverops,				/* Type of module. This is a driver         */
	"Endace DAG driver 2.7.x",	/* Name of the module                       */
	&dag_dev_ops				/* operations structure                     */
};

/* module linkage */
static struct modlinkage dag_modlinkage = {
	MODREV_1,
	{ &dag_modldrv, NULL},
};

/*
 * DMA Attributes. These help define how will be the dma allocated memory.
 * DMA memory allocations will try to keep data aligned looking at these
 * parameters. This information is not directly important for the correct
 * operation of the cards.
 */
static ddi_dma_attr_t dag_dma_attr = {
	DMA_ATTR_V0,		/* version number */
	0x000000000,		/* low address */
	(uint64_t)0xFFFFFFFF,			/* high address taht can be accessed by the DMA*/
	(uint64_t)0xFFFFFFFF,			/* Maximum Trasnefer size per cookie*/
	(uint64_t)1,				/* FIXME Page boundary alligment of the memory*/
	// 1bytes,2,4,8,16,32,64,128,256...
	//0xFF8,				/* CHECKME Burst sizes: 8bytes 16,32,64,128,256,512,1024,2048,4096... */
	0x0F8,				/* CHECKME Burst sizes: 8bytes 16,32,64,128,256,512,1024,2048,4096... */
	//0x8,				/* Minimum transfer size */
	0x1,				/* Minimum transfer size */
	0xFFFFFFFF,			/* FIXME Max transfer size */
	0xFFFFFFFF,			/* Address register max */
	1,					/* No scatter-gather / Only one DMA cookie */
	8,					/* Device operates on 64-bit words only for scatter-gather */
	0,					/* Attr flags: set to 0 */
};

/*
 * This structure stores information about the endianess and data order
 * requirements of the device. Byte swapping is done transparently inside the
 * DDI functions (if necessary). Note that all the registers will be considered
 * little endian or big endian, but no a mix of them.
 */
static ddi_device_acc_attr_t dag_device_attr = {
	DDI_DEVICE_ATTR_V0,		/* structure version */
	DDI_STRUCTURE_LE_ACC,	/* Little Endian (LE) */
	DDI_STRICTORDER_ACC		/* access to device data cannot be reordered */
};
/*The same settings for the drb access but juts in case changes in the future*/
static ddi_device_acc_attr_t dag_drb_device_attr = {
	DDI_DEVICE_ATTR_V0,		/* structure version */
	DDI_STRUCTURE_LE_ACC,	/* Little Endian (LE) */
	DDI_STRICTORDER_ACC		/* access to device data cannot be reordered */
};

/*
 * This variable keeps track of driver instances. One driver can handle
 * several devices. For each device, one instance should be used. The DDI
 * environment provides tools for doing this. At this moment no advantage
 * is found using that tools and this way we keep a similar look with
 * DAG drivers for other platforms.
 */
//static dag_softc_t dag_array[DAG_MAX_BOARDS];
static void *dag_state;
/****************************************************************************
 * Loadable module entry points
 ****************************************************************************/

/****************************************************************************/
int _init(void) {
	int err;
	//TODO: if needed add ddi_soft_state_init()
	cmn_err(CE_NOTE, "Inside _init");
	//which will init memory for instances (card specific information)
	err = ddi_soft_state_init(&dag_state, sizeof(struct dag_softstate), 1);
	
	if (err == 0) {
		//at the moment it is static array
		if ((err = mod_install(&dag_modlinkage)) != 0)
			ddi_soft_state_fini(&dag_state);
	}
	cmn_err(CE_NOTE, "Inside _init: modinstall result: %d", err);
	return err;
}

/****************************************************************************/
int _info(struct modinfo * modinfo_p) {
	cmn_err(CE_NOTE, "Inside _info");
	return  (mod_info(&dag_modlinkage, modinfo_p));
}

/****************************************************************************/
int _fini(void) {
	int err;
	
	cmn_err(CE_NOTE, "Inside _fini");
	
	//CHECKME: Do we need verify of dag_state
	if ((err = mod_remove(&dag_modlinkage)) == 0)
		ddi_soft_state_fini(&dag_state);
	
	return err;
}


/****************************************************************************
 * Autoconfiguration entry points
 ****************************************************************************/

/****************************************************************************
 * Because each pair vendor-id/device-id has a different name (prtconf -vp)
 * we are using instead the pciclass property 028000 to bind the DAG driver
 * to the DAG card. The class number means: 02 - network device,
 * 8000 - unknown. As other devices could use the same pciclass, we should
 * probe if the detected device is a DAG card, looking at the vendor-id.
 ****************************************************************************/
static int dag_probe(dev_info_t * dip) {
	
	ddi_acc_handle_t dag_pci_handle;
	uint16_t pci_vendorid;
	int res;
	
	cmn_err(CE_NOTE, "Inside dag_probe (%d)", ddi_get_instance(dip));
	
	if (ddi_dev_is_sid(dip) == DDI_SUCCESS) {
		cmn_err(CE_NOTE, "dag_probe: Selfidentified device");
		/*
		 * This is the self-identifying version (OpenBoot).
		 * No need to probe for it because we know it is there.
		 * The existence of dip && ddi_dev_is_sid() proves this.
		 */
		return (DDI_PROBE_FAILURE);
		return (DDI_PROBE_DONTCARE);
	};
	
	cmn_err(CE_NOTE, "dag_probe: Not Self Identified device");
	//vlad the pci is self ident
	//ddi_prop_lookup
	//  reg array
	//ddi_regs_map_setup
	return (DDI_PROBE_FAILURE);
	
	
	
	// Setup PCI card to read its configuration registers
	res = pci_config_setup(dip, &dag_pci_handle);
	if (res == DDI_FAILURE) {
		cmn_err(CE_NOTE, "[dag_probe] failed pci_config_setup.");
		return (DDI_PROBE_FAILURE);
	}
	
	// Read PCI config registers
	pci_vendorid = pci_config_get16(dag_pci_handle, PCI_CONF_VENID);
	cmn_err(CE_NOTE, "[dag_probe] pci_vendorid: 0x%04x", pci_vendorid);
	
	// Remember that probe() has to undo all the things it does
	pci_config_teardown(&dag_pci_handle);
	
	// Endace's vendor-id is 0xeace (nice, isn't it?)
	if (pci_vendorid != 0xeace) {
		cmn_err(CE_NOTE, "[dag_probe] pci_vendorid: 0x%04x", pci_vendorid);
		return (DDI_PROBE_FAILURE);
	};
	
	/*
	 * If not all Endace PCI (PCI-X) cards are supported, this is the right
	 * place to check it.
	 */
	
	return (DDI_PROBE_SUCCESS);
}


/****************************************************************************
 * Some notes about minor names and numbers ... (to fill later)
 ****************************************************************************/
static int dag_attach(dev_info_t * dip, ddi_attach_cmd_t cmd) {
	int res;
	struct dag_softstate *softstate;
	int instance;
	int nregs;
	int nintrs;
	int i;
	off_t size;
	int mem_size;
	char name[100];
	
	instance = ddi_get_instance(dip);
	cmn_err(CE_NOTE, "Inside dag_attach (%d)", instance);
	
	/* Check arguments */
	switch(cmd) {
		case DDI_ATTACH:
			break;
		case DDI_RESUME:
		case DDI_PM_RESUME:
			/* We do not support power management at this moment */
			cmn_err(CE_NOTE, "Power resume not supported (%d)", instance);
			return (DDI_FAILURE);
			break;
		default:
			cmn_err(CE_NOTE, "dag_attach: Unsopported attach(%d)", instance);
			return (DDI_FAILURE);
	}
	
	/*
	 * this card is a bus master, reject any slave-only slot
	 */
	if (ddi_slaveonly(dip) == DDI_SUCCESS) {
		cmn_err(CE_NOTE, "dag_attach: slot does not support PCI bus-master");
		return DDI_FAILURE;
	}
	//we have one instance per card
	//later we will decide for the streams
	//allocate card(stream) and driver specific information
	if (ddi_soft_state_zalloc(dag_state, instance) != DDI_SUCCESS) {
		cmn_err(CE_NOTE, "dag_attach: zalloc failed bad");
		return DDI_FAILURE;
	}
	//Get the structure and start to initialize it
	softstate = ddi_get_soft_state(dag_state, instance);
	//init the Device Information Pointer
	cmn_err(CE_NOTE, "dag_attach: Init the device_info_t pointer in softstate ");
	softstate->dip = dip;
	
	// init to access the PCI configuration space
	if (pci_config_setup(softstate->dip, &softstate->pci_conf_handle) != DDI_SUCCESS) {
		cmn_err( CE_NOTE, "dag %d: pci_config_setup failed", instance);
		dag_cleanup(softstate);
		return DDI_FAILURE;
	}
	
	if (dag_pci_setup(softstate) != DAG_SUCCESS) {
		cmn_err(CE_NOTE, "dag %d: dag_pci_setup failed", instance);
		dag_cleanup(softstate);
		return DDI_FAILURE;
	}
	
	
	/*
	 * Read memory hole size from external config file
	 * Defaults to what 128 MB at the moment
	 *
	 */
	mem_size = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, "mem_size", 128);
	
	/* Do some sanity checks and convert mem size into bytes */
	if (mem_size <= 0) mem_size = 128;
	if (mem_size > 1024) mem_size = 128;
	mem_size = (mem_size << 20);
	cmn_err(CE_NOTE, "Attach memory hole %d", mem_size);
	
	/* Memory hole allocation and mapping to the dma of the card
	 On solaris this mapping is needed to initialize the MMU if presented
	 and to make sure that the memory is allocated properly according to the card
	 and system needs*/
	res = dag_mem_alloc(dip, instance, mem_size);
	if (res == DAG_FAILURE) {
		cmn_err(CE_NOTE, "could not allocate memory hole (%d)", instance);
		return (DDI_FAILURE);
	}
	
	/*
	 * Sanity checks for the IO Space before mapping
	 * to the Kern driver
	 */
	/* Get number of register spaces */
	res = ddi_dev_nregs(dip, &nregs);
	if (res == DDI_SUCCESS) {
		if(nregs != 2 ) {
			cmn_err(CE_NOTE, "ERROR dag_attach: nregs is not 2 but: %d", nregs);
			return ( DAG_FAILURE );
		};
		cmn_err(CE_NOTE, "dag_attach: nregs: %d", nregs);
	} else {
		cmn_err(CE_NOTE, "Error while accessing PCI registers");
		return (DDI_FAILURE);
	};
	
	/* Get the size of every register space */
	for (i=0; i< nregs;i++) {
		res = ddi_dev_regsize(dip, (uint_t) i, &size);
		if (res == DDI_FAILURE)
			return res;
		cmn_err(CE_NOTE, "reg %d, size: %d", i, size);
		if( (i==0) && (size !=0 ) )
			cmn_err(CE_NOTE, "Warning BAR 0 is probably old ARM interface not supported");
	};
	
	if (size == 0) {
		cmn_err(CE_NOTE, "Warning BAR 1(DRB) is probably badly configured call firmware team");
		return DAG_FAILURE;
	}
	
	softstate->info.iom_size = size;
	softstate->drb_regs_size = size;
	/*
	 * Empirical information: IOM space on DAG cards is pointed by the second
	 * register space (number 1). It reports 64KB size (which is right).
	 */
	
	res = ddi_regs_map_setup(
	dip,						/* pointer to device                */
	1,								/* register space to mmap           */
	//&(softstate->iom),			/* return address of mmaped regs    */
	&(softstate->drb_regs),			/* return address of kernel mapped regs    */
	0,								/* offset from where to map we can have multiple mappings */
	softstate->drb_regs_size, /* size - number of registers to map
	 returned by the PCI BAR (ddi_dev_regsize) we map all registers*/
	&dag_drb_device_attr,				/* device attributes                */
	&(softstate->drb_regs_handle)	/* handle to iom mmapping           */
	);
	
	switch (res) {
		case DDI_SUCCESS:
			//cmn_err(CE_NOTE, "mmaped iom at addr: 0x%08x", softstate->iom);
			cmn_err(CE_NOTE, "dag_attach kernel mapped drb registers at addr: 0x%08x", softstate->drb_regs);
			break;
		case DDI_FAILURE:
			cmn_err(CE_NOTE, "ddi_regs_map_setup: invalid register number, offset\
			or length");
			return (DDI_FAILURE);
		case DDI_ME_RNUMBER_RANGE:
			cmn_err(CE_NOTE, "Invalid register number of unable to find reg\
			property");
			return (DDI_FAILURE);
		case DDI_REGS_ACC_CONFLICT:
			cmn_err(CE_NOTE, "Cannot enable the register mapping due to access\
			conflicts with other enabled mappings");
			return (DDI_FAILURE);
			
		default:
			cmn_err(CE_NOTE, "ddi_regs_map_setup: unknown error");
			return (DDI_FAILURE);
	}
	//TESTING register space
	//	cmn_err(CE_NOTE, "Registers dumped:");
	//	for(i=0;i<1000;i+=4)
	//		cmn_err(CE_CONT, "0x%08x, ",dag_ioread(softstate,i) );
	
	
	/*
	 * Initialize DUCK
	 */
	//softstate->duck = duck_new();
	
	
	cmn_err(CE_NOTE, "dag_attach: Instance Mutex init ");
	mutex_init(&softstate->instance_mutex, NULL, MUTEX_DRIVER, NULL);
	
	
	/*
	 * Register interrupts
	 */
	
	/* Get number of interrupts the device has */
	res = ddi_dev_nintrs(dip, &nintrs);
	if (res == DDI_SUCCESS) {
		cmn_err(CE_NOTE, "number of interrupts: %d", nintrs);
	} else {
		cmn_err(CE_NOTE, "the device has no interrupt specifications");
	}
	/* we know there is only one interrupt. */
	res = ddi_intr_hilevel(dip, 0);
	if (res != 0) {
		cmn_err(CE_NOTE, "interrupt 0 is high level");
	} else {
		cmn_err(CE_NOTE, "interrupt 0 is not high level");
	}
	
	/* get interrupt handling cookie */
	cmn_err(CE_NOTE, "dag_attach: get iblock cookie for interrupts");
	if (ddi_get_iblock_cookie(softstate->dip, 0, &softstate->intr_cookie) != DDI_SUCCESS) {
		cmn_err(CE_WARN, "Could not get iblock cookie");
		dag_cleanup(softstate);
		return DDI_FAILURE;
	};
	
	cmn_err(CE_NOTE, "dag_attach: Init interrupt mutex");
	mutex_init(&softstate->intr_mutex, NULL,
	MUTEX_DRIVER, (void*)softstate->intr_cookie);
	
	/* as a parameter to the interrupt is given the softstate the structure to the driver and card */
	cmn_err(CE_NOTE, "dag_attach: Add Interrupt handler dag_interrupt");
	if (ddi_add_intr(softstate->dip, 0,
	&softstate->intr_cookie, NULL,
	dag_interrupt, (caddr_t)softstate) != DDI_SUCCESS) {
		
		cmn_err(CE_WARN, "Could not add interrupt handler");
		dag_cleanup(softstate);
		return DDI_FAILURE;
	}
	
	
	/* now we can enable interrupts on the device */
	dag_iowrite(softstate, IntEn, 0xffff);
	
	
	//-----------------------------------------------
	//creates the dag device nodes
	//TODO: move at the end after we ensure proper attachment
	sprintf(name, "dag%d", instance);
	cmn_err(CE_NOTE, "Created node: %s from instance:%d", name, instance);
	if ( ddi_create_minor_node(dip, name, S_IFCHR,
	instance + 0, "dag", 0) != DDI_SUCCESS) {
		cmn_err(CE_NOTE, "could not create 'dag' device node.");
		return (DDI_FAILURE);
	};
	//creating the dagmem device node
	sprintf(name, "dagmem%d", instance);
	cmn_err(CE_NOTE, "Created node: %s from instance:%d", name, instance);
	if ( ddi_create_minor_node(dip, name, S_IFCHR,
	instance + 0x10 , "dag", 0) != DDI_SUCCESS) {
		cmn_err(CE_NOTE, "could not create 'dagmem' device node.");
		return (DDI_FAILURE);
	};
	//Create the node for dagiomem
	sprintf(name, "dagiom%d", instance);
	cmn_err(CE_NOTE, "Created node: %s from instance:%d", name, instance);
	if ( ddi_create_minor_node(dip, name, S_IFCHR,
	instance  + 0x20, "dag", 0) != DDI_SUCCESS) {
		cmn_err(CE_NOTE, "could not create 'dagiom' device node.");
		return (DDI_FAILURE);
	};
	//Create the node for the arm access dagarm
	sprintf(name, "dagarm%d", instance);
	cmn_err(CE_NOTE, "Created node: %s from instance:%d", name, instance);
	if ( ddi_create_minor_node(dip, name, S_IFCHR,
	instance  + 0x30, "dag", 0) != DDI_SUCCESS) {
		cmn_err(CE_NOTE, "could not create 'dagarm' device node.");
		return (DDI_FAILURE);
	};
	//------------------------------------------------
	/* save pci configuration registers */
	//the save should be done before power off or befeore reset
	// fo the moment we save it once and restore it only after a reset
	//the restore should be done before changing the power and
	// before reset or rom reload(any xilinx reset)
	// but here it is easy
	if (pci_save_config_regs(dip) != DDI_SUCCESS) {
		cmn_err(CE_WARN, "failed to save pci config regs");
	}
	/* Initialize time facility (DUCK) */
	//FIXME/TODO:
	//duck_init(softstate);
	
	
	cmn_err(CE_NOTE, "dag_attach: OK - instance of driver attached");
	return (DDI_SUCCESS);
}
/****************************************************************************
 * Here starts the memory hole allocation hole, at least
 * for test purposes. Later we may decide to put part of
 * this code on the _init function.
 ****************************************************************************/
//static int dag_setup_dma
static int dag_mem_alloc(dev_info_t * dip, int instance, int size) {
	struct dag_softstate *softstate = ddi_get_soft_state(dag_state, instance);
	int res;
	
	softstate->memhole_dma_bound = 0; //false
	
	/* DMA handle */
	res = ddi_dma_alloc_handle(dip, &dag_dma_attr,	DDI_DMA_DONTWAIT, NULL, &(softstate->memhole_dma_handle));
	if( res != DDI_SUCCESS)
		softstate->memhole_dma_handle = 0;
	switch(res) {
		case DDI_SUCCESS:
			break;
			
		case DDI_DMA_NORESOURCES:
			cmn_err(CE_NOTE, "[dag_alloc] no available resources.");
			return (DDI_FAILURE);
			
		case DDI_DMA_BADATTR:
			cmn_err(CE_NOTE, "[dag_alloc] bad DMA attributes.");
			return (DDI_FAILURE);
			
		default:
			cmn_err(CE_NOTE, "unknow error in ddi_dma_alloc_handle.");
			return (DDI_FAILURE);
	}
	
	/* allocate contiguous physical memory */
	//Warnings If DDI_NEVERSWAP_ACC is specified, memory can be used for any purpose; but if either endian mode
	//        is specified, you must use ddi_get/put* and never anything else.
	// we are not exactly on this but CHECKME:
	
	res = ddi_dma_mem_alloc(softstate->memhole_dma_handle, size,
	&dag_device_attr, DDI_DMA_CONSISTENT, DDI_DMA_DONTWAIT, NULL,
	&(softstate->memhole_addr), &(softstate->memhole_size),
	&(softstate->memhole_acc_handle));
	
	if (res != DDI_SUCCESS) {
		cmn_err(CE_NOTE, "[dag_alloc] cannot allocate memory hole err %d", res);
		//ddi_dma_free_handle(&(softstate->dma_handle));
		return (DAG_FAILURE);
	}
	
	cmn_err(CE_NOTE,  "MEMHOLE at kernel address %p\n", softstate->memhole_addr);
	cmn_err(CE_NOTE, "[dag_alloc] allocated memory size %d", softstate->memhole_size);
	
	if ((res = ddi_dma_addr_bind_handle(softstate->memhole_dma_handle,
	NULL,
	softstate->memhole_addr,  //we use the kernel virtual address
	softstate->memhole_size,
	DDI_DMA_RDWR | DDI_DMA_CONSISTENT,
	DDI_DMA_DONTWAIT, NULL,
	&(softstate->cookie), &(softstate->cookie_count))) != DDI_DMA_MAPPED) {
		cmn_err(CE_NOTE, "setup_dma: bind failed (%d)\n", res);
		switch (res) {
			case DDI_DMA_PARTIAL_MAP:
				cmn_err(CE_NOTE, "could not accept partial map");
				break;
			case DDI_DMA_INUSE:
				cmn_err(CE_NOTE, "mapping already in use");
				break;
			case DDI_DMA_NORESOURCES:
				cmn_err(CE_NOTE, "not enough resources to map");
				break;
			case DDI_DMA_NOMAPPING:
				cmn_err(CE_NOTE, "memory object cannot be reached by device");
				break;
			case DDI_DMA_TOOBIG:
				cmn_err(CE_NOTE, "mapping request too big for this system");
				break;
			default:
				cmn_err(CE_NOTE, "[ddi_dma_addr_bind_handle] unknown error");
				break;
		}
		
		return DAG_FAILURE;
	}
	
	/*
	 * We should receive exactly one physical page mapping
	 * (=contiguous physical memory)
	 */
	if (softstate->cookie_count != 1) {
		cmn_err(CE_NOTE, "dag_setup_dma: unexpected dma cookie count > 1 (%u)\n", softstate->cookie_count);
		return DAG_FAILURE;
	}
	
	softstate->memhole_dma_bound = 1; //TRUE;
	
	cmn_err(CE_NOTE,  "MEMHOLE at physical address %p\n",
	(void*) (long) softstate->cookie.dmac_address);
	
	softstate->memhole_paddr =  (void*) (long)softstate->cookie.dmac_address;
	
	cmn_err(CE_NOTE, "cookie addr: 0x%08x", softstate->cookie.dmac_address);
	cmn_err(CE_NOTE, "cookie size: 0x%08x", softstate->cookie.dmac_size);
	cmn_err(CE_NOTE, "cookie count: %d", softstate->cookie_count);
	
	
	//temp fix to make it working the correct thing is to se strategy or physio
	//or to use ddi_umem_alloc , ddi_umem_lock and ddi_dma_buf_bind_handle
	// that is the correct way but for the reason of betta that is good enough
	//curent implementation ddi_dma_mem_alloc , ddi_dma_addr_bind , ddi_umem_lock
	cmn_err(CE_NOTE, "TEMP fix to make it working UMEM COOKIE SETUP: ");
	res = ddi_umem_lock( softstate->memhole_addr, softstate->memhole_size,
	DDI_UMEMLOCK_READ | DDI_UMEMLOCK_WRITE, &softstate->umem_cookie);
	
	return DAG_SUCCESS;
}
/****************************************************************************/
//Obsolete this is done by dag_cleanup
/*
 static void dag_mem_free(dev_info_t * dip, int instance) {
 struct dag_softstate *softstate = ddi_get_soft_state(dag_state, instance);

 ddi_dma_mem_free(&(softstate->memhole_acc_handle));
 ddi_dma_free_handle(&(softstate->memhole_dma_handle));
 }


 */
/****************************************************************************/
static int dag_detach(dev_info_t * dip, ddi_detach_cmd_t cmd) {
	int instance = ddi_get_instance(dip);
	struct dag_softstate *softstate = ddi_get_soft_state(dag_state, instance);
	int res;
	
	cmn_err(CE_NOTE, "inside dag_detach: detach, cmd=%d\n, instance=%d ", cmd, instance);
	
	switch (cmd) {
		case DDI_DETACH:
			break;
			
		default:
			return DDI_FAILURE;
	}
	
	/* now we can disable interrupts on the device */
	//we protect by mutex but this is probably not needed here
	mutex_enter(&softstate->intr_mutex);
	dag_iowrite(softstate, IntEn, 0xffff);
	mutex_exit(&softstate->intr_mutex);
	
	ddi_remove_minor_node(dip, NULL);
	
	if (dag_cleanup(softstate) != DDI_SUCCESS) {
		cmn_err(CE_NOTE, "dag_detach: dag_cleanup failed");
		return DDI_FAILURE;
	}
	cmn_err(CE_NOTE, "inside dag_detach: Detached OK, cmd=%d\n, instance=%d ", cmd, instance);
	
	return DDI_SUCCESS;
	
}

/****************************************************************************/
static int dag_getinfo(dev_info_t * dip, ddi_info_cmd_t cmd, void * arg,  void ** result_p) {
	int instance; 
	struct dag_softstate *softstate;

	instance = ddi_get_instance(dip);
	softstate = ddi_get_soft_state(dag_state, instance);
	
	cmn_err(CE_NOTE, "Inside dag_getinfo (%d)", instance);
	cmn_err(CE_NOTE, "Inside dag_getinfo (%d)", instance);
	
	switch(cmd) {
		case DDI_INFO_DEVT2DEVINFO:
			*result_p = softstate->dip;
			return (DDI_SUCCESS);
			
		case DDI_INFO_DEVT2INSTANCE:
			*result_p = (void *)instance;
			return (DDI_SUCCESS);
		default:
			return (DDI_FAILURE);
	}
}

/****************************************************************************/
static int dag_prop_op(dev_t dev, dev_info_t * dip,
ddi_prop_op_t prop_op, int flags, char * name, caddr_t value_p,
int * length_p) {
	
	cmn_err(CE_NOTE, "Inside dag_prop_op (%d)", ddi_get_instance(dip));
	//	return (DDI_PROP_NOTFOUND);
	return (ddi_prop_op(dev, dip, prop_op, flags, name, value_p,
	length_p));
}

/****************************************************************************
 * Interrupt handler
 ****************************************************************************/
static uint_t
dag_interrupt(caddr_t intr_arg) {
	struct dag_softstate *softstate = (void*)intr_arg;
	uint32_t type;
	int instance;
	
	
	//REVERSE lookup for the instance from the softstate
	instance = ddi_get_instance( softstate->dip ); 
	cmn_err(CE_NOTE, "dag_interrupt: instance: %d arg: 0x%p", intr_arg);
	
	/* clear interrupt */
	type = dag_ioread(softstate, ToHDB) & 0x0000ffff;
	if (type) {
		cmn_err(CE_NOTE, "interrupt type: 0x%04x", type);
		dag_iowrite(softstate, ToHDB, type);
	}
	
	/* Choose action from interrupt type */
	
	/* defined invalid, probably FPGA resetting */
	if (type == 0xffff) {
		return(DDI_INTR_CLAIMED);
	}
	
	/* duck interrupt */
	if (type & 0x110) {
		cmn_err(CE_NOTE, "duck interrupt (%d)", instance);
		//FIXME:
		//duck_intr(softstate);
	}
	
	/* PBM safety-net-reached */
	if(type & 0x1000) {
		cmn_err(CE_NOTE, "pbm safety net reached (%d)", instance);
	}
	
	/* phy status change */
	if(type & 0x2000) {
		cmn_err(CE_NOTE, "Phy status change (%d)", instance);
	}
	
	/* thermal overload in PP chip - DAG6.2RevC */
	if(type & 0x0001) {
		cmn_err(CE_NOTE, "thermal overload (%d)", instance);
	}
	
	// return (DDI_INTR_UNCLAIMED);
	return (DDI_INTR_CLAIMED);
}

/****************************************************************************
 * User context entry points
 ****************************************************************************/

/****************************************************************************/
static int dag_open(dev_t * dev_p, int flag, int otyp, cred_t * cred_p) {
	int minor;
	int aux;
	
	minor = getminor(*dev_p);
	//instance = minor & 0xF; // for single stream for the moment
	aux = minor & 0xFF0;
	cmn_err(CE_NOTE, "Inside dag_open: Major %d: Minor %d Aux %d", getmajor(*dev_p), minor, aux);
	
	switch(aux) {
		case DAG_MINOR:
			cmn_err(CE_NOTE, "opening /dev/dagX");
			break;
			
		case DAGMEM_MINOR:
			cmn_err(CE_NOTE, "opening /dev/dagmemX");
			break;
			
		case DAGIOM_MINOR:
			cmn_err(CE_NOTE, "opening /dev/dagiomX");
			break;
			
		case DAGARM_MINOR:
			cmn_err(CE_NOTE, "opening /dev/dagarmX");
			break;
			
		default:
			cmn_err(CE_NOTE, "error opening DAG device: unknown minor");
			return (DDI_FAILURE);
	}
	
	return (DDI_SUCCESS);
}

/****************************************************************************/
static int dag_close(dev_t dev, int flag, int otyp	, cred_t * cred_p) {
	cmn_err(CE_NOTE, "Inside dag_close");
	return (DDI_SUCCESS);
}

/****************************************************************************
 * Notes about dag_read:
 * we are using a direct copy from the mmaped memory region to the user space
 * although the manual recommends using ddi_put/ddi_get functions. These ones
 * need the destination buffer aligned with the type of data we want to read
 * (8, 16, 32, 64 bits) and we cannot guarantee that. Could also happen the
 * user wants to read an unaligned buffer size.
 * Other factor to take into consideration is the use of uio_p->uio_offset
 * instead of uio_p->uio_loffset. The last one should be used on a 64 bit
 * driver.
 * At that point in time we do not support the normal read function from the card
 * We return Error on read  
 ****************************************************************************/
static int dag_read(dev_t dev, struct uio * uio_p, cred_t * cred_p) {
	int minor;
	int instance;
	int aux_minor;
	iovec_t * iovec_p;
	int amount;
	int res;
	struct dag_softstate *softstate;
	
	/* get card info */
	minor = getminor(dev);
	aux_minor = minor  & 0xFF0;
	
	//FIXME: instead of using the minor to use the dev
	instance = minor & 0xF; 
	//instance = ddi_get_instance(dip);
	
	cmn_err(CE_NOTE, "dag_read: instance %d minor %d", instance, minor);
	cmn_err(CE_NOTE, "dag_read: not supported!!");
	//TODO: feature to support standart read via the driver 
	return (EFAULT);
	
	softstate = ddi_get_soft_state(dag_state, instance);
	
	switch(aux_minor) {
		case DAG_MINOR:
			break;
			
		case DAGMEM_MINOR:
			/* get first io vector */
			iovec_p = uio_p->uio_iov;
			
			/* while there are bytes to transfer */
			while (uio_p->uio_resid > 0 &&
			uio_p->uio_offset < softstate->memhole_size) {
				
				/* how many bytes to copy? */
				amount = MIN(softstate->memhole_size - uio_p->uio_offset,
				iovec_p->iov_len);
				
				/* do the copy */
				res = copyout(softstate->memhole_addr + uio_p->uio_offset,
				iovec_p->iov_base, amount);
				
				if (res != 0)
					return (EFAULT);
				
				/* advance pointers */
				uio_p->uio_offset += amount;
				uio_p->uio_resid -= amount;
				iovec_p++;
			}
			break;
			
		case DAGIOM_MINOR:
			/* get first io vector */
			iovec_p = uio_p->uio_iov;
			
			/* while there are bytes to transfer */
			while (uio_p->uio_resid > 0 &&
			uio_p->uio_offset < softstate->info.iom_size) {
				
				/* how many bytes to copy? */
				amount = MIN(softstate->info.iom_size - uio_p->uio_offset,
				iovec_p->iov_len);
				
				/* do the copy */
				res = copyout(softstate->drb_regs + uio_p->uio_offset,
				iovec_p->iov_base, amount);
				
				if (res != 0)
					return (EFAULT);
				
				/* advance pointers */
				uio_p->uio_offset += amount;
				uio_p->uio_resid -= amount;
				iovec_p++;
			}
			break;
			
		case DAGARM_MINOR:
			break;
	}
	
	return (DDI_SUCCESS);
}

/****************************************************************************/
/****************************************************************************/
static int dag_write(dev_t dev, struct uio * uio_p, cred_t * cred_p) {
	int minor;
	int instance;
	int aux_minor;
	iovec_t * iovec_p;
	int amount;
	int res;
	struct dag_softstate *softstate;
	
	/* get card info */
	minor = getminor(dev);
	//FIXME:
	instance = minor& 0xf;
	//instance = ddi_get_instance(dip);
	softstate = ddi_get_soft_state(dag_state, instance);
	aux_minor = minor  & 0xFF0 ;
	
	switch(aux_minor) {
		case DAG_MINOR:
			break;
			
		case DAGMEM_MINOR:
			/* get first io vector */
			iovec_p = uio_p->uio_iov;
			
			/* while there are bytes to transfer */
			while (uio_p->uio_resid > 0 &&
			uio_p->uio_offset < softstate->memhole_size) {
				
				/* how many bytes to copy? */
				amount = MIN(softstate->memhole_size - uio_p->uio_offset,
				iovec_p->iov_len);
				
				/* do the copy */
				res = copyin(iovec_p->iov_base, softstate->memhole_addr +
				uio_p->uio_offset, amount);
				
				if (res != 0)
					return (EFAULT);
				
				/* advance pointers */
				uio_p->uio_offset += amount;
				uio_p->uio_resid -= amount;
				iovec_p++;
			}
			
			/* on overflow, return success, but write only what it fits */
			uio_p->uio_resid = 0;
			
			break;
			
		case DAGIOM_MINOR:
			/* get first io vector */
			iovec_p = uio_p->uio_iov;
			
			/* while there are bytes to transfer */
			while (uio_p->uio_resid > 0 &&
			uio_p->uio_offset < softstate->info.iom_size) {
				
				/* how many bytes to copy? */
				amount = MIN(softstate->info.iom_size - uio_p->uio_offset,
				iovec_p->iov_len);
				
				/* do the copy */
				res = copyin(iovec_p->iov_base, softstate->drb_regs +
				uio_p->uio_offset, amount);
				
				if (res != 0)
					return (EFAULT);
				
				/* advance pointers */
				uio_p->uio_offset += amount;
				uio_p->uio_resid -= amount;
				iovec_p++;
			}
			
			/* on overflow, return success, but write only what it fits */
			uio_p->uio_resid = 0;
			
			break;
			
		case DAGARM_MINOR:
			break;
	}
	
	return (DDI_SUCCESS);
}

/****************************************************************************
 * Note that IOCTL function is critical when porting the driver to 64-bit.
 * All the data transfers between user processes and the kernel need to be
 * checked and converted if necessary.
 ****************************************************************************/
static int dag_ioctl(dev_t dev, int cmd, intptr_t arg, int mode,
cred_t * cred_p, int * rval_p) {
	
	int res;
	int minor;
	int instance;
	daginf_t info;
	uint32_t sub_cmd;
	uint32_t lock;
	pid_t pid;
	int stream;
	struct dag_softstate *softstate;
	
	/* get card info */
	minor = getminor(dev);
	//FIXME:
	//CHECKME: why we locak on minor devided two 2
	//if the lock is per card it should be according the stream numbers for that card
	//if the lock is per RX or TX streams it is OK
	//if the lock is per stream it should not devide by two
	//	instance = minor >> 2;
	
	//FIXME : not to use the minor 
	instance = minor & 0xF;
	softstate = ddi_get_soft_state(dag_state, instance);
	//----------------
	
	//FIXME: some of the Resets and other thinks should be locked because of
	//multithreaded enviremnt in the kernel drivers like RESET
	
	cmn_err(CE_NOTE, "ioctl, cmd: %d, ninor: %d", cmd, minor);
	
	switch (cmd) {
		case DAGIOCINFO:
			cmn_err(CE_NOTE, "issued DAGIOCINFO ioctl command");
			
			info.id = (uint32_t) instance;
			info.phy_addr = (uint32_t) softstate->cookie.dmac_address;
			info.buf_size = (uint32_t) softstate->cookie.dmac_size;
			info.iom_size = (uint32_t) softstate->info.iom_size;
			info.device_code = (uint16_t) softstate->info.device_code;
			
			#if 1
			cmn_err(CE_NOTE, "dag_ioctl: info.id: 0x%08x", info.id);
			cmn_err(CE_NOTE, "dag_ioctl: info.phy_addr: 0x%08x", info.phy_addr);
			cmn_err(CE_NOTE, "dag_ioctl: softstate->cookiedmac_address (phy_addr): 0x%08x", softstate->cookie.dmac_address);
			cmn_err(CE_NOTE, "dag_ioctl: info.buf_size: 0x%08x", info.buf_size);
			cmn_err(CE_NOTE, "dag_ioctl: info.iom_size: 0x%08x", info.iom_size);
			cmn_err(CE_NOTE, "dag_ioctl: info.device_code: 0x%04x", info.device_code);
			cmn_err(CE_NOTE, "dag_ioctl: sizeof(daginf_t): %d", sizeof(daginf_t));
			#endif
			
			res = ddi_copyout(&info, (void *)arg, sizeof(daginf_t), mode);
			if (res != 0) {
				cmn_err(CE_NOTE, "ioctl ddi_copyout failed");
				return (EFAULT);
			}
			
			break;
			
		case DAGIOCRESET:
			cmn_err(CE_NOTE, "issued DAGIORESET ioctl command");
			res = ddi_copyin((void *)arg, &sub_cmd, sizeof(uint32_t), mode);
			if (res !=0) return (EFAULT);
			
			switch(sub_cmd) {
				case DAGRESET_FULL:
					cmn_err(CE_NOTE, "issued DAGRESET_FULL ioctl subcommand");
					res = dag_reset(instance);
					break;
					
				case DAGRESET_REBOOT:
					cmn_err(CE_NOTE, "issued DAGRESET_REBOOT ioctl subcommand");
					res = dag_pciconfig_restore(softstate);/* rewrite pci conf */
					if (res == DDI_FAILURE) return (EFAULT);
					
					dag_iowrite(softstate, IntEn, 0xffff);	/* Re-enable ints */
					//FIXME:
					//duck_init(softstate);	/* restart duck */
					dagmon_init(softstate);
					break;
					
				case DAGRESET_DUCK:
					cmn_err(CE_NOTE, "issued DAGRESET_DUCK ioctl subcommand");
					//FIXME:
					//duck_init(softstate); /* restart duck */
					break;
					
				default:
					cmn_err(CE_NOTE, "Unknown IOCRESET type %d\n", sub_cmd);
					break;
			}
			break;
			
		case DAGIOCLOCK:
			cmn_err(CE_NOTE, "issued DAGIOCLOCK ioctl command");
			res = ddi_copyin((void *)arg, &lock, sizeof(uint32_t), mode);
			if (res != 0) return (EFAULT);
			
			stream = lock >> 16;
			if (stream > DAG_STREAM_MAX) {
				return (EFAULT);
			}
			
			pid = ddi_get_pid();
			
			mutex_enter(&softstate->instance_mutex);
			
			res = 0;
			switch (lock & 0xffff) {
				case 0: /* unlock */
					if (softstate->lock[stream] == pid) {
						softstate->lock[stream] = 0;
						cmn_err(CE_NOTE, "pid %d unlocked stream %d", pid, stream);
					} else {
						res = EACCES;
					}
					break;
					
				case 1: /* lock */
					if (softstate->lock[stream] == 0) {
						softstate->lock[stream] = pid;
						cmn_err(CE_NOTE, "pid %d locked stream %d", pid, stream);
					} else if (softstate->lock[stream] == pid) {
						/* ok - multiple locks */
					} else {
						res = EACCES;
					}
					break;
					
				default: /* unimplemented lock system */
					break;
			}
			
			mutex_exit(&softstate->instance_mutex);
			
			if (res) return (res);
			
			break;
		case DAGIOCDUCK:
			cmn_err(CE_NOTE, "issued DAGIOCDUCK ioctl command not supported at the moment ");
			/*  Linux version 
				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;
			*/
			break;
		default:
			cmn_err(CE_NOTE, "unknown ioctl command");
			return (ENOTTY);
	}
	
	return (0);
}

/****************************************************************************
 * This function is called on behalf of user's mmap system call.
 ****************************************************************************/
static int
dag_devmap(dev_t dev, devmap_cookie_t handle, offset_t off, size_t len,
size_t *maplen, uint_t model) {
	int res;
	int minor;
	int instance;
	int aux;
	off_t reg_size;
	dev_info_t * dip;
	offset_t aux_off;
	struct dag_softstate *softstate; //xsp
	int      error;
	/* round up len to a multiple of a page size */
	
	/* get card info */
	minor = getminor(dev);
	//FIXME: at the moment we get the minor as instance not the real instance
	//it woorks fine like that if needs to be changed then needs to be changed every where 
	instance = minor & 0xF; 
	aux = minor & 0xFF0;
	
	softstate = ddi_get_soft_state(dag_state, instance);
	if (softstate == NULL) {
		cmn_err(CE_NOTE, "dag_devmap: softstate not allocated", off, len);
		return (ENXIO);
	}
	
	cmn_err(CE_NOTE, "dag_devmap: instance %d minor %d", instance, minor);
	cmn_err(CE_NOTE, "dag_devmap: offset %d len %d maplen %d", off, len, *maplen);
	//TODO: working with 64bit APIs at the moment driver works in 64bit and api in 32bit
	if (ddi_model_convert_from(model) == DDI_MODEL_ILP32)
		cmn_err(CE_NOTE, "dag_devmap: DDI_MDEL_ILP32 32bit bodel", off, len);
	else {
		cmn_err(CE_NOTE, "dag_devmap: DDI_MDEL_ILP64 64bit bodel", off, len);
		return (ENXIO);
	}
	
	switch(aux) {
		case DAG_MINOR:
			cmn_err(CE_NOTE, "DAG_MINOR trying to mmap memory hole off: 0x%x, len: %d",  off,  len);
			cmn_err(CE_NOTE, "DAG_MINOR trying to mmap memory hole (minor & 0xF=%d)(/dev/dag %d) off: %d, len: %d", minor & 0xF, instance, (uint32_t) off, (uint32_t) len);
			//			cmn_err(CE_NOTE, "DAG_MINOR User space address: 0x%x",  handle->cvaddr,  len);
			//			cmn_err(CE_NOTE, "DAG_MINOR Kernel address  address: 0x%08x",  softstate->umem_cookie.cvaddr);
			/* round up len to a multiple of a page size */
			//len = ptob(btopr(len));
			
			/* off and len must be page aligned */
			len = ptob(btopr(len));
			aux_off = ptob(btopr(off));
			if (aux_off != off) {
				cmn_err(CE_NOTE, "[dag_devmap] offset must be page aligned");
				return (ENXIO);
			}
			
			/* Necessary sanity check */
			if (off + len > softstate->memhole_size) {
				cmn_err(CE_NOTE, "[dag_devmap] map out of bounds");
				return (ENXIO);
			}
			
			/* Set up the user mapping */
			
			//error = devmap_setup(dev, (offset_t)off, asp, addrp, len,
			//	    prot, maxprot, flags, credp);
			//fix  the offset and len properly
			
			/* Now we have a cookie we can use to export the memory hole */
			res = devmap_umem_setup(handle,
			softstate->dip, NULL, softstate->umem_cookie,
			off, len, PROT_ALL, DEVMAP_DEFAULTS, &dag_device_attr);
			/* Now we have a cookie we can use to export the memory hole */
			//			res = devmap_umem_setup(handle,
			//			softstate->dip, NULL, softstate->umem_cookie,
			//			off, len, PROT_ALL, DEVMAP_DEFAULTS, &dag_device_attr);
			if(res){
				cmn_err(CE_NOTE, "dag_devmap: Mapping Memory hole");
			}
			
			*maplen = len;
			return (res);
			
			break;
			
		case DAGMEM_MINOR:
			cmn_err(CE_NOTE, "dag_devmap: DAGMEM_MINOR cannot mmap dagmem device");
			return (-1);
			
		case DAGIOM_MINOR:
			cmn_err(CE_NOTE, "dag_devmap: DAGIOM_MINOR  mmap dagmem device");
			/* check iom size */
			//	We know if the driver is attached that this is already mapped to the driver space
			// If we use the other aproach to map the kernel mapping then we double not need this
			// The kernel needs mapping to the DRB space beacus of DUCK and interrupts
			//may be it is a good to verify the dip and softstate->dip if the same for debugging
			res = ddi_dev_regsize(softstate->dip, 1, &reg_size);
			if (res != DDI_SUCCESS) {
				return (-1);
			}
			cmn_err(CE_NOTE, "dag_devmap: reg_size: %d", reg_size);
			
			/* round up len to a multiple of a page size */
			len = ptob(btopr(len));
			if (off + len > reg_size) {
				cmn_err(CE_NOTE, "dag_devmap: off_len bigger then the registry size ");
				return (-1);
			}
			cmn_err(CE_NOTE, "dag_devmap: len %d after alignment, maplen %d ", len, maplen);
			
			/* set up device mapping */
			//we can use different algorith because we already have it mapped into the kernel space
			//we can use the same aproach as for the memory hole
			res = devmap_devmem_setup(handle, softstate->dip, NULL, 1, off, len,
			PROT_ALL, DEVMAP_DEFAULTS, &dag_drb_device_attr);
			
			*maplen = len;
			cmn_err(CE_NOTE, "dag_devmap: Export DRB finished maplen=%d\n", *maplen);
			return (res);
			
		case DAGARM_MINOR:
			cmn_err(CE_NOTE, "dag_devmap: cannot mmap dagarm device");
			return (-1);
			break;
			
	}
	
	cmn_err(CE_NOTE, "dag_devmap: not suppoted or implemented");
	return -1;
	
	//	return (ENXIO);
	
	
	//	 if (ddi_model_convert_from(model) == DDI_MODEL_ILP32)
	/* request from 32-bit application. Skip first page */
	//		 off += ptob(1);
	/* export the memory to the application */
	//	 error = devmap_umem_setup(handle, softstate->dip, NULL, softstate->memhole_addr,
	//	 off, len, PROT_ALL, DEVMAP_DEFAULTS, NULL);
	//	 *maplen = len;
	//	 return (error);
	
	
	
	
	
	
	
	//FIXME: thread protection maybe
	
	
	//FIXME:
	//instance = minor >> 2;
	//	instance = 0;
	//	softstate = ddi_get_soft_state(dag_state, instance);
	//	dip = softstate->dip;
	//	cmn_err(CE_NOTE, "dag_devmap: instance %d minor %d", instance, minor);
	
	/* FIXME For a 64-bit driver 'model' has to be processed */
	
	/* From which minor are we receivin the mmap? */
	//	aux = minor & 0xFF0 ;
	//	cmn_err(CE_NOTE, "dag_devmap: instance %d, ", instance, aux);
	
	return (-1);
}

/****************************************************************************
 * Helper functions - Non entry points
 ****************************************************************************/
//TODO: implement it
static void
dag_stop_dma(struct dag_softstate *softstate) {
	/* pause bus master (needed for the following reset register) */
	//I810_BM_PUT8 (I810_BM_PI_CR,       0x0);
	//I810_BM_PUT8 (I810_BM_PO_CR,       0x0);
	//I810_BM_PUT8 (I810_BM_MC_CR,       0x0);
	
	/* and reset the bm registers for a three dma engines */
	//I810_BM_PUT8 (I810_BM_PI_CR,       I810_BM_CONTROL_RESETREGS);
	//I810_BM_PUT8 (I810_BM_PO_CR,       I810_BM_CONTROL_RESETREGS);
	//I810_BM_PUT8 (I810_BM_MC_CR,       I810_BM_CONTROL_RESETREGS);
}


/*
 * Cleanup all resources allocated by the driver during attach time
 * and during run-time, in reverse order.
 */
static int
dag_cleanup(struct dag_softstate *softstate) {
	/*    if (softstate->audio_handle) {
	 if (audio_hlp_detach(softstate->audio_handle, DDI_DETACH) != DDI_SUCCESS)
	 return DDI_FAILURE;
	 }
	
	 if (softstate->codec_handle) {
	 CODEC_DELETE(softstate->codec_handle);
	 softstate->codec_handle = NULL;
	 }
	 */
	
	if (softstate->memhole_acc_handle != NULL) {
		
		/*
		 * just to be sure, turn off DMA, before releasing the DMA
		 * buffers or buffer
		 */
		dag_stop_dma(softstate);
		
		/* This is example for mutliple dma allocations free audio sample buffer DMA memory */
		//        dag_free_sample_buffer(softstate, &softstate->play_buf);
		//       dag_free_sample_buffer(softstate, &softstate->record_buf);
		
		//unlock the memory this
		if(softstate->umem_cookie != NULL ){
			cmn_err(CE_NOTE, "dag_clean: Unlock memory ");
			ddi_umem_unlock(softstate->umem_cookie);
		}
		
		/* free the main memory hole DMA memory for the BM or CSBM engine*/
		if (softstate->memhole_dma_bound) {
			cmn_err(CE_NOTE, "dag_clean: Unbound dma from memory hole");
			ddi_dma_unbind_handle(softstate->memhole_dma_handle);
		}
		cmn_err(CE_NOTE, "dag_clean: Free Memory Hole");
		ddi_dma_mem_free(&softstate->memhole_acc_handle);
		softstate->memhole_addr = NULL;
	}
	
	
	if (softstate->memhole_dma_handle) {
		cmn_err(CE_NOTE, "dag_clean: Free DMA handle ");
		ddi_dma_free_handle(&softstate->memhole_dma_handle);
	}
	
	/*
	 * remove interrupt handler before we free the card's mapped registers
	 * otherwise an interrupt could access softstate->regs_handle in our
	 * interrupt handler
	 */
	
	if (softstate->intr_cookie != NULL) {
		cmn_err(CE_NOTE, "dag_clean: interrupt cockie clean");
		ddi_remove_intr(softstate->dip, 0, softstate->intr_cookie);
	}
	
	// We do not use power managment
	// if (pm_lower_power(softstate->dip, 0, 0) != DDI_SUCCESS) {
	// cnt_err("Failed to disable device power.");
	//}
	
	//Releases the kernel mapped DRB registers
	if (softstate->drb_regs_handle != NULL) {
		cmn_err(CE_NOTE, "dag_clean: unmap drb registers from the kernel space");
		ddi_regs_map_free(&softstate->drb_regs_handle);
	}
	/*
	 if (softstate->am_regs_handle != NULL) {
	 ddi_regs_map_free(&softstate->am_regs_handle);
	 }
	 */
	/* cleans the handle for PCI configuration space access*/
	if (softstate->pci_conf_handle != NULL) {
		cmn_err(CE_NOTE, "dag_clean: Cleans the handle for PCI configurations space access");
		pci_config_teardown(&softstate->pci_conf_handle);
	}
	
	/*destroys the mutexes
	 Note: The mutexes should be not used by eny other thread at that point
	 This can be done by verifying in the detach procces that all functions are
	 finished there execution
	 */
	cmn_err(CE_NOTE, "dag_clean: Destroy the instance and interrupt mutexes");
	mutex_destroy(&softstate->instance_mutex);
	mutex_destroy(&softstate->intr_mutex);
	
	cmn_err(CE_NOTE, "dag_clean: Free of the structure allocated for card and driver information");
	ddi_soft_state_free(dag_state, ddi_get_instance(softstate->dip));
	
	return DDI_SUCCESS;
	//TO VERIFY
	/* destroy duck */
	//if (softstate->duck)
	//	duck_destroy(softstate);
	
	/* is this really needed? */
	//		softstate->dip = 0; /* should not be NULL? */
	
}


/****************************************************************************/
static int dag_reset(int instance) {
	uint32_t temp;
	struct dag_softstate *softstate = ddi_get_soft_state(dag_state, instance);
	int res;
	
	//probably we need the two lokings here if the software has not been done before
	mutex_enter(&softstate->instance_mutex);
	mutex_enter(&softstate->intr_mutex);
	cmn_err(CE_NOTE, "dag_reset:");
	
	temp = dag_ioread(softstate, Ctrl);
	temp |= 0x40000000;
	dag_iowrite(softstate, Ctrl, temp);
	drv_usecwait(1000);
	temp &= ~0x40000000;
	dag_iowrite(softstate, Ctrl, temp);
	
	res = dag_pciconfig_restore(softstate);
	if (res != 0) return res;
	
	dag_iowrite(softstate, IntEn, 0xffff);	/* Re-enable ints */
	//FIXME:
	//duck_init(softstate);
	dagmon_init(softstate);
	
	mutex_exit(&softstate->intr_mutex);
	mutex_exit(&softstate->instance_mutex);
	
	return 0;
	
}

/****************************************************************************/
/*
 * Configure the PCI device, so that the device is ready to
 * accept I/O requests.  This includes enabling PCI busmaster,
 * and anabling access to the device's I/O registers.
 */
static int
dag_pci_setup(struct dag_softstate *softstate) {
	dev_info_t *dip = softstate->dip;
	ddi_acc_handle_t conf_handle = softstate->pci_conf_handle;
	uint16_t pci_status;
	uint16_t pci_vendorid;
	uint16_t pci_deviceid;
	uint8_t  pci_revision;
	uint8_t  pci_intrinfo;
	uint16_t pci_commandreg;
	uint16_t enable_access;
	uint8_t  pm_level;
	char *revstr = "";
	
	pci_status   = pci_config_get16(conf_handle, PCI_CONF_STAT);
	pci_vendorid = pci_config_get16(conf_handle, PCI_CONF_VENID);
	pci_deviceid = pci_config_get16(conf_handle, PCI_CONF_DEVID);
	pci_revision = pci_config_get8(conf_handle, PCI_CONF_REVID);
	pci_intrinfo = pci_config_get8(conf_handle, PCI_CONF_ILINE);
	
	
	
	switch (pci_deviceid) {
		case 0x454e: revstr = "DAG:4.5G4"; break;
		default:
			revstr="DAG: UNKNOWN";
	}
	cmn_err(CE_NOTE, "dag_pci_setup: card  %s ,pci_status=0x%x , pci_vendorid=0x%x , pci_deviceid=0x%x , pci_revision=0x%x, pci_intrinfo=0x%x", revstr, pci_status, pci_vendorid, pci_deviceid, pci_revision , pci_intrinfo);
	
	/* cache some device information */
	softstate->info.device_code = pci_deviceid;
	
	//CHECKME: Do we need the pci Revision ??
	//softstate->rev = pci_revision;
	//softstate->revstr = revstr;
	
	/* Announce the hw we're attaching to */
	ddi_report_dev(dip);
	cmn_err(CE_NOTE, "?PCI-device: %s, dag %d, %s%s(rev. 0x%02x) at irq %d\n",
	ddi_node_name(dip),
	ddi_get_instance(dip),
	revstr, revstr[0] ? " " : "",
	pci_revision,
	pci_intrinfo);
	
#if i386
    /*
     * reject interrupt line 255, per PCI spec 6.2.4, footnote 43,
     * this means "unknown" or "not connected" to the interrupt
     * controller on x86 based PCs.
     * Sun drivers (iprb) reject iline 0 as well.
     */
    if (pci_intrinfo == 0 || pci_intrinfo == 255) {
	    cmn_err(CE_WARN, "interrupt line value out of range: %d", pci_intrinfo);
	    return DAG_FAILURE;
    }
#endif
    
    pci_commandreg = pci_config_get16(conf_handle, PCI_CONF_COMM);
    cmn_err(CE_NOTE, "pci command register 0x%x %s%s%s%s",
    pci_commandreg,
    pci_commandreg & PCI_COMM_SERR_ENABLE ? " serr#_en"  : "",
    pci_commandreg & PCI_COMM_ME  ? " busmaster" : "",
    pci_commandreg & PCI_COMM_MAE ? " mem_access" : "",
    pci_commandreg & PCI_COMM_IO  ? " io_access" : "");
    
    enable_access = PCI_COMM_IO | PCI_COMM_ME;
    enable_access |= PCI_COMM_MAE;  /* card has mmap'ed I/O regs */
    
    if ((pci_commandreg & enable_access) == enable_access) {
	    cmn_err(CE_NOTE, "busmastering and io_access already enabled");
    } else {
	    cmn_err(CE_NOTE, "Enabling PCI I/O and bus mastering access");
	    return DAG_FAILURE;
	    //FIXME: Temporary remove changing
	    //pci_config_put16(conf_handle, PCI_CONF_COMM, pci_commandreg | enable_access);
    }
    
    /*
     * If the device isn't in D0 full power state, make sure that
     * there are valid saved pci config registers that we can restore
     * in the power entry point, when the device is powered up.
     */
    //this is not needed but probably a good idea to verify
    //if the device is not in D0 mode whenb the driver starts we can not be sres for PCI
    //space
    //pm_level = dag_pci_device_powerlevel(softstate);
    //if (pm_level != PCI_PMCSR_D0) {
    //		cmn_err(CE_WARN, "Device is in PCI D%d power state, "
    //		"and not in PCI D0 (full power) state.", pm_level);
    //  };
    //This is done by the dag_attach
    //    if (pci_save_config_regs(dip) != DDI_SUCCESS) {
    //	    cmn_err(CE_WARN, "failed to save pci config regs");
    //    }
    
    // we do not need this
    //softstate->saved_iline =
    //    pci_config_get8(conf_handle, PCI_CONF_ILINE);
    
    
    return DAG_SUCCESS;
}


//copy from PCI CONFIG REGISTERS to loac structure
// we use on solaris kernel function to double that fo us
//
/*static int dag_pciconfig_save(struct dag_softstate *softstate) {
 dag_pciconf_t * conf;
 //	struct dag_softstate *softstate = ddi_get_soft_state(dag_state, instance);
 int res;

 //this are for quick access for the instance
 //probably not needed as well for next releases
 // it should have 32/64 bit address verificvations
 // while the functions we use from the DDI will double that for us
 conf = &softstate->pciconf;

 //all accesse to the PCI configuration registry spaces
 //are done via the softstate->pci_conf_handle
 if (softstate->pci_conf_handle == NULL ) {
 cmn_err(CE_NOTE, "dag_pciconfig_save: failed pci_conf_handle not setup.");
 return (DAG_FAILURE);
 }

 // Store PCI config registers
 //has to take care about 64bit firmaware
 conf->base0 = pci_config_get32(softstate->pci_conf_handle, PCI_CONF_BASE0);
 conf->base1 = pci_config_get32(softstate->pci_conf_handle, PCI_CONF_BASE1);
 conf->base2 = pci_config_get32(softstate->pci_conf_handle, PCI_CONF_BASE2);
 conf->command = pci_config_get16(softstate->pci_conf_handle, PCI_CONF_COMM);
 conf->latency_timer = pci_config_get8(softstate->pci_conf_handle, PCI_CONF_LATENCY_TIMER);
 conf->interrupt_line = pci_config_get8(softstate->pci_conf_handle, PCI_CONF_ILINE);

 // cache some device information
 softstate->info.device_code =	pci_config_get16(softstate->pci_conf_handle, PCI_CONF_DEVID);


 return (DDI_SUCCESS);
 }
 */

/* Waiting for the Xilinx chip to become accessable */
static int dag_xilinx_ready_wait(struct dag_softstate *softstate) {
	int ready;
	int loop;
	uint32_t val;
	
	cmn_err(CE_NOTE, "dag_xilinx_ready_wait: begin");
	if (softstate->pci_conf_handle == NULL) {
		cmn_err(CE_NOTE, "dag_xilinx_ready_wait: failed pci_config_setup.");
		return (DAG_FAILURE);
	}
	
	/* Waiting for the Xilinx chip to become accessable */
	loop = 0;
	ready = 0;
	while (loop < 10000 && !ready) {
		val = pci_config_get32(softstate->pci_conf_handle, PCI_CONF_BASE0);
		if (val == 0) {
			ready = 1;
		} else {
			loop++;
			drv_usecwait(10000);
		}
	}
	
	if (!ready) {
		cmn_err(CE_NOTE, "dag_xilinx_ready_wait: FPGA not responding");
		//pci_config_teardown(&dag_pci_handle);
		return (DAG_FAILURE);
	}
	cmn_err(CE_NOTE, "dag_xilinx_ready_wait: end");
	
	return ( DAG_SUCCESS );
	
}

/****************************************************************************/
//copy from the local copy to PCI configurations space
//

static int dag_pciconfig_restore(struct dag_softstate *softstate) {
	//	dag_pciconf_t * conf;
	int res;
	//	int loop;
	//	uint32_t val;
	//	struct dag_softstate *softstate = ddi_get_soft_state(dag_state, instance);
	
	/* use a shortcut */
	//	conf = &softstate->pciconf;
	
	/* Setup PCI card to read its configuration registers */
	//we use the handle already setup at the attach time
	cmn_err(CE_NOTE, "dag_pciconfig_restore: waiting for xilinx to get back");
	res = dag_xilinx_ready_wait(softstate);
	if(res != DAG_SUCCESS) {
		cmn_err(CE_NOTE, "dag_pciconfig_restore: xilinx  wait returned an error");
		return res;
	}
	if (pci_restore_config_regs(softstate->dip) == DDI_FAILURE) {
		cmn_err(CE_NOTE, "dag_pci_restore: could not restore the PCI configuration space");
		return  DDI_FAILURE;
	}
	cmn_err(CE_NOTE, "pci configuration registers restored successfully");
	/* Remeber again the values of PCI configuration space
	* for next restore 
	* the driver functions expect save -> restore and does not work in mode save ->restore-> restore the second restore will fail
	*/
	if (pci_save_config_regs(softstate->dip) != DDI_SUCCESS) {
		cmn_err(CE_WARN, "dag_pciconfig_restore: failed to save pci config regs");
	}

	//we might not need this
	//CHECK ME:
	cmn_err(CE_NOTE, "pci configuration registers restored successfully");
	/* Restore PCI config registers why*/
	//pci_config_put32(softstate->pci_conf_handle, PCI_CONF_BASE0, conf->base0);
	//pci_config_put32(softstate->pci_conf_handle, PCI_CONF_BASE1, conf->base1);
	//pci_config_put32(softstate->pci_conf_handle, PCI_CONF_BASE2, conf->base2);
	//pci_config_put16(softstate->pci_conf_handle, PCI_CONF_COMM, conf->command);
	//pci_config_put8(softstate->pci_conf_handle, PCI_CONF_LATENCY_TIMER, conf->latency_timer);
	//pci_config_put8(softstate->pci_conf_handle, PCI_CONF_ILINE, conf->interrupt_line);
	
	//cmn_err(CE_NOTE, "pci configuration registers restored successfully");
	
	return (DDI_SUCCESS);
}

#if 0
/****************************************************************************/
static void duck_init(struct dag_softstate *softstate) {
	
}
#endif

/****************************************************************************/
static void dagmon_init(struct dag_softstate *softstate) {
	
}

/****************************************************************************/
inline uint32_t dag_ioread(struct dag_softstate *softstate, uint32_t offset) {
	if( offset >= softstate->drb_regs_size)
		cmn_err(CE_NOTE, "ERROR offset out of range [dag_ioread] offset: 0x%04x", offset);
	
	return ddi_get32( softstate->drb_regs_handle, (uint32_t *) (softstate->drb_regs+offset));
}
/****************************************************************************/
inline void dag_iowrite(struct dag_softstate *softstate, uint32_t offset, uint32_t value) {
	
	if(offset >= softstate->drb_regs_size)
		cmn_err(CE_NOTE, "ERROR offset out of range [dag_ioread] offset: 0x%04x", offset);
	ddi_put32(softstate->drb_regs_handle, (uint32_t *)(softstate->drb_regs+offset), value);
}


