/* API - Any Device - Any OS */
/* $Workfile: gsc_io.c $ */
/* $Revision: 1 $ */
/* $Date: 10/27/07 10:09a $ */
/* $Log: /NEW SOURCESAFE DATABASE/SDK/PLX/gsc/common/api/gsc_io.c $
 * 
 * 5     10/27/07 10:09a Dnewbold
 * Ported to Linux.
 * 
 * 4     4/02/07 4:50p Dnewbold
 * Ported to Linux.
 * 
 * 3     3/01/07 5:17p Dnewbold
 * Linted.
 * 
 * 2     5/04/06 3:39p Dnewbold
 * Removed key validation code and increased modularity.
 * 
 * 1     8/22/05 11:48a Dnewbold
*/

#include <string.h>

#include "gsc_io.h"

#include "dev_reg.h"

#include "gsc_os_time.h"



/*****************************************************************************
*
*	Function:	_io_begin_status
*
*	Purpose:
*
*		Initialize event status appropriate for the start of an I/O transfer.
*
*	Arguments:
*
*		io			The I/O structure in use.
*
*		function	The I/O transfer function to call.
*
*		buffer		The data transfer buffer.
*
*		bytes		The number of data bytes to transfer.
*
*	Returned:
*
*		GSC_SUCCESS	All went well.
*		else		A status reflecting the error encountered.
*
*****************************************************************************/

static U32 _io_begin_status(gsc_io_t*	io,
							U32			(*function)(dev_data_t*	dev,
													void*		buffer,
													U32			bytes,
													U32*		count),
							void*		buffer,
							U32			bytes)
{
	U32	io_stat;
	U32	stat;
	U32	status	= (U32) GSC_SUCCESS;
	U32	tx;

	tx			= io->is_tx ? GSC_IO_STATUS_TX : 0;
	io->aborted	= 0;
	io_stat		= tx
				| GSC_IO_STATUS_ACTIVE
				| (GSC_IO_STATUS_COUNT_MASK & bytes);

	if (io->overlap.enable)
	{
		status	= gsc_event_config(
					io->overlap.transfer,
					function,				// id
					(void*) GSC_NO_CHANGE,	// function
					io,						// arg1: gsc_io_t*
					(UL) buffer,			// arg2: buffer
					io_stat,				// arg3: I/O status
					NULL);
	}

	stat	= gsc_event_config(
				io->user,
				(void*) GSC_NO_CHANGE,	// id: index
				(void*) GSC_NO_CHANGE,	// function
				(void*) GSC_NO_CHANGE,	// arg1: device handle
				io_stat,				// arg2: I/O status
				GSC_NO_CHANGE,			// arg3: user argument
				NULL);
	status	= (status == (U32) GSC_SUCCESS) ? stat : status;
	return(status);
}



/*****************************************************************************
*
*	Function:	_io_end_status
*
*	Purpose:
*
*		Update the I/O status according to the data given.
*
*	Arguments:
*
*		io			The I/O data structure in use.
*
*		overlap		Was this an overlapped I/O request?
*
*		count		The count of bytes actually transferred.
*
*		status		The status from the process so far.
*
*	Returned:
*
*		GSC_SUCCESS	All went well.
*		else		A status reflecting the error encountered.
*
*****************************************************************************/

static U32 _io_end_status(gsc_io_t* io, U32 overlap, U32 count, U32 status)
{
	U32	io_stat;
	U32	trans		= (U32) GSC_SUCCESS;
	U32	tx;
	U32	user;

	tx			= io->is_tx ? GSC_IO_STATUS_TX : 0;
	io->abort	= 0;

	if (status == (U32) GSC_SUCCESS)
	{
		// The transfer completed successfully.
		io_stat	= tx
				| GSC_IO_STATUS_SUCCESS
				| (GSC_IO_STATUS_COUNT_MASK & count);
	}
	else if (status == (U32) GSC_WAIT_TIMEOUT)
	{
		// The transfer timed out.
		io_stat	= tx
				| GSC_IO_STATUS_TIMEOUT
				| (GSC_IO_STATUS_COUNT_MASK & count);
	}
	else if (status == (U32) GSC_ABORTED)
	{
		// The transfer was abort by the application.
		io_stat	= tx
				| GSC_IO_STATUS_ABORTED
				| (GSC_IO_STATUS_COUNT_MASK & count);
	}
	else
	{
		// The transfer encountered an error.
		io_stat	= tx
				| GSC_IO_STATUS_ERROR
				| (GSC_IO_STATUS_COUNT_MASK & count);
	}

	if (overlap)
	{
		trans	= gsc_event_config(	io->overlap.transfer,
									(void*) GSC_NO_CHANGE,	// id: index
									(void*) GSC_NO_CHANGE,	// function
									io,						// arg1: gsc_io_t*
									0,						// arg2: buffer
									io_stat,				// arg3: I/O status
									NULL);
	}

	user	= gsc_event_config(	io->user,
								(void*) GSC_NO_CHANGE,	// id: index
								(void*) GSC_NO_CHANGE,	// function
								(void*) GSC_NO_CHANGE,	// arg1: device handle
								io_stat,				// arg2: I/O status
								GSC_NO_CHANGE,			// arg3: user argument
								NULL);

	if (status == (U32) GSC_SUCCESS)
		status	= (trans == (U32) GSC_SUCCESS) ? user : trans;

	return(status);
}



/*****************************************************************************
*
*	Function:	_overlap_io_service
*
*	Purpose:
*
*		This is the event callback routine that performs overlapped I/O.
*
*	Arguments:
*
*		arg1		This is the "gsc_io_t*" for the transfer to perform.
*
*		arg2		Ignored.
*
*		arg3		Ignored.
*
*	Returned:
*
*		None.
*
*****************************************************************************/

static void _overlap_io_service(void* arg1, UL arg2, UL arg3)
{
	void*		buffer;
	UL			bytes;
	UL			count	= 0;
	U32			(*function)(dev_data_t*, void*, UL, UL*);
	gsc_io_t*	io		= (gsc_io_t*) arg1;
	U32			lock;
	U32			stat;
	U32			status;
	gsc_event_t	tmp;

	/*
	*	If, for some reason, we don't get the lock then we must procede
	*	any way, since this is the only means of making the effort go to
	*	completion. So, we'll keep working and do the best we can.
	*/

	lock	= gsc_sem_lock(&io->sem);

	status	= gsc_event_config(	io->overlap.transfer,
								(void*) GSC_NO_CHANGE,	// id: index
								(void*) GSC_NO_CHANGE,	// function
								(void*) GSC_NO_CHANGE,	// arg1: gsc_io_t*
								GSC_NO_CHANGE,			// arg2: buffer
								GSC_NO_CHANGE,			// arg3: I/O status
								&tmp);
	status	= (status == (U32) GSC_SUCCESS) ? lock : status;

	if (status == (U32) GSC_SUCCESS)
	{
		// Request the transfer and wait for completion.
		function	= tmp.id;
		buffer		= (void*) tmp.arg2;
		bytes		= tmp.arg3 & GSC_IO_STATUS_COUNT_MASK;
		status		= (function)(io->dev, buffer, bytes, &count);
	}

	stat	= _io_end_status(io, 1, count, status);
	status	= (status == (U32) GSC_SUCCESS) ? stat : status;
	gsc_event_trigger(io->user);	// The I/O is done.

	if (lock == (U32) GSC_SUCCESS)
		gsc_sem_unlock(&io->sem);
}	/*lint !e715 */



/******************************************************************************
*
*	Function:	_io_setup
*
*	Purpose:
*
*		Perform some setup before commencing data transfer.
*
*	Arguments:
*
*		io			The GSC common I/O parameters for the transfer.
*
*	Returned:
*
*		GSC_SUCCESS	All went well.
*		else		A status reflecting the error encountered.
*
******************************************************************************/

static U32 _io_setup(gsc_io_t* io)
{
	U32							delta;
	U32							offset;
	const gsc_io_func_size_t*	set;
	U32							status	= (U32) GSC_SUCCESS;
	U32							type;

	if (io->buffer == NULL)
		status	= (U32) GSC_NULL_PARAM;

	delta				= io->data_lane / 8;
	offset				= GSC_REG_OFFSET(io->reg) + delta;
	type				= GSC_REG_TYPE(io->reg);
	io->loc_adrs_adj	= io->loc_adrs + delta;

	if (io->data_size == 32)
	{
		set				= &io->func->_32;
		io->xfer_tot	&= ~0x3UL;
		io->reg_adj		= GSC_REG_ENCODE(type, 4, offset);

		if (io->bar->ptr)
		{
			io->reg_ptr		= (void*) (((UL) io->bar->ptr) + offset);
			io->fn_transfer	= set->transfer_map;
		}
		else
		{
			io->fn_transfer	= set->transfer_reg;
		}
	}
	else if (io->data_size == 16)
	{
		set				= &io->func->_16;
		io->xfer_tot	&= ~0x1UL;
		io->reg_adj		= GSC_REG_ENCODE(type, 2, offset);

		if (io->bar->ptr)
		{
			io->reg_ptr		= (void*) (((UL) io->bar->ptr) + offset);
			io->fn_transfer	= set->transfer_map;
		}
		else
		{
			io->fn_transfer	= set->transfer_reg;
		}
	}
	else
	{
		set			= &io->func->_8;
		io->reg_adj	= GSC_REG_ENCODE(type, 1, offset);

		if (io->bar->ptr)
		{
			io->reg_ptr		= (void*) (((UL) io->bar->ptr) + offset);
			io->fn_transfer	= set->transfer_map;
		}
		else
		{
			io->fn_transfer	= set->transfer_reg;
		}
	}

	io->fn_auto_start	= set->auto_start;
	io->fn_available	= set->available;
	io->fn_done			= set->done;
	io->fn_setup		= set->setup;
	io->fn_wait			= set->wait;
	return(status);
}



/*****************************************************************************
*
*	Function:	gsc_io_abort
*
*	Purpose:
*
*		Abort an I/O operation.
*		A PIO request should abort within about .1 seconds, maximum.
*		A DMA request should abort within about 1.1 seconds, maximum.
*
*	Arguments:
*
*		io			The I/O structure to access.
*
*	Returned:
*
*		GSC_SUCCESS	All went well.
*		else		A status reflecting the error encountered.
*
*****************************************************************************/

U32 gsc_io_abort(gsc_io_t* io)
{
	int	i;
	U32	locked	= 0;
	UL	stat;
	U32	status;

	io->abort	= 1;	// Request an abort.

	/*
	*	It is possible that this abort is for an overlapped I/O where the
	*	overlapped I/O thread has not yet run. The below code is here so that
	*	the overlapped I/O thread has a chance to run and actually abort the
	*	I/O.
	*/

	for (i = 0;; i++)
	{
		if (i >= 11)	// Equates to 1.1 seconds (see other code).
			break;

		locked	= gsc_sem_lock(&io->sem);

		if (locked != (U32) GSC_SUCCESS)
			continue;

		status	= gsc_io_status(io, &stat);

		if (status != (U32) GSC_SUCCESS)
			break;

		if ((stat & GSC_IO_STATUS_MASK) == GSC_IO_STATUS_ACTIVE)
		{
			/*
			*	We can only get here when an overlapped I/O request has been
			*	activated but not yet run.
			*/
			gsc_sem_unlock(&io->sem);
			os_thread_wait(&io->overlap.transfer->thread, 100);
		}
		else
		{
			// The thread has been aborted.
			break;
		}
	}

	status	= gsc_io_status(io, &stat);

	if (status != (U32) GSC_SUCCESS)
	{
	}
	else if ((stat & GSC_IO_STATUS_MASK) == GSC_IO_STATUS_ACTIVE)
	{
		// The I/O was not aborted.
		status	= (U32) GSC_FAILED;
	}
	else if (io->abort)
	{
		// An I/O was not in progress. If it were this field
		// would have been cleared.
	}
	else
	{
		// The I/O was either aborted as requested, completed before the
		// abort request was seen, or it was ended due to an error or a
		// timeout.
	}

	io->abort	= 0;	// Discard abort request.

	if (locked == (U32) GSC_SUCCESS)
		gsc_sem_unlock(&io->sem);

	return(status);
}



/*****************************************************************************
*
*	Function:	gsc_io_callback_config
*
*	Purpose:
*
*		Configure the referenced I/O callback event.
*
*	Arguments:
*
*		io			The I/O structure to access.
*
*		id			A private event identifier.
*
*		callback	A call back function to call when the interrupt occurs.
*					Pass in NULL to cancal call back notification.
*
*		arg1		The first argument passed to the helper funtion.
*
*		arg2		The second argument passed to the helper funtion.
*
*		arg3		The third argument passed to the helper funtion.
*
*		dest		A copy of the source structure is put her so the caller
*					can safely look at the results of the call.
*
*	Returned:
*
*		GSC_SUCCESS	All went well.
*		else		A status reflecting the error encountered.
*
*****************************************************************************/

U32 gsc_io_callback_config(
	gsc_io_t*		io,
	void*			id,
	void			(*callback)(void* arg1, UL arg2, UL arg3),
	void*			arg1,
	UL				arg2,
	UL				arg3,
	gsc_event_t*	dest)
{
	U32	status;

	status	= gsc_event_config(	io->user,
								id,			// id: index
								callback,	// function
								arg1,		// arg1: device handle
								arg2,		// arg2: I/O status
								arg3,		// arg3: user argument
								dest);
	return(status);
}

								
								
/*****************************************************************************
*
*	Function:	gsc_io_start_transfer
*
*	Purpose:
*
*		Initiate the blocking vs overlapped I/O transfer operation according
*		to the configuration.
*
*	Arguments:
*
*		sem			Unlock this semaphore after locking that of the io
*					structure.
*
*		io			The I/O structure to utilize.
*
*		function	The I/O transfer function to call.
*
*		buffer		The data transfer buffer.
*
*		bytes		The number of data bytes to transfer.
*
*		count		The actual number of bytes transferred is put here.
*
*	Returned:
*
*		GSC_SUCCESS	All went well.
*		else		A status reflecting the error encountered.
*
*****************************************************************************/

U32 gsc_io_start_transfer(	gsc_sem_t*	sem,
							gsc_io_t*	io,
							U32			(*function)(dev_data_t*	dev,
													void*		buffer,
													U32			bytes,
													U32*		count),
							void*		buffer,
							U32			bytes,
							U32*		count)
{
	UL	stat	= 0;
	U32	status;

	status	= gsc_sem_lock(&io->sem);
	gsc_sem_unlock(sem);

	if (bytes > GSC_IO_STATUS_COUNT_MASK)
		bytes	= GSC_IO_STATUS_COUNT_MASK;

	if (status == (U32) GSC_SUCCESS)
		status	= gsc_io_status(io, &stat);

	if ((status == (U32) GSC_SUCCESS) &&
		((stat & GSC_IO_STATUS_MASK) == GSC_IO_STATUS_ACTIVE))
	{
		status	= (U32) GSC_ACCESS_DENIED;	// Only one Tx/Rx at a time.
	}

	if (status != (U32) GSC_SUCCESS)
	{
	}
	else if (io->overlap.enable == 0)
	{
		// Perform blocking I/O
		status	= _io_begin_status(io, function, buffer, bytes);

		if (status == (U32) GSC_SUCCESS)
			status	= (function)(io->dev, buffer, bytes, count);

		if (count)
			status	= _io_end_status(io, 0, count[0], status);
		else
			status	= _io_end_status(io, 0, 0, status);

		gsc_event_trigger(io->user);	// The I/O is done.
		gsc_sem_unlock(&io->sem);
	}
	else
	{
		// Perform overlapped I/O
		status	= _io_begin_status(io, function, buffer, bytes);

		if (status == (U32) GSC_SUCCESS)
		{
			if (count)
				count[0]	= 0;

			gsc_event_trigger(io->overlap.transfer);	// Start the transfer.
		}
		else
		{
			status	= _io_end_status(io, 1, 0, status);
			gsc_event_trigger(io->user);	// The I/O is done.
		}

		gsc_sem_unlock(&io->sem);
	}

	return(status);
}



/******************************************************************************
*
*	Function:	gsc_io_overlap_enable
*
*	Purpose:
*
*		Adjust and report the current Overlap Enable state.
*
*	Arguments:
*
*		overlap		The structure to initialize.
*
*	Returned:
*
*		GSC_SUCCESS	All went well.
*		else		A status reflecting the error encountered.
*
******************************************************************************/

U32 gsc_io_overlap_enable(gsc_io_overlap_t* overlap, UL set, UL* get)
{
	if (set != GSC_NO_CHANGE)
		overlap->enable	= set ? 1 : 0;

	if (get)
		get[0]	= overlap->enable;

	return((U32) GSC_SUCCESS);
}



/******************************************************************************
*
*	Function:	_io_overlap_create
*
*	Purpose:
*
*		Initialize the referenced I/O structure in preparation for use.
*
*	Arguments:
*
*		overlap	The structure to initialize.
*
*		name	A name to associate with the structure and its content.
*
*		device	The data used to access the device.
*
*		dev		The main device specific data structure.
*
*	Returned:
*
*		GSC_SUCCESS	All went well.
*		else		A status reflecting the error encountered.
*
******************************************************************************/

static U32 _io_overlap_create(	gsc_io_overlap_t*	overlap,
								const char*			name,
								gsc_device_t*		device,
								dev_data_t*			dev)
{
	char	buf[64];
	U32		stat;
	U32		status	= (U32) GSC_SUCCESS;

	memset(overlap, 0, sizeof(gsc_io_overlap_t));
	strcpy(buf, name);
	strcat(buf, ": OVERLAP");
	stat	= gsc_event_create(&overlap->transfer, buf, device);
	status	= (status == (U32) GSC_SUCCESS) ? stat : status;

	stat	= gsc_event_config(	overlap->transfer,
								0,			// Later: device I/O function
								_overlap_io_service,	// function
								dev,		// arg1: device handle
								0,			// arg2: buffer
								0,			// arg3: I/O status
								NULL);
	status	= (status == (U32) GSC_SUCCESS) ? stat : status;
	return(status);
}



/******************************************************************************
*
*	Function:	gsc_io_create
*
*	Purpose:
*
*		Initialize the referenced I/O structure in preparation for use.
*
*	Arguments:
*
*		io		The structure to initialize.
*
*		name	A name to associate with the structure and its content.
*
*		device	The data used to access the device.
*
*		dev		The main device specific data structure.
*
*		dio		The device specific I/O data structure.
*
*		bar		The Base Address Register structure applicable for I/O
*				to firmware register (direct and indirect).
*
*		dma		The device's DMA structure.
*
*		is_tx	Is this the transmitter side?
*
*	Returned:
*
*		GSC_SUCCESS	All went well.
*		else		A status reflecting the error encountered.
*
******************************************************************************/

U32 gsc_io_create(	gsc_io_t*		io,
					const char*		name,
					gsc_device_t*	device,
					dev_data_t*		dev,
					dev_io_t*		dio,
					gsc_bar_t*		bar,
					gsc_dma_t*		dma,
					U32				is_tx)
{
	char	buf[64];
	U32		stat;
	U32		status	= (U32) GSC_SUCCESS;

	memset(io, 0, sizeof(io[0]));
	io->device	= device;
	io->dev		= dev;
	io->dio		= dio;
	io->bar		= bar;
	io->dma		= dma;
	io->is_tx	= is_tx ? 1 : 0;

	stat	= gsc_sem_create(&io->sem, name);
	status	= (status == (U32) GSC_SUCCESS) ? stat : status;

	stat	= _io_overlap_create(&io->overlap, name, device, dev);
	status	= (status == (U32) GSC_SUCCESS) ? stat : status;

	strcpy(buf, name);
	strcat(buf, ": USER");
	stat	= gsc_event_create(&io->user, buf, device);
	status	= (status == (U32) GSC_SUCCESS) ? stat : status;

	stat	= gsc_event_config(	io->user,
								0,				// id: index
								NULL,			// function
								0,				// arg1: device handle
								0,				// arg2: I/O status
								0,				// arg3: user argument
								NULL);
	status	= (status == (U32) GSC_SUCCESS) ? stat : status;

	stat	= gsc_io_init(io);
	status	= (status == (U32) GSC_SUCCESS) ? stat : status;
	return(status);
}



/******************************************************************************
*
*	Function:	gsc_io_destroy
*
*	Purpose:
*
*		Cleanup the referenced I/O structure as the device is no longer in
*		use.
*
*	Arguments:
*
*		io		The structure to clean up.
*
*	Returned:
*
*		GSC_SUCCESS	All went well.
*		else		A status reflecting the error encountered.
*
******************************************************************************/

void gsc_io_destroy(gsc_io_t* io)
{
	gsc_sem_destroy(&io->sem);
	gsc_event_destroy(&io->overlap.transfer);
	gsc_event_destroy(&io->user);
	memset(io, 0, sizeof(io[0]));
}



/******************************************************************************
*
*	Function:	gsc_io_init
*
*	Purpose:
*
*		Initialize the referenced I/O structure in preparation for use.
*
*	Arguments:
*
*		io		The structure to initialize.
*
*	Returned:
*
*		GSC_SUCCESS	All went well.
*		else		A status reflecting the error encountered.
*
******************************************************************************/

U32 gsc_io_init(gsc_io_t* io)
{
	U32	io_stat	= io->is_tx ? GSC_IO_STATUS_TX : 0;
	U32	stat;
	U32	status	= (U32) GSC_SUCCESS;

	io->abort				= 0;	// Later: set at the time it is needed.
	io->aborted				= 0;	// Later: set at the time it is needed.
	io->auto_start			= 1;	// Later: set by device specific code.
	io->buffer				= NULL;	// Later: set at the time it is needed.
	io->data_lane			= 0;	// Later: set by device specific code.
	io->data_size			= 32;	// Later: set by device specific code.
	io->dma_channel			= NULL;	// Later: set at the time it is needed.
	io->dma_control_mode	= 0;	// Later: set by device specific code.
	io->dma_demand			= 1;	// Later: set by device specific code.
	io->dma_dynamic			= 0;	// Later: set by device specific code.
	io->dma_enable			= 1;	// Later: set by device specific code.
	io->dma_need			= 0;	// Later: set at the time it is needed.
	io->dma_priority		= 1;	// Later: set by device specific code.
	io->fifo_like			= 1;	// Later: set by device specific code.
	io->fn_auto_start		= NULL;	// Later: set at the time it is needed.
	io->fn_available		= NULL;	// Later: set at the time it is needed.
	io->fn_done				= NULL;	// Later: set at the time it is needed.
	io->fn_setup			= NULL;	// Later: set at the time it is needed.
	io->fn_transfer			= NULL;	// Later: set at the time it is needed.
	io->fn_wait				= NULL;	// Later: set at the time it is needed.
	io->func				= NULL;	// Later: set at the time it is needed.
	io->loc_adrs			= 0;	// Later: set by device specific code.
	io->loc_adrs_adj		= 0;	// Later: set at the time it is needed.
	io->pio_threshold		= 0;	// Later: set at the time it is needed.
	io->timeout_s			= 10;	// Later: set by device specific code.
	io->reg					= 0;	// Later: set by device specific code.
	io->reg_adj				= 0;	// Later: set at the time it is needed.
	io->reg_ptr				= 0;	// Later: set at the time it is needed.
	io->xfer				= 0;	// Later: set at the time it is needed.
	io->xfer_buf			= NULL;	// Later: set at the time it is needed.
	io->xfer_time_end		= 0;	// Later: set at the time it is needed.
	io->xfer_tot			= 0;	// Later: set at the time it is needed.
	io->xfered				= 0;	// Later: set at the time it is needed.
	io->xfered_tot			= 0;	// Later: set at the time it is needed.

	stat	= gsc_io_overlap_enable(&io->overlap, 0, NULL);
	status	= (status == (U32) GSC_SUCCESS) ? stat : status;

	stat	= gsc_event_config(	io->overlap.transfer,
								0,						// id: index
								(void*) GSC_NO_CHANGE,	// function
								io,						// arg1: gsc_io_t*
								0,						// arg2: buffer
								io_stat,				// arg3: I/O status
								NULL);
	status	= (status == (U32) GSC_SUCCESS) ? stat : status;

	stat	= gsc_event_config(	io->user,
								0,				// id; index
								NULL,			// function
								io->dev,		// arg1: device handle
								io_stat,		// arg2: I/O status
								0,				// arg3: user argument
								NULL);
	status	= (status == (U32) GSC_SUCCESS) ? stat : status;
	return(status);
}



/******************************************************************************
*
*	Function:	gsc_io_status
*
*	Purpose:
*
*		Return the present recorded I/O status.
*
*	Arguments:
*
*		io			The I/O to access.
*
*		status		The status is recorded here.
*
*	Returned:
*
*		GSC_SUCCESS	All went well.
*		else		A status reflecting the error encountered.
*
******************************************************************************/

U32 gsc_io_status(gsc_io_t* io, UL* status)
{
	U32			stat;
	gsc_event_t	tmp;

	stat	= gsc_event_config(	io->user,
								(void*) GSC_NO_CHANGE,	// id: index
								(void*) GSC_NO_CHANGE,	// function
								(void*) GSC_NO_CHANGE,	// arg1: device handle
								GSC_NO_CHANGE,			// arg2: I/O status
								GSC_NO_CHANGE,			// arg3: user argument
								&tmp);

	if (stat == (U32) GSC_SUCCESS)
		status[0]	= tmp.arg2;

	return(stat);
}



/******************************************************************************
*
*	Function:	gsc_io_transfer_work
*
*	Purpose:
*
*		Implement the common mode and direction independent working portion
*		of the read() and write() services. If a timeout is permitted and
*		called for, we block when it is needed.
*
*	Arguments:
*
*		io			The GSC common I/O parameters for the transfer.
*
*	Returned:
*
*		GSC_SUCCESS	All went well.
*		else		A status reflecting the error encountered.
*
******************************************************************************/

U32	gsc_io_transfer_work(gsc_io_t* io)
{
	U32 (*auto_start)(gsc_io_t* io);
	U32	end;
	U32	ms_left;
	U32	now;
	U32	remainder;
	U32	status;

	status	= _io_setup(io);

	if (io->auto_start)
		auto_start	= io->fn_auto_start;
	else
		auto_start	= NULL;

	if ((status == (U32) GSC_SUCCESS) && (io->fn_setup))
		status	= (io->fn_setup)(io);

	if (io->timeout_s)
	{
		now	= os_time_delta_ms();
		end	= now + io->timeout_s * 1000;
	}
	else
	{
		end	= 0;
	}

	io->xfer_time_end	= end;

	if ((status == (U32) GSC_SUCCESS) && (auto_start))
		status	= (auto_start)(io);

	if (status == (U32) GSC_SUCCESS)
		remainder	= io->xfer_tot;
	else
		remainder	= 0;

	for (; remainder;)
	{
		if (io->abort)
		{
			io->aborted	= 1;
			status		= (U32) GSC_ABORTED;
			break;
		}

		status	= (io->fn_available)(io);
		if (status != (U32) GSC_SUCCESS)	break;

		if (io->xfer)
		{
			// Data/space is available so do the transfer.

			if (io->xfer > remainder)
				io->xfer	= remainder;

			io->xfered	= 0;
			status		= (io->fn_transfer)(io);
			if (status != (U32) GSC_SUCCESS)	break;

			if (auto_start)
			{
				status	= (auto_start)(io);
				if (status != (U32) GSC_SUCCESS)	break;
			}

			if (io->xfered)
			{
				// The transfer has already been completed.
				// This is usually PIO based I/O.
			}
			else
			{
				// We have to wait for the transfer to complete.
				// This is usually DMA.
				now		= os_time_delta_ms();
				ms_left	= end - now;
				status	= (io->fn_wait)(io, ms_left);

				if (status == (U32) GSC_SUCCESS)
					;		// The transfer completed.
				else if (status == (U32) GSC_WAIT_TIMEOUT)
					break;	// We timeout out.
				else
					break;	// There was a problem.
			}

			remainder		-= io->xfered;
			io->xfered_tot	+= io->xfered;
			io->xfer_buf	= (void*) (io->xfered + (UL) io->xfer_buf);

			if (io->fifo_like == 0)
				io->loc_adrs_adj	+= io->xfered;

			if (remainder == 0)
			{
				/* The request has been fulfilled. */
				break;
			}

			now	= os_time_delta_ms();

			if ((end) && (now >= end))
			{
				/* We got some data then timeout out. */
				status	= (U32) GSC_WAIT_TIMEOUT;
				break;
			}
			else if (io->xfered)
			{
				// Try for more data.
				continue;
			}
			else if (end == 0)
			{
				/* We can't block and we got no data. */
				break;
			}
		}
		else
		{
			// No data/space is available so we either leave or wait.

			if (end == 0)
			{
				/* No data/space is available and we can't block. */
				break;
			}

			now	= os_time_delta_ms();

			if (now >= end)
			{
				/* No data/space is available and we can't wait for it. */
				status	= (U32) GSC_WAIT_TIMEOUT;
				break;
			}
		}

		// Wait.
		now		= os_time_delta_ms();
		ms_left	= end - now;
		status	= (io->fn_wait)(io, ms_left);

		if (status == (U32) GSC_SUCCESS)
		{
			/* More data/space may be available. */
			continue;
		}

		if (status == (U32) GSC_WAIT_TIMEOUT)
		{
			/*
			*	We timeout out, but more data may be available if the needed
			*	interrupt is not properly configured.
			*/

			continue;
		}

		/* There was a problem. */
		break;
	}

	if (io->fn_done == NULL)
		;
	else if (status == (U32) GSC_SUCCESS)
		status	= (io->fn_done)(io);
	else
		(io->fn_done)(io);	/*lint !e522 */

	return(status);
}



/*****************************************************************************
*
*	Function:	gsc_io_wait
*
*	Purpose:
*
*		Wait for any one of the referenced I/O transfers to complete. No
*		waiting or complaining is done if both pointers are done.
*
*	Arguments:
*
*		sem			Unlock this semaphore before blocking.
*
*		tx			The transmit side I/O of interest. If NULL, it is ignored.
*
*		rx			The receive side I/O of interest. If NULL, it is ignored.
*
*		timeout_ms	The number of milliseconds of timeout to wait before
*					aborting the wait.
*
*	Returned:
*
*		GSC_SUCCESS	All went well.
*		else		A status reflecting the error encountered.
*
*****************************************************************************/

U32 gsc_io_wait(gsc_sem_t* sem, gsc_io_t* tx, gsc_io_t* rx, U32 timeout_ms)
{
	U32	status;

	if ((tx) && (rx))
	{
		gsc_sem_unlock(sem);
		status	= gsc_event_wait_2(tx->user, rx->user, timeout_ms);
	}
	else if (tx)
	{
		gsc_sem_unlock(sem);
		status	= gsc_event_wait(tx->user, timeout_ms);
	}
	else if (rx)
	{
		gsc_sem_unlock(sem);
		status	= gsc_event_wait(rx->user, timeout_ms);
	}
	else
	{
		status	= (U32) GSC_SUCCESS;
	}

	return(status);
}


