/* API - Any Device - Linux */
/* $Workfile: gsc_os_drv.c $ */
/* $Revision: 8 $ */
/* $Date: 3/04/08 4:34p $ */
/* $Log: /NEW SOURCESAFE DATABASE/SDK/common/api/linux/gsc_os_drv.c $
 * 
 * 8     3/04/08 4:34p Dnewbold
 * Added #error statements for undefined DEV_USES_XXX macros.
 * 
 * 7     2/10/08 9:17p Dnewbold
 * Updated error text.
 * 
 * 6     10/27/07 1:47p Dnewbold
 * Corrected some error text.
 * 
 * 5     10/27/07 10:16a Dnewbold
 * API porting updates.
 * 
 * 4     4/04/07 4:22p Dnewbold
 * Additional bug fixing.
 * 
 * 3     4/04/07 10:15a Dnewbold
 * gsc_drv_version_get() now returns the version number rather than the
 * device name.
 * 
 * 2     4/03/07 4:44p Dnewbold
 * #If 0'd out some code due to Linux behaviours.
 * 
 * 1     4/02/07 4:59p Dnewbold
*/

#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/types.h>

#include "dev_drv.h"

#include "gsc_common.h"
#include "gsc_driver.h"
#include "gsc_access.h"
#include "gsc_drv.h"
#include "gsc_key.h"

#include "gsc_os_error.h"

#include "dev_version.h"
#include "dev_os_version.h"
#include "gsc_version.h"
#include "gsc_os_version.h"



#if (MAKEFILE_API_DEV_VERSION != API_DEV_VERSION)
	#error "--------------------------------------------------------------------------"
	#error "MAKEFILE_API_DEV_VERSION != API_DEV_VERSION                               "
	#error "SRC: sdk/DEVICE/api/dev_version.h                      API_DEV_VERSION    "
	#error "MOD: sdk/DEVICE/api/linux/gcc/makefile        MAKEFILE_API_DEV_VERSION    "
#endif

#if (MAKEFILE_API_DEV_OS_VERSION != API_DEV_OS_VERSION)
	#error "--------------------------------------------------------------------------"
	#error "MAKEFILE_API_DEV_OS_VERSION != API_DEV_OS_VERSION                         "
	#error "SRC: sdk/DEVICE/api/linux/dev_os_version.h             API_DEV_OS_VERSION "
	#error "MOD: sdk/DEVICE/api/linux/gcc/makefile        MAKEFILE_API_DEV_OS_VERSION "
#endif

#if (MAKEFILE_API_GSC_VERSION != API_GSC_VERSION)
	#error "--------------------------------------------------------------------------"
	#error "MAKEFILE_API_GSC_VERSION != API_GSC_VERSION                               "
	#error "SRC: sdk/common/api/gsc_version.h                      API_GSC_VERSION    "
	#error "MOD: sdk/DEVICE/api/linux/gcc/makefile        MAKEFILE_API_GSC_VERSION    "
#endif

#if (MAKEFILE_API_GSC_OS_VERSION != API_GSC_OS_VERSION)
	#error "--------------------------------------------------------------------------"
	#error "MAKEFILE_API_GSC_OS_VERSION != API_GSC_OS_VERSION                         "
	#error "SRC: sdk/common/api/linux/gsc_os_version.h             API_GSC_OS_VERSION "
	#error "MOD: sdk/DEVICE/api/linux/gcc/makefile        MAKEFILE_API_GSC_OS_VERSION "
#endif

#if (MAKEFILE_API_DEV_API_VERSION != API_DEV_API_VERSION)
	#error "--------------------------------------------------------------------------"
	#error "MAKEFILE_API_DEV_API_VERSION != API_DEV_API_VERSION                       "
	#error "SRC: sdk/DEVICE/api/DEVICE_api.h              XXXXX_API_VERSION           "
	#error "MOD: sdk/DEVICE/api/linux/gcc/makefile        MAKEFILE_API_DEV_API_VERSION"
	#error "VIA: sdk/DEVICE/api/dev_version.h                      API_DEV_API_VERSION"
#endif



/*****************************************************************************
*
*	Function:	_irq_wait
*
*	Purpose:
*
*		Wait for an interrupt or a timeout.
*
*	Arguments:
*
*		dev			The device to access.
*
*		secs		The number of seconds to wait. The min is 1, the max is 60.
*
*	Returned:
*
*		GSC_SUCCESS	All went well.
*		else		An appropriate error status.
*
*****************************************************************************/

#if DEV_USES_IRQ

static U32 _irq_wait(os_device_t* dev, __u32 secs)
{
	int				i;
	gsc_drv_irq_t	irq;
	U32				status;

	OS_DEVICE_T_KEY_VERIFY(dev);
	memset(&irq, 0, sizeof(irq));

	if (secs < 1)
		irq.wait_seconds	= 1;
	else if (secs > 60)
		irq.wait_seconds	= 60;
	else
		irq.wait_seconds	= secs;

	i		= ioctl(OS_DEVICE_HANDLE(dev), GSC_IOCTL_IRQ_WAIT, &irq);

	if (i)
		status	= os_errno_2_status(errno);
	else
		status	= irq.status;

	return(status);
}

#elif ! defined(DEV_USES_IRQ)
	#error DEV_USES_IRQ undefined. Must #include dev_drv.h.
#endif



/*****************************************************************************
*
*	Function:	_irq_wait_cancel
*
*	Purpose:
*
*		Cancel an active wait request.
*
*	Arguments:
*
*		dev			The device to access.
*
*	Returned:
*
*		GSC_SUCCESS	All went well.
*		else		An appropriate error status.
*
*****************************************************************************/

#if DEV_USES_IRQ

static U32 _irq_wait_cancel(os_device_t* dev)
{
	int				i;
	gsc_drv_irq_t	irq;
	U32				status;

	OS_DEVICE_T_KEY_VERIFY(dev);
	memset(&irq, 0, sizeof(irq));
	irq.wait_cancel	= 1;
	i				= ioctl(OS_DEVICE_HANDLE(dev),
							GSC_IOCTL_IRQ_WAIT_CANCEL,
							&irq);

	if (i)
		status	= os_errno_2_status(errno);
	else
		status	= irq.status;

	return(status);
}

#elif ! defined(DEV_USES_IRQ)
	#error DEV_USES_IRQ undefined. Must #include dev_drv.h.
#endif



/*****************************************************************************
*
*	Function:	_master_irq_thread
*
*	Purpose:
*
*		This is the master interrupt thread. Here, we wait for an interrupt
*		to occur, then we trigger the main event.
*
*	Arguments:
*
*		arg	The thread's entry argument.
*
*	Returned:
*
*		0
*
*****************************************************************************/

#if DEV_USES_IRQ

static U32 _master_irq_thread(void* arg)
{
	os_device_t*	device	= (os_device_t*) arg;
	gsc_drv_irq_t	irq;
	U32				status;
	int				i;

	OS_DEVICE_T_KEY_VERIFY(device);
	memset(&irq, 0, sizeof(irq));
	i			= ioctl(OS_DEVICE_HANDLE(device),
						GSC_IOCTL_IRQ_STATUS,
						&irq);
	irq.notify	|= GSC_IRQ_PCI;
	i			= ioctl(OS_DEVICE_HANDLE(device),
						GSC_IOCTL_IRQ_NOTIFY,
						&irq);

	for (;;)	// Keep working until we're told to quit or there is an error.
	{
		if (device->master.quit)
			break;

		status	= _irq_wait(device, 1);

		if (status == GSC_WAIT_TIMEOUT)
			continue;

		if (status != GSC_SUCCESS)
		{
			device->master.error	= 1;
			break;
		}

		status	= os_event_trigger(device->master.event);

		if (status != GSC_SUCCESS)
		{
			device->master.error	= 1;
			break;
		}
	}

	return(0);
}

#elif ! defined(DEV_USES_IRQ)
	#error DEV_USES_IRQ undefined. Must #include dev_drv.h.
#endif



/*****************************************************************************
*
*	Function:	_reg_bar_mod
*
*	Purpose:
*
*		Perform a read-modify-write on a memory mapped register from the
*		given Base Address Register region.
*
*	Arguments:
*
*		device		The data used to access the device.
*
*		bar			The Base Address Register region to access.
*					A value of -1 refers to the PCI registers.
*
*		offset		The offset of the register to access.
*					Alligment must be legitimate.
*
*		size		The size of the register to access.
*					Permitted sizes are: 1, 2, 3 or 4 bytes.
*
*		value		The value is recorded here. Excess bits are zero.
*
*		mask		These are the set of value bits to modify.
*
*	Returned:
*
*		GSC_SUCCESS	All went well.
*		else		An appropriate error status.
*
*****************************************************************************/

static U32 _reg_bar_mod(gsc_device_t*	device,
						S8				bar,
						U16				offset,
						U8				size,
						U32				value,
						U32				mask)
{
	int				i;
	gsc_drv_reg_t	reg;
	U32				status;

	if (device)
	{
		OS_DEVICE_T_KEY_VERIFY(&device->device);
		reg.bar		= bar;
		reg.offset	= offset;
		reg.size	= size;
		reg.value	= value;
		reg.mask	= mask;
		reg.status	= GSC_FAILED;
		i			= ioctl(OS_DEVICE_HANDLE(&device->device),
							GSC_IOCTL_REG_MOD,
							&reg);

		if (i)
			status	= os_errno_2_status(errno);
		else
			status	= reg.status;

		OS_DEVICE_T_KEY_VERIFY(&device->device);
	}
	else
	{
		status	= GSC_NULL_PARAM;
	}

	return(status);
}



/*****************************************************************************
*
*	Function:	_reg_bar_read
*
*	Purpose:
*
*		Read a register from the given Base Address Register region.
*
*	Arguments:
*
*		device		The data used to access the device.
*
*		bar			The Base Address Register region to access.
*					A value of -1 refers to the PCI registers.
*
*		offset		The offset of the register to access.
*					Alligment must be legitimate.
*
*		size		The size of the register to access.
*					Permitted sizes are: 1, 2, 3 or 4 bytes.
*
*		value		The value is recorded here. Excess bits are zero.
*
*	Returned:
*
*		GSC_SUCCESS	All went well.
*		else		An appropriate error status.
*
*****************************************************************************/

static U32 _reg_bar_read(	gsc_device_t*	device,
							S8				bar,
							U16				offset,
							U8				size,
							U32*			value)
{
	int				i;
	gsc_drv_reg_t	reg;
	U32				status;

	if ((device) && (value))
	{
		OS_DEVICE_T_KEY_VERIFY(&device->device);
		reg.bar		= bar;
		reg.offset	= offset;
		reg.size	= size;
		reg.value	= 0xDEADBEEF;
		reg.mask	= 0;
		reg.status	= GSC_FAILED;
		i			= ioctl(OS_DEVICE_HANDLE(&device->device),
							GSC_IOCTL_REG_READ,
							&reg);
		value[0]	= reg.value;

		if (i)
			status	= os_errno_2_status(errno);
		else
			status	= reg.status;
	
		OS_DEVICE_T_KEY_VERIFY(&device->device);
	}
	else
	{
		status	= GSC_NULL_PARAM;
	}

	return(status);
}



/*****************************************************************************
*
*	Function:	_reg_bar_write
*
*	Purpose:
*
*		Write a value to a register in the given Base Address Register region.
*
*	Arguments:
*
*		device		The data used to access the device.
*
*		bar			The Base Address Register region to access.
*
*		offset		The offset of the register to access.
*					Alligment must be legitimate.
*
*		size		The size of the register to access.
*					Permitted sizes are: 1, 2, 3 or 4 bytes.
*
*		value		The value is recorded here. Excess bits are zero.
*
*	Returned:
*
*		GSC_SUCCESS	All went well.
*		else		An appropriate error status.
*
*****************************************************************************/

static U32 _reg_bar_write(	gsc_device_t*	device,
							S8				bar,
							U16				offset,
							U8				size,
							U32				value)
{
	int				i;
	gsc_drv_reg_t	reg;
	U32				status;

	if (device)
	{
		OS_DEVICE_T_KEY_VERIFY(&device->device);
		reg.bar		= bar;
		reg.offset	= offset;
		reg.size	= size;
		reg.value	= value;
		reg.mask	= 0;
		reg.status	= GSC_FAILED;
		i			= ioctl(OS_DEVICE_HANDLE(&device->device),
							GSC_IOCTL_REG_WRITE,
							&reg);

		if (i)
			status	= os_errno_2_status(errno);
		else
			status	= reg.status;

		OS_DEVICE_T_KEY_VERIFY(&device->device);
	}
	else
	{
		status	= GSC_NULL_PARAM;
	}

	return(status);
}



/*****************************************************************************
*
*	Function:	_dma_info_2_dma_open
*
*	Purpose:
*
*		Translate data from the source to the destination structure.
*
*	Arguments:
*
*		dest	The destination structure.
*
*		src		The source structure.
*
*		block	Is this for a block transfer?
*
*	Returned:
*
*		None.
*
*****************************************************************************/

#if DEV_USES_DMA

static void _dma_info_2_dma_open(
	gsc_drv_dma_t*			dest,
	const gsc_dma_info_t*	src,
	int						block)
{
	__u32	flags	= 0;

	switch (src->priority)
	{
		default:
		case GSC_DMA_PRI_SHARE:	flags	|= GSC_DMA_OPEN_PRI_SHARE;
								break;

		case GSC_DMA_PRI_CH_0:	flags	|= GSC_DMA_OPEN_PRI_CH_0;
								break;

		case GSC_DMA_PRI_CH_1:	flags	|= GSC_DMA_OPEN_PRI_CH_1;
								break;
	}

	if (src->bus_width == 32)
		flags	|= GSC_DMA_OPEN_SIZE_32_BITS;
	else if (src->bus_width == 16)
		flags	|= GSC_DMA_OPEN_SIZE_16_BITS;
	else
		flags	|= GSC_DMA_OPEN_SIZE_8_BITS;

	if (block)
		flags	|= GSC_DMA_OPEN_MEM_BLOCK;
	else
		flags	|= GSC_DMA_OPEN_MEM_SGL;

	if (src->demand_mode)
		flags	|= GSC_DMA_OPEN_XFER_DEMAND;
	else
		flags	|= GSC_DMA_OPEN_XFER_FORCE;

	if (src->fifo_mode)
		flags	|= GSC_DMA_OPEN_LADRS_FIXED;
	else
		flags	|= GSC_DMA_OPEN_LADRS_INC;

	if (src->bterm_input)
		flags	|= GSC_DMA_OPEN_BTERM_INPUT;

	if (src->dma_eot_pin)
		flags	|= GSC_DMA_OPEN_EOT_PIN;

	if (src->local_bursting)
		flags	|= GSC_DMA_OPEN_LOCAL_BURST;

	if (src->ready_input)
		flags	|= GSC_DMA_OPEN_READY_INPUT;

	if (src->stop_xfer_mode)
		flags	|= GSC_DMA_OPEN_STOP_XFER_MODE;

	if (src->wr_inval_mode)
		flags	|= GSC_DMA_OPEN_WR_INVAL_MODE;

	dest->u.open.flags	= flags;

	if (src->wait_states > 15)
		dest->u.open.wait_states	= 15;
	else
		dest->u.open.wait_states	= (unsigned int) src->wait_states;
}

#elif ! defined(DEV_USES_DMA)
	#error DEV_USES_DMA undefined. Must #include dev_drv.h.
#endif



/*****************************************************************************
*
*	Function:	_dma_info_2_dma_xfer
*
*	Purpose:
*
*		Translate data from the source to the destination structure.
*
*	Arguments:
*
*		dest	The destination structure.
*
*		src		The source structure.
*
*	Returned:
*
*		None.
*
*****************************************************************************/

#if DEV_USES_DMA

static void _dma_info_2_dma_xfer(gsc_drv_dma_t* dest, const gsc_dma_xfer_t* src)
{
	// src->done_irq	A done IRQ is always generated!

	if (src->local_to_pci)
		dest->u.xfer.flags	= GSC_DMA_XFER_DIR_TO_HOST;
	else
		dest->u.xfer.flags	= GSC_DMA_XFER_DIR_TO_DEVICE;

	dest->u.xfer.bytes		= src->bytes;
	dest->u.xfer.local_adrs	= src->local_adrs;

	if (src->phys_adrs)
	{
		dest->u.xfer.phys_adrs	= src->phys_adrs;
		dest->u.xfer.user_adrs	= 0;
	}
	else
	{
		dest->u.xfer.phys_adrs	= 0;
		dest->u.xfer.user_adrs	= src->user_adrs;
	}
}

#elif ! defined(DEV_USES_DMA)
	#error DEV_USES_DMA undefined. Must #include dev_drv.h.
#endif



/*****************************************************************************
*
*	Function:	gsc_drv_bar_map
*
*	Purpose:
*
*		Map a memory mapped Base Address Register region to user space.
*
*	Arguments:
*
*		device		The data used to access the device.
*
*		bar			The Base Address Register to access.
*
*		pva			Return the user space address here.
*
*	Returned:
*
*		GSC_SUCCESS	All went well.
*		else		An appropriate error status.
*
*****************************************************************************/

U32 gsc_drv_bar_map(gsc_device_t* device, S8 bar, void *pva)
{
	gsc_drv_bar_t	data;
	int				i;
	U32				status;
	void**			vpp	= (void**) pva;

	for (;;)
	{
		if ((device == NULL) || (pva == NULL))
		{
			status	= GSC_NULL_PARAM;
			break;
		}

		// Retrieve the information about BARX.
		OS_DEVICE_T_KEY_VERIFY(&device->device);
		memset(&data, 0, sizeof(data));
		data.index	= bar;
		i			= ioctl(OS_DEVICE_HANDLE(&device->device),
							GSC_IOCTL_BAR_INFO_GET,
							&data);

		if (i)
		{
			status	= os_errno_2_status(errno);
			break;
		}

		if (data.status != GSC_SUCCESS)
		{
			status	= data.status;
			break;
		}

		// If it's size is zero, then it can't be mapped.

		if (data.mmap_size == 0)
		{
			status	= GSC_ACCESS_DENIED;
			break;
		}

		// If it is already mapped, then return failure.

		if (data.mmap)
		{
			status	= GSC_OBJECT_ALREADY_ALLOCATED;
			break;
		}

		// Attempt to map the region.
		vpp[0]	= mmap(	NULL,
						data.mmap_size,
						PROT_READ | PROT_WRITE,
						MAP_SHARED,
						OS_DEVICE_HANDLE(&device->device),
						data.mmap_offset);

		if (vpp[0] == MAP_FAILED)
		{
			status	= GSC_FAILED;
			vpp[0]	= NULL;
			break;
		}

		status	= GSC_SUCCESS;
		vpp[0]	= (void*) ((unsigned long) vpp[0] + data.mmap_inset);
		OS_DEVICE_T_KEY_VERIFY(&device->device);
		break;
	}

	return(status);
}



/*****************************************************************************
*
*	Function:	gsc_drv_bar_size_get
*
*	Purpose:
*
*		Return the size of the given BAR region.
*
*	Arguments:
*
*		device		The data used to access the device.
*
*		bar			The Base Address Register to access.
*
*		size		The size of the region is returned here.
*
*	Returned:
*
*		GSC_SUCCESS	All went well.
*		else		An appropriate error status.
*
*****************************************************************************/

U32 gsc_drv_bar_size_get(gsc_device_t* device, S8 bar, U32* size)
{
	gsc_drv_bar_t	data;
	int				i;
	U32				status;

	for (;;)
	{
		if ((device == NULL) || (size == NULL))
		{
			status	= GSC_NULL_PARAM;
			break;
		}

		// Retrieve the information about BARX.
		OS_DEVICE_T_KEY_VERIFY(&device->device);
		memset(&data, 0, sizeof(data));
		data.index	= bar;
		i			= ioctl(OS_DEVICE_HANDLE(&device->device),
							GSC_IOCTL_BAR_INFO_GET,
							&data);

		if (i)
		{
			status	= os_errno_2_status(errno);
		}
		else if (data.status != GSC_SUCCESS)
		{
			status	= data.status;
		}
		else
		{
			status	= GSC_SUCCESS;
			size[0]	= data.size;
		}

		OS_DEVICE_T_KEY_VERIFY(&device->device);
		break;
	}

	return(status);
}



/*****************************************************************************
*
*	Function:	gsc_drv_bar_unmap
*
*	Purpose:
*
*		Unmap a memory mapped Base Address Register region from user space.
*
*	Arguments:
*
*		device		The data used to access the device.
*
*		bar			The Base Address Register to access.
*
*		pva			The user spaces address to unmap.
*
*	Returned:
*
*		GSC_SUCCESS	All went well.
*		else		An appropriate error status.
*
*****************************************************************************/

U32 gsc_drv_bar_unmap(gsc_device_t* device, S8 bar, void* pva)
{
	gsc_drv_bar_t	data;
	int				i;
	U32				status;

	for (;;)
	{
		if ((device == NULL) || (pva == NULL))
		{
			status	= GSC_NULL_PARAM;
			break;
		}

		// Retrieve the information about BARX.
		OS_DEVICE_T_KEY_VERIFY(&device->device);
		memset(&data, 0, sizeof(data));
		data.index	= bar;
		i			= ioctl(OS_DEVICE_HANDLE(&device->device),
							GSC_IOCTL_BAR_INFO_GET,
							&data);

		if (i)
		{
			status	= os_errno_2_status(errno);
			break;
		}

		if (data.status != GSC_SUCCESS)
		{
			status	= data.status;
			break;
		}

		// If it isn't mapped, then there isn't anything to do.

		if (data.mmap == 0)
		{
			status	= GSC_SUCCESS;
			break;
		}

		// Attempt to unmap the region.
		pva	= (void*) ((unsigned long) pva - data.mmap_inset);
		i	= munmap((void*) pva, data.mmap_size);

		if (i)
			status	= GSC_FAILED;
		else
			status	= GSC_SUCCESS;

		OS_DEVICE_T_KEY_VERIFY(&device->device);
		break;
	}

	return(status);
}



/*****************************************************************************
*
*	Function:	gsc_drv_close
*
*	Purpose:
*
*		Close an open connectrion.
*
*	Arguments:
*
*		device		The data used to access the device.
*
*	Returned:
*
*		GSC_SUCCESS	All went well.
*		else		An appropriate error status.
*
*****************************************************************************/

U32 gsc_drv_close(gsc_device_t* device)
{
	U32	status;

	if (device)
	{
		os_device_close(&device->device);
		status	= GSC_SUCCESS;
	}
	else
	{
		status	= GSC_NULL_PARAM;
	}

	return(status);
}



/*****************************************************************************
*
*	Function:	gsc_drv_dma_abort
*
*	Purpose:
*
*		Abort an ongoing DMA transfer.
*
*	Arguments:
*
*		device		The data used to access the device.
*
*		index		The DMA channel to access.
*
*	Returned:
*
*		GSC_SUCCESS	All went well.
*		else		An appropriate error status.
*
*****************************************************************************/

#if DEV_USES_DMA

U32 gsc_drv_dma_abort(gsc_device_t* device, U8 index)
{
	gsc_drv_dma_t	dma;
	int				i;
	U32				status;

	if (device)
	{
		memset(&dma, 0, sizeof(dma));
		dma.index	= index;
		i			= ioctl(OS_DEVICE_HANDLE(&device->device),
							GSC_IOCTL_DMA_ABORT,
							&dma);

		if (i)
			status	= os_errno_2_status(errno);
		else
			status	= dma.status;
	}
	else
	{
		status	= GSC_NULL_PARAM;
	}

	return(status);
}

#elif ! defined(DEV_USES_DMA)
	#error DEV_USES_DMA undefined. Must #include dev_drv.h.
#endif



/*****************************************************************************
*
*	Function:	gsc_drv_dma_block_close
*
*	Purpose:
*
*		Close a block-mode DMA channel.
*
*	Arguments:
*
*		device		The data used to access the device.
*
*		index		The DMA channel to access.
*
*	Returned:
*
*		GSC_SUCCESS	All went well.
*		else		An appropriate error status.
*
*****************************************************************************/

#if DEV_USES_DMA

U32 gsc_drv_dma_block_close(gsc_device_t* device, U8 index)
{
	gsc_drv_dma_t	dma;
	int				i;
	U32				status;

	if (device)
	{
		memset(&dma, 0, sizeof(dma));
		dma.index	= index;
		i			= ioctl(OS_DEVICE_HANDLE(&device->device),
							GSC_IOCTL_DMA_CLOSE,
							&dma);

		if (i)
			status	= os_errno_2_status(errno);
		else
			status	= dma.status;
	}
	else
	{
		status	= GSC_NULL_PARAM;
	}

	return(status);
}

#elif ! defined(DEV_USES_DMA)
	#error DEV_USES_DMA undefined. Must #include dev_drv.h.
#endif



/*****************************************************************************
*
*	Function:	gsc_drv_dma_block_open
*
*	Purpose:
*
*		Open a block-mode DMA channel.
*
*	Arguments:
*
*		device		The data used to access the device.
*
*		index		The DMA channel to access.
*
*		info		The data to initialize the DMA channel with.
*
*	Returned:
*
*		GSC_SUCCESS	All went well.
*		else		An appropriate error status.
*
*****************************************************************************/

#if DEV_USES_DMA

U32 gsc_drv_dma_block_open(	gsc_device_t*	device,
							U8				index,
							gsc_dma_info_t*	info)
{
	gsc_drv_dma_t	dma;
	int				i;
	U32				status;

	if ((device) && (info))
	{
		memset(&dma, 0, sizeof(dma));
		_dma_info_2_dma_open(&dma, info, 1);
		dma.index	= index;
		i			= ioctl(OS_DEVICE_HANDLE(&device->device),
							GSC_IOCTL_DMA_OPEN,
							&dma);

		if (i)
			status	= os_errno_2_status(errno);
		else
			status	= dma.status;
	}
	else
	{
		status	= GSC_NULL_PARAM;
	}

	return(status);
}

#elif ! defined(DEV_USES_DMA)
	#error DEV_USES_DMA undefined. Must #include dev_drv.h.
#endif



/*****************************************************************************
*
*	Function:	gsc_drv_dma_block_xfer
*
*	Purpose:
*
*		Perform a block based DMA transfer.
*
*	Arguments:
*
*		device		The data used to access the device.
*
*		index		The index of the DMA channel to use.
*
*		xfer		The data for the transfer to perform.
*
*	Returned:
*
*		GSC_SUCCESS	All went well.
*		else		An appropriate error status.
*
*****************************************************************************/

#if DEV_USES_DMA

U32 gsc_drv_dma_block_xfer(	gsc_device_t*	device,
							U8				index,
							gsc_dma_xfer_t*	xfer)
{
	gsc_drv_dma_t	dma;
	int				i;
	U32				status;

	if ((device) && (xfer))
	{
		_dma_info_2_dma_xfer(&dma, xfer);
		dma.index	= index;
		i			= ioctl(OS_DEVICE_HANDLE(&device->device),
							GSC_IOCTL_DMA_XFER,
							&dma);

		if (i)
			status	= os_errno_2_status(errno);
		else
			status	= dma.status;
	}
	else
	{
		status	= GSC_NULL_PARAM;
	}

	return(status);
}

#elif ! defined(DEV_USES_DMA)
	#error DEV_USES_DMA undefined. Must #include dev_drv.h.
#endif



/*****************************************************************************
*
*	Function:	gsc_drv_dma_sgl_close
*
*	Purpose:
*
*		Close a Scatter Gather List mode DMA channel.
*
*	Arguments:
*
*		device		The data used to access the device.
*
*		index		The DMA channel to access.
*
*	Returned:
*
*		GSC_SUCCESS	All went well.
*		else		An appropriate error status.
*
*****************************************************************************/

#if DEV_USES_DMA

U32 gsc_drv_dma_sgl_close(gsc_device_t* device, U8 index)
{
	gsc_drv_dma_t	dma;
	int				i;
	U32				status;

	if (device)
	{
		memset(&dma, 0, sizeof(dma));
		dma.index	= index;
		i			= ioctl(OS_DEVICE_HANDLE(&device->device),
							GSC_IOCTL_DMA_CLOSE,
							&dma);

		if (i)
			status	= os_errno_2_status(errno);
		else
			status	= dma.status;
	}
	else
	{
		status	= GSC_NULL_PARAM;
	}

	return(status);
}

#elif ! defined(DEV_USES_DMA)
	#error DEV_USES_DMA undefined. Must #include dev_drv.h.
#endif



/*****************************************************************************
*
*	Function:	gsc_drv_dma_sgl_open
*
*	Purpose:
*
*		Open a Scatter Gather List mode DMA channel.
*
*	Arguments:
*
*		device		The data used to access the device.
*
*		index		The DMA channel to access.
*
*		info		The data to initialize the DMA channel with.
*
*	Returned:
*
*		GSC_SUCCESS	All went well.
*		else		An appropriate error status.
*
*****************************************************************************/

#if DEV_USES_DMA

U32 gsc_drv_dma_sgl_open(	gsc_device_t*	device,
							U8				index,
							gsc_dma_info_t*	info)
{
	gsc_drv_dma_t	dma;
	int				i;
	U32				status;

	if ((device) && (info))
	{
		memset(&dma, 0, sizeof(dma));
		_dma_info_2_dma_open(&dma, info, 0);
		dma.index	= index;
		i			= ioctl(OS_DEVICE_HANDLE(&device->device),
							GSC_IOCTL_DMA_OPEN,
							&dma);

		if (i)
			status	= os_errno_2_status(errno);
		else
			status	= dma.status;
	}
	else
	{
		status	= GSC_NULL_PARAM;
	}

	return(status);
}

#elif ! defined(DEV_USES_DMA)
	#error DEV_USES_DMA undefined. Must #include dev_drv.h.
#endif



/*****************************************************************************
*
*	Function:	gsc_drv_dma_sgl_xfer
*
*	Purpose:
*
*		Perform a Scatter Gather List based DMA transfer.
*
*	Arguments:
*
*		device		The data used to access the device.
*
*		index		The index of the DMA channel to use.
*
*		xfer		The data for the transfer to perform.
*
*	Returned:
*
*		GSC_SUCCESS	All went well.
*		else		An appropriate error status.
*
*****************************************************************************/

#if DEV_USES_DMA

U32 gsc_drv_dma_sgl_xfer(	gsc_device_t*	device,
							U8				index,
							gsc_dma_xfer_t*	xfer)
{
	gsc_drv_dma_t	dma;
	int				i;
	U32				status;

	if ((device) && (xfer))
	{
		_dma_info_2_dma_xfer(&dma, xfer);
		dma.index	= index;
		i			= ioctl(OS_DEVICE_HANDLE(&device->device),
							GSC_IOCTL_DMA_XFER,
							&dma);

		if (i)
			status	= os_errno_2_status(errno);
		else
			status	= dma.status;
	}
	else
	{
		status	= GSC_NULL_PARAM;
	}

	return(status);
}

#elif ! defined(DEV_USES_DMA)
	#error DEV_USES_DMA undefined. Must #include dev_drv.h.
#endif



/*****************************************************************************
*
*	Function:	gsc_drv_dma_status
*
*	Purpose:
*
*		Return the status of a DMA transfer.
*
*	Arguments:
*
*		device		The data used to access the device.
*
*		index		The index of the DMA channel to use.
*
*	Returned:
*
*		GSC_SUCCESS	All went well.
*		else		An appropriate error status.
*
*****************************************************************************/

#if DEV_USES_DMA

U32 gsc_drv_dma_status(gsc_device_t* device, U8 index)
{
	gsc_drv_dma_t	dma;
	int				i;
	U32				status;

	if (device)
	{
		memset(&dma, 0, sizeof(dma));
		dma.index	= index;
		i			= ioctl(OS_DEVICE_HANDLE(&device->device),
							GSC_IOCTL_DMA_STATUS,
							&dma);

		if (i)
			status	= os_errno_2_status(errno);
		else if (dma.status != GSC_SUCCESS)
			status	= dma.status;
		else if (dma.u.status.flags & GSC_DMA_STATUS_BUSY)
    		status	= GSC_DMA_IN_PROGRESS;
		else
			status	= GSC_DMA_DONE;
	}
	else
	{
		status	= GSC_NULL_PARAM;
	}

	return(status);
}

#elif ! defined(DEV_USES_DMA)
	#error DEV_USES_DMA undefined. Must #include dev_drv.h.
#endif



/*****************************************************************************
*
*	Function:	gsc_drv_init
*
*	Purpose:
*
*		Perform initilization that is device interface specific.
*
*	Arguments:
*
*		None.
*
*	Returned:
*
*		GSC_SUCCESS	All went well.
*		else		An appropriate error status.
*
*****************************************************************************/

U32 gsc_drv_init(void)
{
	os_device_t	dev;
	U32			status;

	if ((strlen(API_DEV_PRODUCT_NAME_UPPER) + 1) >= sizeof(dev.name))
		status	= GSC_INTERNAL_ERROR;
	else
		status	= GSC_SUCCESS;

	if (api_status == GSC_SUCCESS)
	{
		if ((sizeof(S8) != 1) ||
			(sizeof(U8) != 1) ||
			(sizeof(S16) != 2) ||
			(sizeof(U16) != 2) ||
			(sizeof(S32) != 4) ||
			(sizeof(U32) != 4))
		{
			api_status	= GSC_INIT_FAILURE;
		}
	}

	return(status);
}



/*****************************************************************************
*
*	Function:	gsc_drv_irq_attach
*
*	Purpose:
*
*		Register the set of interrupts for which we want notification.
*
*	Arguments:
*
*		device		The data used to access the device.
*
*		irq			The set of interrupts we're interrested in.
*
*		event		The event structure to use for notification.
*
*	Returned:
*
*		GSC_SUCCESS	All went well.
*		else		An appropriate error status.
*
*****************************************************************************/

#if DEV_USES_IRQ

U32 gsc_drv_irq_attach(	gsc_device_t*	device,
						gsc_irq_src_t*	irq,
						os_event_t*		event)
{
	gsc_drv_irq_t	dirq;
	int				i;
	U32				status;

	if ((device) && (irq) && (event))
	{
		// The event structure is handled within the master irq event thread.

		memset(&dirq, 0, sizeof(dirq));
		dirq.notify	|= irq->dma0  ? GSC_IRQ_DMA0  : 0;
		dirq.notify	|= irq->dma1  ? GSC_IRQ_DMA1  : 0;
		dirq.notify	|= irq->local ? GSC_IRQ_LOCAL : 0;
		i			= ioctl(OS_DEVICE_HANDLE(&device->device),
							GSC_IOCTL_IRQ_NOTIFY,
							&dirq);

		if (i)
			status	= os_errno_2_status(errno);
		else
			status	= dirq.status;
	}
	else
	{
		status	= GSC_NULL_PARAM;
	}

	return(status);
}

#elif ! defined(DEV_USES_IRQ)
	#error DEV_USES_IRQ undefined. Must #include dev_drv.h.
#endif



/*****************************************************************************
*
*	Function:	gsc_drv_irq_disable
*
*	Purpose:
*
*		Disable all device interrupts.
*
*	Arguments:
*
*		device		The data used to access the device.
*
*		irq			Record here which did occur.
*
*	Returned:
*
*		GSC_SUCCESS	All went well.
*		else		An appropriate error status.
*
*****************************************************************************/

#if DEV_USES_IRQ

U32 gsc_drv_irq_disable(gsc_device_t* device)
{
	int				i;
	gsc_drv_irq_t	irq;
	U32				status;

	if (device)
	{
		memset(&irq, 0, sizeof(irq));
		irq.enable	= GSC_IRQ_PCI
					| GSC_IRQ_DMA0
					| GSC_IRQ_DMA1
					| GSC_IRQ_LOCAL;
		i			= ioctl(OS_DEVICE_HANDLE(&device->device),
							GSC_IOCTL_IRQ_DISABLE,
							&irq);

		if (i)
			status	= os_errno_2_status(errno);
		else
			status	= irq.status;
	}
	else
	{
		status	= GSC_NULL_PARAM;
	}

	return(status);
}

#elif ! defined(DEV_USES_IRQ)
	#error DEV_USES_IRQ undefined. Must #include dev_drv.h.
#endif



/*****************************************************************************
*
*	Function:	gsc_drv_irq_enable
*
*	Purpose:
*
*		Enable all appropriate device interrupts.
*
*	Arguments:
*
*		device		The data used to access the device.
*
*		irq			Record here which did occur.
*
*	Returned:
*
*		GSC_SUCCESS	All went well.
*		else		An appropriate error status.
*
*****************************************************************************/

#if DEV_USES_IRQ

U32 gsc_drv_irq_enable(gsc_device_t* device)
{
	int				i;
	gsc_drv_irq_t	irq;
	U32				status;

	if (device)
	{
		memset(&irq, 0, sizeof(irq));
		irq.enable	= GSC_IRQ_PCI | GSC_IRQ_LOCAL;
		i			= ioctl(OS_DEVICE_HANDLE(&device->device),
							GSC_IOCTL_IRQ_ENABLE,
							&irq);

		if (i)
			status	= os_errno_2_status(errno);
		else
			status	= irq.status;
	}
	else
	{
		status	= GSC_NULL_PARAM;
	}

	return(status);
}

#elif ! defined(DEV_USES_IRQ)
	#error DEV_USES_IRQ undefined. Must #include dev_drv.h.
#endif



/*****************************************************************************
*
*	Function:	gsc_drv_irq_status
*
*	Purpose:
*
*		Tell the caller which of the specified interrupts has occurred.
*
*	Arguments:
*
*		device		The data used to access the device.
*
*		irq			Record here which did occur.
*
*	Returned:
*
*		GSC_SUCCESS	All went well.
*		else		An appropriate error status.
*
*****************************************************************************/

#if DEV_USES_IRQ

U32 gsc_drv_irq_status(gsc_device_t* device, gsc_irq_src_t* irq)
{
	gsc_drv_irq_t	dirq;
	int				i;
	U32				status;

	if ((device) && (irq))
	{
		memset(&dirq, 0, sizeof(dirq));
		i	= ioctl(OS_DEVICE_HANDLE(&device->device),
					GSC_IOCTL_IRQ_STATUS,
					&dirq);

		if (i)
		{
			status	= os_errno_2_status(errno);
		}
		else
		{
			status		= dirq.status;
			irq->dma0	= (dirq.triggered & GSC_IRQ_DMA0) ? 1 : 0;
			irq->dma1	= (dirq.triggered & GSC_IRQ_DMA1) ? 1 : 0;
			irq->local	= (dirq.triggered & GSC_IRQ_LOCAL) ? 1 : 0;
		}
	}
	else
	{
		status	= GSC_NULL_PARAM;
	}

	return(status);
}

#elif ! defined(DEV_USES_IRQ)
	#error DEV_USES_IRQ undefined. Must #include dev_drv.h.
#endif



/*****************************************************************************
*
*	Function:	gsc_drv_irq_master_event_create
*
*	Purpose:
*
*		Create the master event handle for the main event thread.
*
*	Arguments:
*
*		irq			The main interrupt notification structure.
*
*		device		The data used to access the device.
*
*	Returned:
*
*		GSC_SUCCESS	All went well.
*		else		An appropriate error status.
*
*****************************************************************************/

#if DEV_USES_IRQ

U32 gsc_drv_irq_master_event_create(gsc_irq_t* irq, gsc_device_t* device)
{
	U32	status;

	if ((irq == NULL) || (device == NULL))
	{
		status	= GSC_NULL_PARAM;
	}
	else if (OS_DEVICE_HANDLE_IS_VALID(&device->device) == 0)
	{
		status	= GSC_NULL_PARAM;
	}
	else
	{
		device->device.master.error	= 0;
		device->device.master.quit	= 0;
		device->device.master.event	= &irq->pci->event;
		status	= os_thread_create(	&device->device.master.thread,
									"Master IRQ Thread",
									_master_irq_thread,
									(void*) &device->device);
	}

	return(status);
}

#elif ! defined(DEV_USES_IRQ)
	#error DEV_USES_IRQ undefined. Must #include dev_drv.h.
#endif



/*****************************************************************************
*
*	Function:	gsc_drv_irq_master_event_destroy
*
*	Purpose:
*
*		Destroy the master event and all its resources.
*
*	Arguments:
*
*		irq			The main interrupt notification structure.
*
*	Returned:
*
*		None.
*
*****************************************************************************/

#if DEV_USES_IRQ

void gsc_drv_irq_master_event_destroy(gsc_irq_t* irq)
{
	os_device_t*		dev;
	os_event_node_t*	node;
	U32					status	= GSC_SUCCESS;
	os_event_node_t*	tail;

	if (irq)
		gsc_event_destroy(&irq->pci);

	if ((irq) &&
		(irq->device) &&
		(OS_DEVICE_HANDLE_IS_VALID(&irq->device->device)))
	{
		// Abort the master thread.
		dev					= &irq->device->device;
		dev->master.quit	= 1;
		_irq_wait_cancel(dev);
		os_thread_destroy(&dev->master.thread);

		// Resume all blocked threads, just in case any are here.
		status	= os_sem_lock(&dev->events.sem);

		if (status == GSC_SUCCESS)
		{
			// There should be ABSOLUTELY NO threads blocked here.
			node	= (os_event_node_t*) dev->events.head;
			tail	= (os_event_node_t*) &dev->events.tail;

			for (; node != tail; node = node->next)
				pthread_cond_broadcast(&node->cond);

			os_sem_unlock(&dev->events.sem);
		}
	}
}

#elif ! defined(DEV_USES_IRQ)
	#error DEV_USES_IRQ undefined. Must #include dev_drv.h.
#endif



/*****************************************************************************
*
*	Function:	gsc_drv_mem_alloc
*
*	Purpose:
*
*		Allocate a region of physically contiguous memory usable for DMA. The
*		memory granted may be smaller than we requested.
*
*	Arguments:
*
*		device		The data used to access the device.
*
*		size		The size of the memory region.
*
*		phy_adrs	The physical address of the memory region.
*
*	Returned:
*
*		GSC_SUCCESS	All went well.
*		else		An appropriate error status.
*
*****************************************************************************/

#if DEV_USES_MEM

U32 gsc_drv_mem_alloc(gsc_device_t* device, U32* size, unsigned long* phy_adrs)
{
	int				i;
	gsc_drv_mem_t	mem;
	U32				status;

	if ((device) && (size) && (phy_adrs))
	{
		OS_DEVICE_T_KEY_VERIFY(&device->device);
		memset(&mem, 0, sizeof(mem));
		mem.size	= size[0];
		i			= ioctl(OS_DEVICE_HANDLE(&device->device),
							GSC_IOCTL_MEM_ALLOC,
							&mem);
		size[0]		= mem.size;
		phy_adrs[0]	= mem.dma_address;

		if (i)
			status	= os_errno_2_status(errno);
		else
			status	= mem.status;

		OS_DEVICE_T_KEY_VERIFY(&device->device);
	}
	else
	{
		status	= GSC_NULL_PARAM;
	}

	return(status);
}

#elif ! defined(DEV_USES_MEM)
	#error DEV_USES_MEM undefined. Must #include dev_drv.h.
#endif



/*****************************************************************************
*
*	Function:	gsc_drv_mem_free
*
*	Purpose:
*
*		Free the given memory region.
*
*	Arguments:
*
*		device		The data used to access the device.
*
*		size		The size of the memory region.
*
*		phy_adrs	The physical address of the memory region.
*
*	Returned:
*
*		GSC_SUCCESS	All went well.
*		else		An appropriate error status.
*
*****************************************************************************/

#if DEV_USES_MEM

U32 gsc_drv_mem_free(gsc_device_t* device, U32 size, U32 phy_adrs)
{
	int				i;
	int				j;
	gsc_drv_mem_t	mem;
	U32				status	= GSC_INVALID_DATA;

	if (device)
	{
		for (i = 0; i < GSC_MEM_ALLOC_MAX; i++)
		{
			memset(&mem, 0, sizeof(mem));
			mem.index	= i;
			j			= ioctl(OS_DEVICE_HANDLE(&device->device),
								GSC_IOCTL_MEM_INFO,
								&mem);

			if (j)
			{
				status	= os_errno_2_status(errno);
				break;
			}

			if ((size == mem.size) && (phy_adrs == mem.dma_address))
			{
				j			= ioctl(OS_DEVICE_HANDLE(&device->device),
									GSC_IOCTL_MEM_FREE,
									&mem);

				if (j)
					status	= os_errno_2_status(errno);
				else
					status	= mem.status;

				break;
			}
		}
	}
	else
	{
		status	= GSC_NULL_PARAM;
	}

	return(status);
}

#elif ! defined(DEV_USES_MEM)
	#error DEV_USES_MEM undefined. Must #include dev_drv.h.
#endif



/*****************************************************************************
*
*	Function:	gsc_drv_mem_map
*
*	Purpose:
*
*		Map a region of driver memory to user space.
*
*	Arguments:
*
*		device		The data used to access the device.
*
*		size		The size of the memory region.
*
*		phy_adrs	The physical address of the memory region.
*
*		usr_adrs	The user space address is recorded here.
*
*	Returned:
*
*		GSC_SUCCESS	All went well.
*		else		An appropriate error status.
*
*****************************************************************************/

#if DEV_USES_MEM

U32 gsc_drv_mem_map(gsc_device_t* device, U32 size, U32 phy_adrs, unsigned long* usr_adrs)
{
	int				i;
	int				j;
	gsc_drv_mem_t	mem;
	U32				status	= GSC_INVALID_DATA;
	void*			vp;

	if ((device) && (usr_adrs))
	{
		for (i = 0; i < GSC_MEM_ALLOC_MAX; i++)
		{
			memset(&mem, 0, sizeof(mem));
			mem.index	= i;
			j			= ioctl(OS_DEVICE_HANDLE(&device->device),
								GSC_IOCTL_MEM_INFO,
								&mem);

			if (j)
			{
				status	= os_errno_2_status(errno);
				break;
			}

			if ((size == mem.size) && (phy_adrs == mem.dma_address))
			{
				vp	= mmap(	0,
							size,
							PROT_READ | PROT_WRITE,
							MAP_SHARED,
							OS_DEVICE_HANDLE(&device->device),
							mem.mmap_offset);

				if (vp == MAP_FAILED)
				{
					status		= GSC_FAILED;
					usr_adrs[0]	= 0;
				}
				else
				{
					status		= GSC_SUCCESS;
					usr_adrs[0]	= (unsigned long) vp;
				}

				break;
			}
		}
	}
	else
	{
		status	= GSC_NULL_PARAM;
	}

	return(status);
}

#elif ! defined(DEV_USES_MEM)
	#error DEV_USES_MEM undefined. Must #include dev_drv.h.
#endif



/*****************************************************************************
*
*	Function:	gsc_drv_mem_unmap
*
*	Purpose:
*
*		Unmap the given memory region from user space.
*
*	Arguments:
*
*		device		The data used to access the device.
*
*		size		The size of the memory region.
*
*		phy_adrs	The physical address of the memory region.
*
*		usr_adrs	The user space address of the memory region.
*
*	Returned:
*
*		GSC_SUCCESS	All went well.
*		else		An appropriate error status.
*
*****************************************************************************/

#if DEV_USES_MEM

U32 gsc_drv_mem_unmap(gsc_device_t* device, U32 size, U32 phy_adrs, unsigned long usr_adrs)
{
	int	i;
	U32	status;

	if ((device) && (size) && (phy_adrs) && (usr_adrs))
	{
		i	= munmap((void*) usr_adrs, size);

		if (i)
			status	= os_errno_2_status(errno);
		else
			status	= GSC_SUCCESS;
	}
	else
	{
		status	= GSC_NULL_PARAM;
	}

	return(status);
}

#elif ! defined(DEV_USES_MEM)
	#error DEV_USES_MEM undefined. Must #include dev_drv.h.
#endif



/*****************************************************************************
*
*	Function:	gsc_drv_open
*
*	Purpose:
*
*		Open a channel to the specified board.
*
*	Arguments:
*
*		device		The data used to access the device.
*
*		index		The absolute board index to access.
*
*		name_upper	The upper case name of the device to use.
*
*	Returned:
*
*		GSC_SUCCESS	All went well.
*		else		An appropriate error status.
*
*****************************************************************************/

U32 gsc_drv_open(gsc_device_t* device, U8 index, const char* name_upper)
{
	U32	status;

	if ((device) && (name_upper))
		status	= os_device_open(index, name_upper, &device->device);
	else
		status	= GSC_NULL_PARAM;

	return(status);
}



/*****************************************************************************
*
*	Function:	gsc_drv_reg_io_mod
*
*	Purpose:
*
*		Perform a read-modify-write on an I/O mapped register.
*
*	Arguments:
*
*		device		The data used to access the device.
*
*		bar			The PCI BAR region where the register resides.
*
*		offset		The offset of the register to access.
*					Alligment must be legitimate.
*
*		size		The size of the register to access.
*					Permitted sizes are: 1, 2 or 4 bytes.
*
*		value		The value to apply. Excess bits are ignored.
*
*		mask		The set of bits to modity. Excess bits are ignored.
*
*	Returned:
*
*		GSC_SUCCESS	All went well.
*		else		An appropriate error status.
*
*****************************************************************************/

U32 gsc_drv_reg_io_mod(	gsc_device_t*	device,
						S8				bar,
						U16				offset,
						U8				size,
						U32				value,
						U32				mask)
{
	U32	status;

	status	= _reg_bar_mod(device, bar, offset, size, value, mask);
	return(status);
}



/*****************************************************************************
*
*	Function:	gsc_drv_reg_io_read
*
*	Purpose:
*
*		Read an I/O mapped register.
*
*	Arguments:
*
*		device		The data used to access the device.
*
*		bar			The PCI BAR region where the register resides.
*
*		offset		The offset of the register to access.
*					Alligment must be legitimate.
*
*		size		The size of the register to access.
*					Permitted sizes are: 1, 2 or 4 bytes.
*
*		value		The value is recorded here. Excess bits are zero.
*
*	Returned:
*
*		GSC_SUCCESS	All went well.
*		else		An appropriate error status.
*
*****************************************************************************/

U32 gsc_drv_reg_io_read(	gsc_device_t*	device,
							S8				bar,
							U16				offset,
							U8				size,
							U32*			value)
{
	U32	status;

	status	= _reg_bar_read(device, bar, offset, size, value);
	return(status);
}



/*****************************************************************************
*
*	Function:	gsc_drv_reg_io_write
*
*	Purpose:
*
*		Write to an I/O mapped register.
*
*	Arguments:
*
*		device		The data used to access the device.
*
*		bar			The PCI BAR region where the register resides.
*
*		offset		The offset of the register to access.
*					Alligment must be legitimate.
*
*		size		The size of the register to access.
*					Permitted sizes are: 1, 2 or 4 bytes.
*
*		value		The value to write. Excess bits are ignored.
*
*	Returned:
*
*		GSC_SUCCESS	All went well.
*		else		An appropriate error status.
*
*****************************************************************************/

U32 gsc_drv_reg_io_write(	gsc_device_t*	device,
							S8				bar,
							U16				offset,
							U8				size,
							U32				value)
{
	U32	status;

	status	= _reg_bar_write(device, bar, offset, size, value);
	return(status);
}



/*****************************************************************************
*
*	Function:	gsc_drv_reg_mem_mod
*
*	Purpose:
*
*		Perform a read-modify-write on a memory mapped register.
*
*	Arguments:
*
*		device		The data used to access the device.
*
*		bar			The PCI BAR region where the register resides.
*
*		offset		The offset of the register to access.
*					Alligment must be legitimate.
*
*		size		The size of the register to access.
*					Permitted sizes are: 1, 2 or 4 bytes.
*
*		value		The value to apply. Excess bits are ignored.
*
*		mask		The set of bits to modity. Excess bits are ignored.
*
*	Returned:
*
*		GSC_SUCCESS	All went well.
*		else		An appropriate error status.
*
*****************************************************************************/

U32 gsc_drv_reg_mem_mod(gsc_device_t*	device,
						S8				bar,
						U16				offset,
						U8				size,
						U32				value,
						U32				mask)
{
	U32	status;

	status	= _reg_bar_mod(device, bar, offset, size, value, mask);
	return(status);
}



/*****************************************************************************
*
*	Function:	gsc_drv_reg_mem_read
*
*	Purpose:
*
*		Read a memory mapped register.
*
*	Arguments:
*
*		device		The data used to access the device.
*
*		bar			The PCI BAR region where the register resides.
*
*		offset		The offset of the register to access.
*					Alligment must be legitimate.
*
*		size		The size of the register to access.
*					Permitted sizes are: 1, 2 or 4 bytes.
*
*		value		The value is recorded here. Excess bits are zero.
*
*	Returned:
*
*		GSC_SUCCESS	All went well.
*		else		An appropriate error status.
*
*****************************************************************************/

U32 gsc_drv_reg_mem_read(	gsc_device_t*	device,
							S8				bar,
							U16				offset,
							U8				size,
							U32*			value)
{
	U32	status;

	status	= _reg_bar_read(device, bar, offset, size, value);
	return(status);
}



/*****************************************************************************
*
*	Function:	gsc_drv_reg_mem_write
*
*	Purpose:
*
*		Write to a memory mapped register.
*
*	Arguments:
*
*		device		The data used to access the device.
*
*		bar			The PCI BAR region where the register resides.
*
*		offset		The offset of the register to access.
*					Alligment must be legitimate.
*
*		size		The size of the register to access.
*					Permitted sizes are: 1, 2 or 4 bytes.
*
*		value		The value to write. Excess bits are ignored.
*
*	Returned:
*
*		GSC_SUCCESS	All went well.
*		else		An appropriate error status.
*
*****************************************************************************/

U32 gsc_drv_reg_mem_write(	gsc_device_t*	device,
							S8				bar,
							U16				offset,
							U8				size,
							U32				value)
{
	U32	status;

	status	= _reg_bar_write(device, bar, offset, size, value);
	return(status);
}



/*****************************************************************************
*
*	Function:	gsc_drv_reg_pci_read
*
*	Purpose:
*
*		Read a PCI register.
*
*	Arguments:
*
*		device		The data used to access the device.
*
*		offset		The offset of the register to access.
*					Alligment must be legitimate.
*
*		size		The size of the register to access.
*					Permitted sizes are: 1, 2, 3 or 4 bytes.
*
*		value		The value is recorded here. Excess bits are zero.
*
*	Returned:
*
*		GSC_SUCCESS	All went well.
*		else		An appropriate error status.
*
*****************************************************************************/

U32 gsc_drv_reg_pci_read(	gsc_device_t*	device,
							U16				offset,
							U8				size,
							U32*			value)
{
	U32	status;

	status	= _reg_bar_read(device, -1, offset, size, value);
	return(status);
}



/*****************************************************************************
*
*	Function:	gsc_drv_version_get
*
*	Purpose:
*
*		Retrieve the API version number and product name from the driver.
*
*	Arguments:
*
*		device		The data used to access the device.
*
*		ver_api		The version number is put here.
*
*		name		The product name goes here.
*
*		size		The maximum size of the version string.
*
*	Returned:
*
*		GSC_SUCCESS	All went well.
*		else		An appropriate error status.
*
*****************************************************************************/

U32 gsc_drv_version_get(gsc_device_t*	device,
						U8*				ver_api,
						char*			name,
						U8				size)
{
	int					i;
	U32					status;
	gsc_drv_version_t	ver;

	for (;;)	// A convenience loop.
	{
		if ((device == NULL) || (ver_api == NULL) || (name == NULL))
		{
			status	= GSC_NULL_PARAM;
			break;
		}

		if (size <= 1)
		{
			status	= GSC_INVALID_SIZE;
			break;
		}

		status	= GSC_SUCCESS;
		OS_DEVICE_T_KEY_VERIFY(&device->device);
		i			= ioctl(OS_DEVICE_HANDLE(&device->device),
							GSC_IOCTL_VERSION_GET,
							&ver);
		ver_api[0]	= ver.api;
		strncpy(name, (char*) ver.version, size);
		name[size - 1]	= 0;

		if (i)
			status	= os_errno_2_status(errno);
		else
			status	= ver.status;

		OS_DEVICE_T_KEY_VERIFY(&device->device);
		break;
	}

	return(status);
}



/*****************************************************************************
*
*	Function:	gsc_drv_version_verify
*
*	Purpose:
*
*		Verify that driver's API version number is supported by the library.
*
*	Arguments:
*
*		device		The data used to access the device.
*
*	Returned:
*
*		GSC_SUCCESS	All went well.
*		else		An appropriate error status.
*
*****************************************************************************/

U32 gsc_drv_version_verify(gsc_device_t* device)
{
	U8		api;
	char	text[64];
	U32		status;

	if (device)
		status	= gsc_drv_version_get(device, &api, text, sizeof(text));
	else
		status	= GSC_NULL_PARAM;

	if ((status == GSC_SUCCESS) &&
		(api != GSC_DRV_INTERFACE_VERSION))
	{
		status	= GSC_INVALID_VERSION_DRIVER;
	}

	return(status);
}


/*****************************************************************************
*
*	Function:	gsc_drv_version_verify_dev_open
*
*	Purpose:
*
*		Tell the call if an open device handle is required to verify the
*		driver version number.
*
*	Arguments:
*
*		open		Put 0 here if an open device handle is NOT required and
*					a non-zero value if an open device handle IS required.
*
*	Returned:
*
*		GSC_SUCCESS	All went well.
*		else		An appropriate error status.
*
*****************************************************************************/

U32 gsc_drv_version_verify_dev_open(int* open)
{
	U32	status;

	if (open)
	{
		// We have to open a device to access the driver version number.
		// We could get it from /proc/xxxx, but that is additional code
		// that isn't in the library.
		status	= (U32) GSC_SUCCESS;
		open[0]	= 1;
	}
	else
	{
		status	= (U32) GSC_NULL_PARAM;
	}

	return(status);
}


