// $URL: http://subversion:8080/svn/gsc/trunk/drivers/SDK/hpdi32/sbtest/io.c $
// $Rev: 1943 $
// $Date:$

#include <stdio.h>
#include <string.h>
#include <time.h>
#include <asm/types.h>

#include "hpdi32_api.h"
#include "main.h"
#include "util_format.h"
#include "util_id.h"
#include "util_label.h"
#include "util_sleep.h"



/* #defines	*****************************************************************/

#define	_1K					(1024L)
#define	_10K				(10L * _1K)
#define	_200K				(200L * _1K)
#define	_1G					(1024L * 1024L * 1024L)

#define	WRITE(h,x)				\
			(_buf_ptr == NULL)	\
			? (GSC_FAILED)		\
			: (hpdi32_write((h), _buf_ptr, _buf_size, &(x)))

#define	READ(h,x)				\
			(_buf_ptr == NULL)	\
			? (GSC_FAILED)		\
			: (hpdi32_read((h), _buf_ptr, _buf_size, &(x)))



/* typedefs	*****************************************************************/

typedef struct
{
	unsigned long	set;
	unsigned long	get;
} set_get_t;

typedef struct
{
	const char*			name;
	U32					parm;
	const set_get_t*	list;
	size_t				size;
} set_get_list_t;

typedef struct
{
	int		tx;				// Is this Tx or Rx?
	U32		fifo_size;		// Size of FIFO in samples.
	U32		buf[1024];		// Samples per transfer.
	U32*	xfer_buf;		// Pointer to current transfer buffer.
	int		pending;		// I/O call is oending.

	long	xfer_b_tot;		// Total bytes to transfer.
	long	xfer_b;			// Bytes to tansfer in this block.

	long	xfered_b_tot;	// Total bytes transferred so far.
	long	xfered_b;		// Total bytes transfered for this block.

} io_t;

typedef struct
{
	long	limit;			// Maximum samples that can be in the pipeline.
	long	current;		// Samples currently in pipeline.
} pipeline_t;



/* variables	*************************************************************/

static	U32				_buffer[150L * 1024];	// Bigger than largest Tx FIFO
static	void*			_buf_ptr				= NULL;
static	unsigned long	_buf_size				= 0;
static	U32				_io_callback_status;



//****************************************************************************
static int _api_buffer_alloc(void* handle)
{
	#define	_2M		(2L * 1024L * 1024L)

	unsigned long	buffer	= 0;
	int				errs	= 0;
	unsigned long	size	= 0;
	int				status;

	status	= HPDI32_IO_BUFFER_SIZE__TX_SET(handle, _2M, &size);

	if (status != GSC_SUCCESS)
	{
		errs	+= label_fail_func(1, "HPDI32_IO_BUFFER_SIZE__TX_SET", status);
	}
	else if (size < _2M)
	{
		errs++;
		printf("FAIL <---  (%d. 2MB buffer size request failed.)\n", __LINE__);
	}

	if (errs == 0)
	{
		status	= HPDI32_IO_BUFFER_POINTER__TX_GET(handle, &buffer);

		if (status != GSC_SUCCESS)
		{
			errs	+= label_fail_func(1, "HPDI32_IO_BUFFER_POINTER__TX_GET", status);
		}
		else if (buffer == 0)
		{
			errs++;
			printf("FAIL <---  (%d. Failed to acquire buffer pointer.)\n", __LINE__);
		}
		else
		{
			memset((void*) buffer, 0, size);
		}
	}

	_buf_ptr	= (void*) buffer;
	_buf_size	= size;
	return(errs);
}



//****************************************************************************
static int _api_buffer_free(void* handle)
{
	int	errs	= 0;
	U32	status;

	status	= HPDI32_IO_BUFFER_SIZE__TX_FREE(handle);

	if (status != GSC_SUCCESS)
		errs	+= label_fail_func(1, "HPDI32_IO_BUFFER_SIZE__TX_FREE", status);

	_buf_ptr	= (void*) NULL;
	_buf_size	= 0;
	return(errs);
}



/*****************************************************************************
*
*	Function:	_io_parm_list_test
*
*	Purpose:
*
*		Perform a series of tests per the given data and list.
*
*	Arguments:
*
*		handle	The handle used to access the device under test.
*
*		which	Which I/O size are these tests for?
*
*		set		The data set to act on.
*
*	Returned:
*
*		0		All went well.
*		> 0		The number of errors seen.
*
*****************************************************************************/

static int _io_parm_list_test(void* handle, U32 which, const set_get_list_t* set)
{
	int				errs	= 0;
	int				form;
	unsigned long	get;
	int				i;
	char			name[128];
	U32				status	= GSC_SUCCESS;

	sprintf(name, "%s %s", (which & HPDI32_WHICH_TX) ? "Tx" : "Rx", set->name);
	label_text(name);

	for (form = 0; form <= 2; form++)
	{
		printf(".");

		for (i = 0; i < (int) set->size; i++)
		{
			status	= hpdi32_config(handle,
									set->parm,
									which,
									set->list[i].set,
									&get);

			if ((status == GSC_SUCCESS) && (get != set->list[i].get))
				status	= GSC_FAILED;

			if (status != GSC_SUCCESS)
				break;
		}

		if (status != GSC_SUCCESS)
			break;
	}

	hpdi32_reset(handle);
	errs	= (status == GSC_SUCCESS) ? 0 : 1;

	if (errs)
		printf("FAIL <---\n");
	else
		printf("PASS\n");

	return(errs);
}



/*****************************************************************************
*
*	Function:	_io_abort_tests
*
*	Purpose:
*
*		Perform I/O Abort tests. This test doesn't actually verify that the
*		abort request actually works. This test simply verifies that the API
*		service operates properly when an I/O operation is not active.
*
*	Arguments:
*
*		handle	The handle used to access the device under test.
*
*	Returned:
*
*		0		All went well.
*		> 0		The number of errors seen.
*
*****************************************************************************/

static int _io_abort_tests(void* handle)
{
	static const set_get_t	list[]	=
	{
		/* set						get						*/
		{ HPDI32_IO_ABORT_NO,		HPDI32_IO_ABORT_NO		},
		{ GSC_NO_CHANGE,			HPDI32_IO_ABORT_NO		},
		{ GSC_DEFAULT,				HPDI32_IO_ABORT_DEFAULT	},
		{ HPDI32_IO_ABORT_YES,		HPDI32_IO_ABORT_NO		},	// It auto clears.
		{ GSC_NO_CHANGE,			HPDI32_IO_ABORT_NO		},	// It auto clears.
		{ GSC_DEFAULT,				HPDI32_IO_ABORT_DEFAULT	},
		{ HPDI32_IO_ABORT_DEFAULT,	HPDI32_IO_ABORT_DEFAULT	},
		{ GSC_NO_CHANGE,			HPDI32_IO_ABORT_DEFAULT	},
		{ GSC_DEFAULT,				HPDI32_IO_ABORT_DEFAULT	}
	};

	static const set_get_list_t	set	=
	{
		/* name	*/	"Abort",
		/* parm	*/	HPDI32_IO_ABORT,
		/* list	*/	list,
		/* size	*/	ARRAY_ELEMENTS(list)
	};

	int	errs	= 0;

	errs	+= _io_parm_list_test(handle, HPDI32_WHICH_TX, &set);
	errs	+= _io_parm_list_test(handle, HPDI32_WHICH_RX, &set);
	return(errs);
}



/*****************************************************************************
*
*	Function:	_io_buffer_pointer_test
*
*	Purpose:
*
*		Perform a I/O Buffer Pointer test.
*
*	Arguments:
*
*		handle	The handle used to access the device under test.
*
*		which	transmitter or receiver side?
*
*	Returned:
*
*		0		All went well.
*		> 0		The number of errors seen.
*
*****************************************************************************/

static int _io_buffer_pointer_test(void* handle, U32 which)
{
	int				errs	= 0;
	int				form;
	unsigned long	get;
	int				i;
	char			label[128];
	U8*				ptr;
	U32				status;

	sprintf(label,
			"%s Buffer pointer",
			(which & HPDI32_WHICH_TX) ? "Tx" : "Rx");
	label_text(label);

	for (form = 0; form <= 2; form++)
	{
		printf(".");
		status	= hpdi32_init(handle);

		if (status == GSC_SUCCESS)
		{
			status	= hpdi32_config(handle,
									HPDI32_IO_BUFFER_SIZE,
									which,
									0,
									&get);

			if ((status == GSC_SUCCESS) && (get != 0))
				status	= GSC_FAILED;	// The size should match.
		}

		if (status == GSC_SUCCESS)
		{
			status	= hpdi32_config(handle,
									HPDI32_IO_BUFFER_POINTER,
									which,
									GSC_NO_CHANGE,
									&get);

			if ((status == GSC_SUCCESS) && (get))
				status	= GSC_FAILED;	// The pointer should be NULL.
		}

		if (status == GSC_SUCCESS)
		{
			status	= hpdi32_config(handle,
									HPDI32_IO_BUFFER_SIZE,
									which,
									1024,
									&get);

			// The size may be larger than requested, depending on the OS.

			if ((status == GSC_SUCCESS) && (get < 1024))
				status	= GSC_FAILED;	// The size should match.
		}

		if (status == GSC_SUCCESS)
		{
			status	= hpdi32_config(handle,
									HPDI32_IO_BUFFER_POINTER,
									which,
									GSC_NO_CHANGE,
									&get);

			if ((status == GSC_SUCCESS) && (get == (unsigned long) NULL))
				status	= GSC_FAILED;	// The pointer should NOT be NULL.
		}

		if (status == GSC_SUCCESS)
		{
			ptr	= (U8*) get;

			for (i = 0; i < (int) 1024; i++)
			{
				ptr[0]	= (U8) i;

				if (ptr[0] != (U8) i)
				{
					status	= GSC_FAILED;
					break;
				}
			}
		}
	}


	hpdi32_init(handle);
	errs	= (status == GSC_SUCCESS) ? 0 : 1;

	if (errs)
		printf("FAIL <---\n");
	else
		printf("PASS\n");

	return(errs);
}



/*****************************************************************************
*
*	Function:	_io_buffer_pointer_tests
*
*	Purpose:
*
*		Perform I/O Buffer Pointer tests.
*
*	Arguments:
*
*		handle	The handle used to access the device under test.
*
*	Returned:
*
*		0		All went well.
*		> 0		The number of errors seen.
*
*****************************************************************************/

static int _io_buffer_pointer_tests(void* handle)
{
	int	errs	= 0;

	errs	+= _io_buffer_pointer_test(handle, HPDI32_WHICH_TX);
	errs	+= _io_buffer_pointer_test(handle, HPDI32_WHICH_RX);
	return(errs);
}



/*****************************************************************************
*
*	Function:	_io_data_size_tests
*
*	Purpose:
*
*		Perform I/O Data Size tests.
*
*	Arguments:
*
*		handle	The handle used to access the device under test.
*
*	Returned:
*
*		0		All went well.
*		> 0		The number of errors seen.
*
*****************************************************************************/

static int _io_data_size_tests(void* handle)
{
	static const set_get_t	list[]	=
	{
		/* set							get							*/
		{ HPDI32_IO_DATA_SIZE_8_BITS,	HPDI32_IO_DATA_SIZE_8_BITS	},
		{ GSC_NO_CHANGE,				HPDI32_IO_DATA_SIZE_8_BITS	},
		{ GSC_DEFAULT,					HPDI32_IO_DATA_SIZE_DEFAULT	},
		{ HPDI32_IO_DATA_SIZE_16_BITS,	HPDI32_IO_DATA_SIZE_16_BITS	},
		{ GSC_NO_CHANGE,				HPDI32_IO_DATA_SIZE_16_BITS	},
		{ GSC_DEFAULT,					HPDI32_IO_DATA_SIZE_DEFAULT	},
		{ HPDI32_IO_DATA_SIZE_32_BITS,	HPDI32_IO_DATA_SIZE_32_BITS	},
		{ GSC_NO_CHANGE,				HPDI32_IO_DATA_SIZE_32_BITS	},
		{ GSC_DEFAULT,					HPDI32_IO_DATA_SIZE_DEFAULT	},
		{ HPDI32_IO_DATA_SIZE_DEFAULT,	HPDI32_IO_DATA_SIZE_DEFAULT	},
		{ GSC_NO_CHANGE,				HPDI32_IO_DATA_SIZE_DEFAULT	},
		{ GSC_DEFAULT,					HPDI32_IO_DATA_SIZE_DEFAULT	}
	};

	static const set_get_list_t	set	=
	{
		/* name	*/	"Data Size",
		/* parm	*/	HPDI32_IO_DATA_SIZE,
		/* list	*/	list,
		/* size	*/	ARRAY_ELEMENTS(list)
	};

	int	errs	= 0;

	errs	+= _io_parm_list_test(handle, HPDI32_WHICH_TX, &set);
	errs	+= _io_parm_list_test(handle, HPDI32_WHICH_RX, &set);
	return(errs);
}



/*****************************************************************************
*
*	Function:	_io_dma_channel_select_tests
*
*	Purpose:
*
*		Perform I/O DMA Channel Select tests.
*
*	Arguments:
*
*		handle	The handle used to access the device under test.
*
*	Returned:
*
*		0		All went well.
*		> 0		The number of errors seen.
*
*****************************************************************************/

static int _io_dma_channel_select_tests(void* handle)
{
	static const set_get_t	tx_list[]	=
	{
		/* set									get										*/
		{ HPDI32_IO_DMA_CHANNEL_SEL_DYNAMIC,	HPDI32_IO_DMA_CHANNEL_SEL_DYNAMIC		},
		{ GSC_NO_CHANGE,						HPDI32_IO_DMA_CHANNEL_SEL_DYNAMIC		},
		{ GSC_DEFAULT,							HPDI32_IO_DMA_CHANNEL_SEL_TX_DEFAULT	},
		{ HPDI32_IO_DMA_CHANNEL_SEL_STATIC,		HPDI32_IO_DMA_CHANNEL_SEL_STATIC		},
		{ GSC_NO_CHANGE,						HPDI32_IO_DMA_CHANNEL_SEL_STATIC		},
		{ GSC_DEFAULT,							HPDI32_IO_DMA_CHANNEL_SEL_TX_DEFAULT	},
		{ HPDI32_IO_DMA_CHANNEL_SEL_RX_DEFAULT,	HPDI32_IO_DMA_CHANNEL_SEL_RX_DEFAULT	},
		{ GSC_NO_CHANGE,						HPDI32_IO_DMA_CHANNEL_SEL_RX_DEFAULT	},
		{ GSC_DEFAULT,							HPDI32_IO_DMA_CHANNEL_SEL_TX_DEFAULT	},
		{ HPDI32_IO_DMA_CHANNEL_SEL_TX_DEFAULT,	HPDI32_IO_DMA_CHANNEL_SEL_TX_DEFAULT	},
		{ GSC_NO_CHANGE,						HPDI32_IO_DMA_CHANNEL_SEL_TX_DEFAULT	},
		{ GSC_DEFAULT,							HPDI32_IO_DMA_CHANNEL_SEL_TX_DEFAULT	},
	};

	static const set_get_t	rx_list[]	=
	{
		/* set									get										*/
		{ HPDI32_IO_DMA_CHANNEL_SEL_DYNAMIC,	HPDI32_IO_DMA_CHANNEL_SEL_DYNAMIC		},
		{ GSC_NO_CHANGE,						HPDI32_IO_DMA_CHANNEL_SEL_DYNAMIC		},
		{ GSC_DEFAULT,							HPDI32_IO_DMA_CHANNEL_SEL_RX_DEFAULT	},
		{ HPDI32_IO_DMA_CHANNEL_SEL_STATIC,		HPDI32_IO_DMA_CHANNEL_SEL_STATIC		},
		{ GSC_NO_CHANGE,						HPDI32_IO_DMA_CHANNEL_SEL_STATIC		},
		{ GSC_DEFAULT,							HPDI32_IO_DMA_CHANNEL_SEL_RX_DEFAULT	},
		{ HPDI32_IO_DMA_CHANNEL_SEL_RX_DEFAULT,	HPDI32_IO_DMA_CHANNEL_SEL_RX_DEFAULT	},
		{ GSC_NO_CHANGE,						HPDI32_IO_DMA_CHANNEL_SEL_RX_DEFAULT	},
		{ GSC_DEFAULT,							HPDI32_IO_DMA_CHANNEL_SEL_RX_DEFAULT	},
		{ HPDI32_IO_DMA_CHANNEL_SEL_TX_DEFAULT,	HPDI32_IO_DMA_CHANNEL_SEL_TX_DEFAULT	},
		{ GSC_NO_CHANGE,						HPDI32_IO_DMA_CHANNEL_SEL_TX_DEFAULT	},
		{ GSC_DEFAULT,							HPDI32_IO_DMA_CHANNEL_SEL_RX_DEFAULT	},
	};

	static const set_get_list_t	tx_set	=
	{
		/* name	*/	"DMA Chennel Select",
		/* parm	*/	HPDI32_IO_DMA_CHANNEL_SEL,
		/* list	*/	tx_list,
		/* size	*/	ARRAY_ELEMENTS(tx_list)
	};

	static const set_get_list_t	rx_set	=
	{
		/* name	*/	"DMA Chennel Select",
		/* parm	*/	HPDI32_IO_DMA_CHANNEL_SEL,
		/* list	*/	rx_list,
		/* size	*/	ARRAY_ELEMENTS(rx_list)
	};

	int	errs	= 0;

	errs	+= _io_parm_list_test(handle, HPDI32_WHICH_TX, &tx_set);
	errs	+= _io_parm_list_test(handle, HPDI32_WHICH_RX, &rx_set);
	return(errs);
}



/*****************************************************************************
*
*	Function:	_io_dma_control_mode_tests
*
*	Purpose:
*
*		Perform I/O DMA Control Mode tests.
*
*	Arguments:
*
*		handle	The handle used to access the device under test.
*
*	Returned:
*
*		0		All went well.
*		> 0		The number of errors seen.
*
*****************************************************************************/

static int _io_dma_control_mode_tests(void* handle)
{
	static const set_get_t	list[]	=
	{
		/* set									get										*/
		{ HPDI32_IO_DMA_CONTROL_MODE_MANUAL,	HPDI32_IO_DMA_CONTROL_MODE_MANUAL		},
		{ GSC_NO_CHANGE,						HPDI32_IO_DMA_CONTROL_MODE_MANUAL		},
		{ GSC_DEFAULT,							HPDI32_IO_DMA_CONTROL_MODE_DEFAULT		},
		{ HPDI32_IO_DMA_CONTROL_MODE_AUTOMATIC,	HPDI32_IO_DMA_CONTROL_MODE_AUTOMATIC	},
		{ GSC_NO_CHANGE,						HPDI32_IO_DMA_CONTROL_MODE_AUTOMATIC	},
		{ GSC_DEFAULT,							HPDI32_IO_DMA_CONTROL_MODE_DEFAULT		},
		{ HPDI32_IO_DMA_CONTROL_MODE_DEFAULT,	HPDI32_IO_DMA_CONTROL_MODE_DEFAULT		},
		{ GSC_NO_CHANGE,						HPDI32_IO_DMA_CONTROL_MODE_DEFAULT		},
		{ GSC_DEFAULT,							HPDI32_IO_DMA_CONTROL_MODE_DEFAULT		}
	};

	static const set_get_list_t	set	=
	{
		/* name	*/	"DMA Control Mode",
		/* parm	*/	HPDI32_IO_DMA_CONTROL_MODE,
		/* list	*/	list,
		/* size	*/	ARRAY_ELEMENTS(list)
	};

	int	errs	= 0;

	errs	+= _io_parm_list_test(handle, HPDI32_WHICH_TX, &set);
	errs	+= _io_parm_list_test(handle, HPDI32_WHICH_RX, &set);
	return(errs);
}



/*****************************************************************************
*
*	Function:	_io_dma_priority_tests
*
*	Purpose:
*
*		Perform I/O DMA Priority tests.
*
*	Arguments:
*
*		handle	The handle used to access the device under test.
*
*	Returned:
*
*		0		All went well.
*		> 0		The number of errors seen.
*
*****************************************************************************/

static int _io_dma_priority_tests(void* handle)
{
	static const set_get_t	tx_list[]	=
	{
		/* set									get									*/
		{ HPDI32_IO_DMA_PRIORITY_DISABLE,		HPDI32_IO_DMA_PRIORITY_DISABLE		},
		{ GSC_NO_CHANGE,						HPDI32_IO_DMA_PRIORITY_DISABLE		},
		{ GSC_DEFAULT,							HPDI32_IO_DMA_PRIORITY_TX_DEFAULT	},
		{ HPDI32_IO_DMA_PRIORITY_ENABLE,		HPDI32_IO_DMA_PRIORITY_ENABLE		},
		{ GSC_NO_CHANGE,						HPDI32_IO_DMA_PRIORITY_ENABLE		},
		{ GSC_DEFAULT,							HPDI32_IO_DMA_PRIORITY_TX_DEFAULT	},
		{ HPDI32_IO_DMA_PRIORITY_RX_DEFAULT,	HPDI32_IO_DMA_PRIORITY_RX_DEFAULT	},
		{ GSC_NO_CHANGE,						HPDI32_IO_DMA_PRIORITY_RX_DEFAULT	},
		{ GSC_DEFAULT,							HPDI32_IO_DMA_PRIORITY_TX_DEFAULT	},
		{ HPDI32_IO_DMA_PRIORITY_TX_DEFAULT,	HPDI32_IO_DMA_PRIORITY_TX_DEFAULT	},
		{ GSC_NO_CHANGE,						HPDI32_IO_DMA_PRIORITY_TX_DEFAULT	},
		{ GSC_DEFAULT,							HPDI32_IO_DMA_PRIORITY_TX_DEFAULT	}
	};

	static const set_get_t	rx_list[]	=
	{
		/* set									get									*/
		{ HPDI32_IO_DMA_PRIORITY_DISABLE,		HPDI32_IO_DMA_PRIORITY_DISABLE		},
		{ GSC_NO_CHANGE,						HPDI32_IO_DMA_PRIORITY_DISABLE		},
		{ GSC_DEFAULT,							HPDI32_IO_DMA_PRIORITY_RX_DEFAULT	},
		{ HPDI32_IO_DMA_PRIORITY_ENABLE,		HPDI32_IO_DMA_PRIORITY_ENABLE		},
		{ GSC_NO_CHANGE,						HPDI32_IO_DMA_PRIORITY_ENABLE		},
		{ GSC_DEFAULT,							HPDI32_IO_DMA_PRIORITY_RX_DEFAULT	},
		{ HPDI32_IO_DMA_PRIORITY_RX_DEFAULT,	HPDI32_IO_DMA_PRIORITY_RX_DEFAULT	},
		{ GSC_NO_CHANGE,						HPDI32_IO_DMA_PRIORITY_RX_DEFAULT	},
		{ GSC_DEFAULT,							HPDI32_IO_DMA_PRIORITY_RX_DEFAULT	},
		{ HPDI32_IO_DMA_PRIORITY_TX_DEFAULT,	HPDI32_IO_DMA_PRIORITY_TX_DEFAULT	},
		{ GSC_NO_CHANGE,						HPDI32_IO_DMA_PRIORITY_TX_DEFAULT	},
		{ GSC_DEFAULT,							HPDI32_IO_DMA_PRIORITY_RX_DEFAULT	}
	};

	static const set_get_list_t	tx_set	=
	{
		/* name	*/	"DMA Priority",
		/* parm	*/	HPDI32_IO_DMA_PRIORITY,
		/* list	*/	tx_list,
		/* size	*/	ARRAY_ELEMENTS(tx_list)
	};

	static const set_get_list_t	rx_set	=
	{
		/* name	*/	"DMA Priority",
		/* parm	*/	HPDI32_IO_DMA_PRIORITY,
		/* list	*/	rx_list,
		/* size	*/	ARRAY_ELEMENTS(rx_list)
	};

	int	errs	= 0;

	errs	+= _io_parm_list_test(handle, HPDI32_WHICH_TX, &tx_set);
	errs	+= _io_parm_list_test(handle, HPDI32_WHICH_RX, &rx_set);
	return(errs);
}



/*****************************************************************************
*
*	Function:	_io_buffer_size_list_test
*
*	Purpose:
*
*		Perform a series of tests on the buffer size parameter per the given
*		data and list. While we can request one size, we may be given a size
*		that is different (larger) than we asked for, depending on the OS.
*		We'll take any other condition as a failure since so little is being
*		requested.
*
*	Arguments:
*
*		handle	The handle used to access the device under test.
*
*		which	Which I/O size are these tests for?
*
*		set		The data set to act on.
*
*	Returned:
*
*		0		All went well.
*		> 0		The number of errors seen.
*
*****************************************************************************/

static int _io_buffer_size_list_test(void* handle, U32 which, const set_get_list_t* set)
{
	int				errs	= 0;
	int				form;
	unsigned long	get;
	int				i;
	char			name[128];
	U32				status	= GSC_SUCCESS;

	sprintf(name, "%s %s", (which & HPDI32_WHICH_TX) ? "Tx" : "Rx", set->name);
	label_text(name);

	for (form = 0; form <= 2; form++)
	{
		printf(".");

		for (i = 0; i < (int) set->size; i++)
		{
			status	= hpdi32_config(handle,
									set->parm,
									which,
									set->list[i].set,
									&get);

			if (status == GSC_SUCCESS)
			{
				if (set->list[i].get == 0)
				{
					// The size we get should also be zero.

					if (get)
						status	= GSC_FAILED;
				}
				else if (get < set->list[i].get)
				{
					status	= GSC_FAILED;
				}
			}

			if (status != GSC_SUCCESS)
				break;
		}

		if (status != GSC_SUCCESS)
			break;
	}

	hpdi32_reset(handle);
	errs	= (status == GSC_SUCCESS) ? 0 : 1;

	if (errs)
		printf("FAIL <---\n");
	else
		printf("PASS\n");

	return(errs);
}



/*****************************************************************************
*
*	Function:	_io_buffer_size_tests
*
*	Purpose:
*
*		Perform I/O Buffer Size tests.
*
*	Arguments:
*
*		handle	The handle used to access the device under test.
*
*	Returned:
*
*		0		All went well.
*		> 0		The number of errors seen.
*
*****************************************************************************/

static int _io_buffer_size_tests(void* handle)
{
	static const set_get_t	list[]	=
	{
		/* set								get								*/
		{ HPDI32_IO_BUFFER_SIZE_DEFAULT,	HPDI32_IO_BUFFER_SIZE_DEFAULT	},
		{ GSC_NO_CHANGE,					HPDI32_IO_BUFFER_SIZE_DEFAULT	},
		{ GSC_DEFAULT,						HPDI32_IO_BUFFER_SIZE_DEFAULT	},
		{ 1024,								1024							},
		{ GSC_NO_CHANGE,					1024							},
		{ GSC_DEFAULT,						HPDI32_IO_BUFFER_SIZE_DEFAULT	},
		{ 2048,								2048							},
		{ GSC_NO_CHANGE,					2048							},
		{ GSC_DEFAULT,						HPDI32_IO_BUFFER_SIZE_DEFAULT	},
		{ 0,								0								},
		{ GSC_NO_CHANGE,					HPDI32_IO_BUFFER_SIZE_DEFAULT	}
	};

	static const set_get_list_t	set	=
	{
		/* name	*/	"Buffer Size",
		/* parm	*/	HPDI32_IO_BUFFER_SIZE,
		/* list	*/	list,
		/* size	*/	ARRAY_ELEMENTS(list)
	};

	int	errs	= 0;

	errs	+= _io_buffer_size_list_test(handle, HPDI32_WHICH_TX, &set);
	errs	+= _io_buffer_size_list_test(handle, HPDI32_WHICH_RX, &set);
	return(errs);
}



/*****************************************************************************
*
*	Function:	_io_callback_arg_tests
*
*	Purpose:
*
*		Perform I/O Callback Argument tests.
*
*	Arguments:
*
*		handle	The handle used to access the device under test.
*
*	Returned:
*
*		0		All went well.
*		> 0		The number of errors seen.
*
*****************************************************************************/

static int _io_callback_arg_tests(void* handle)
{
	static const set_get_t	list[]	=
	{
		/* set								get								*/
		{ 1024,								1024							},
		{ GSC_NO_CHANGE,					1024							},
		{ GSC_DEFAULT,						HPDI32_IO_CALLBACK_ARG_DEFAULT	},
		{ 0,								0								},
		{ GSC_NO_CHANGE,					0								},
		{ GSC_DEFAULT,						HPDI32_IO_CALLBACK_ARG_DEFAULT	},
		{ 2048,								2048							},
		{ GSC_NO_CHANGE,					2048							},
		{ GSC_DEFAULT,						HPDI32_IO_CALLBACK_ARG_DEFAULT	},
		{ HPDI32_IO_CALLBACK_ARG_DEFAULT,	HPDI32_IO_CALLBACK_ARG_DEFAULT	},
		{ GSC_NO_CHANGE,					HPDI32_IO_CALLBACK_ARG_DEFAULT	},
		{ GSC_DEFAULT,						HPDI32_IO_CALLBACK_ARG_DEFAULT	}
	};

	static const set_get_list_t	set	=
	{
		/* name	*/	"Callback Argument",
		/* parm	*/	HPDI32_IO_CALLBACK_ARG,
		/* list	*/	list,
		/* size	*/	ARRAY_ELEMENTS(list)
	};

	int	errs	= 0;

	errs	+= _io_parm_list_test(handle, HPDI32_WHICH_TX, &set);
	errs	+= _io_parm_list_test(handle, HPDI32_WHICH_RX, &set);
	return(errs);
}



/*****************************************************************************
*
*	Function:	_io_callback_func_tests
*
*	Purpose:
*
*		Perform I/O Callback Function tests.
*
*	Arguments:
*
*		handle	The handle used to access the device under test.
*
*	Returned:
*
*		0		All went well.
*		> 0		The number of errors seen.
*
*****************************************************************************/

static void _callback(U32 arg1, U32 arg2, U32 arg3) {}	/*lint !e715 */

static int _io_callback_func_tests(void* handle)
{
	static const set_get_t	list[]	=
	{
		/* set								get								*/
		{ (unsigned long) _callback,		(unsigned long) _callback		},
		{ GSC_NO_CHANGE,					(unsigned long) _callback		},
		{ GSC_DEFAULT,						HPDI32_IO_CALLBACK_FUNC_DEFAULT	},
		{ 0,								0								},
		{ GSC_NO_CHANGE,					0								},
		{ GSC_DEFAULT,						HPDI32_IO_CALLBACK_FUNC_DEFAULT	},
		{ HPDI32_IO_CALLBACK_FUNC_DEFAULT,	HPDI32_IO_CALLBACK_FUNC_DEFAULT	},
		{ GSC_NO_CHANGE,					HPDI32_IO_CALLBACK_FUNC_DEFAULT	},
		{ GSC_DEFAULT,						HPDI32_IO_CALLBACK_FUNC_DEFAULT	}
	};

	static const set_get_list_t	set	=
	{
		/* name	*/	"Callback Function",
		/* parm	*/	HPDI32_IO_CALLBACK_FUNC,
		/* list	*/	list,
		/* size	*/	ARRAY_ELEMENTS(list)
	};

	int	errs	= 0;

	errs	+= _io_parm_list_test(handle, HPDI32_WHICH_TX, &set);
	errs	+= _io_parm_list_test(handle, HPDI32_WHICH_RX, &set);
	return(errs);
}



/*****************************************************************************
*
*	Function:	_io_mode_tests
*
*	Purpose:
*
*		Perform I/O Mode tests.
*
*	Arguments:
*
*		handle	The handle used to access the device under test.
*
*	Returned:
*
*		0		All went well.
*		> 0		The number of errors seen.
*
*****************************************************************************/

static int _io_mode_tests(void* handle)
{
	static const set_get_t	list[]	=
	{
		/* set						get						*/
		{ HPDI32_IO_MODE_PIO,		HPDI32_IO_MODE_PIO		},
		{ GSC_NO_CHANGE,			HPDI32_IO_MODE_PIO		},
		{ GSC_DEFAULT,				HPDI32_IO_MODE_DEFAULT	},
		{ HPDI32_IO_MODE_DMA,		HPDI32_IO_MODE_DMA		},
		{ GSC_NO_CHANGE,			HPDI32_IO_MODE_DMA		},
		{ GSC_DEFAULT,				HPDI32_IO_MODE_DEFAULT	},
		{ HPDI32_IO_MODE_DMDMA,		HPDI32_IO_MODE_DMDMA	},
		{ GSC_NO_CHANGE,			HPDI32_IO_MODE_DMDMA	},
		{ GSC_DEFAULT,				HPDI32_IO_MODE_DEFAULT	},
		{ HPDI32_IO_MODE_DEFAULT,	HPDI32_IO_MODE_DEFAULT	},
		{ GSC_NO_CHANGE,			HPDI32_IO_MODE_DEFAULT	},
		{ GSC_DEFAULT,				HPDI32_IO_MODE_DEFAULT	}
	};

	static const set_get_list_t	set	=
	{
		/* name	*/	"Mode",
		/* parm	*/	HPDI32_IO_MODE,
		/* list	*/	list,
		/* size	*/	ARRAY_ELEMENTS(list)
	};

	int	errs	= 0;

	errs	+= _io_parm_list_test(handle, HPDI32_WHICH_TX, &set);
	errs	+= _io_parm_list_test(handle, HPDI32_WHICH_RX, &set);
	return(errs);
}



/*****************************************************************************
*
*	Function:	_io_overlap_enable_tests
*
*	Purpose:
*
*		Perform I/O Overlap Enable tests.
*
*	Arguments:
*
*		handle	The handle used to access the device under test.
*
*	Returned:
*
*		0		All went well.
*		> 0		The number of errors seen.
*
*****************************************************************************/

static int _io_overlap_enable_tests(void* handle)
{
	static const set_get_t	list[]	=
	{
		/* set								get									*/
		{ HPDI32_IO_OVERLAP_ENABLE_NO,		HPDI32_IO_OVERLAP_ENABLE_NO			},
		{ GSC_NO_CHANGE,					HPDI32_IO_OVERLAP_ENABLE_NO			},
		{ GSC_DEFAULT,						HPDI32_IO_OVERLAP_ENABLE_DEFAULT	},
		{ HPDI32_IO_OVERLAP_ENABLE_YES,		HPDI32_IO_OVERLAP_ENABLE_YES		},
		{ GSC_NO_CHANGE,					HPDI32_IO_OVERLAP_ENABLE_YES		},
		{ GSC_DEFAULT,						HPDI32_IO_OVERLAP_ENABLE_DEFAULT	},
		{ HPDI32_IO_OVERLAP_ENABLE_DEFAULT,	HPDI32_IO_OVERLAP_ENABLE_DEFAULT	},
		{ GSC_NO_CHANGE,					HPDI32_IO_OVERLAP_ENABLE_DEFAULT	},
		{ GSC_DEFAULT,						HPDI32_IO_OVERLAP_ENABLE_DEFAULT	}
	};

	static const set_get_list_t	set	=
	{
		/* name	*/	"Overlap Enable",
		/* parm	*/	HPDI32_IO_OVERLAP_ENABLE,
		/* list	*/	list,
		/* size	*/	ARRAY_ELEMENTS(list)
	};

	int	errs	= 0;

	errs	+= _io_parm_list_test(handle, HPDI32_WHICH_TX, &set);
	errs	+= _io_parm_list_test(handle, HPDI32_WHICH_RX, &set);
	return(errs);
}



/*****************************************************************************
*
*	Function:	_io_single_cycle_tests
*
*	Purpose:
*
*		Perform I/O Single Cycle tests.
*
*	Arguments:
*
*		handle	The handle used to access the device under test.
*
*	Returned:
*
*		0		All went well.
*		> 0		The number of errors seen.
*
*****************************************************************************/

static int _io_single_cycle_tests(void* handle)
{
	static const set_get_t	list[]	=
	{
		/* set								get								*/
		{ HPDI32_IO_SINGLE_CYCLE_ABSENT,	HPDI32_IO_SINGLE_CYCLE_ABSENT	},
		{ GSC_NO_CHANGE,					HPDI32_IO_SINGLE_CYCLE_ABSENT	},
		{ GSC_DEFAULT,						HPDI32_IO_SINGLE_CYCLE_DEFAULT	},
		{ HPDI32_IO_SINGLE_CYCLE_PRESENT,	HPDI32_IO_SINGLE_CYCLE_PRESENT	},
		{ GSC_NO_CHANGE,					HPDI32_IO_SINGLE_CYCLE_PRESENT	},
		{ GSC_DEFAULT,						HPDI32_IO_SINGLE_CYCLE_DEFAULT	},
		{ HPDI32_IO_SINGLE_CYCLE_DEFAULT,	HPDI32_IO_SINGLE_CYCLE_DEFAULT	},
		{ GSC_NO_CHANGE,					HPDI32_IO_SINGLE_CYCLE_DEFAULT	},
		{ GSC_DEFAULT,						HPDI32_IO_SINGLE_CYCLE_DEFAULT	}
	};

	static const set_get_list_t	set	=
	{
		/* name	*/	"Single Cycle",
		/* parm	*/	HPDI32_IO_SINGLE_CYCLE,
		/* list	*/	list,
		/* size	*/	ARRAY_ELEMENTS(list)
	};

	int	errs	= 0;

	errs	+= _io_parm_list_test(handle, HPDI32_WHICH_TX, &set);
	errs	+= _io_parm_list_test(handle, HPDI32_WHICH_RX, &set);
	return(errs);
}



/*****************************************************************************
*
*	Function:	_io_status_which_tests
*
*	Purpose:
*
*		Perform I/O Status tests.
*
*	Arguments:
*
*		handle	The handle used to access the device under test.
*
*	Returned:
*
*		0		All went well.
*		> 0		The number of errors seen.
*
*****************************************************************************/

static int _io_status_which_tests(void* handle, U32 which)
{
	int				errs	= 0;
	int				form;
	unsigned long	get;
	char			label[128];
	U32				status;
	U32				timeout_s	= 1;
	U32				xfer;

	sprintf(label, "%s I/O Status", (which & HPDI32_WHICH_TX) ? "Tx" : "Rx");
	label_text(label);

	for (form = 0; form <= 1; form++)	// Don't call hpdi32_io_config() with active I/O.
	{
		printf(".");
		status	= hpdi32_init(handle);

		if ((which & HPDI32_WHICH_TX) && (status == GSC_SUCCESS))
			status	= HPDI32_TX_CLOCK_DIVIDER__SET(handle, 1000);

		if (status == GSC_SUCCESS)
		{
			status	= HPDI32_IO_OVERLAP_ENABLE__SET(
								handle,
								which,
								HPDI32_IO_OVERLAP_ENABLE_YES);
		}

		if (status == GSC_SUCCESS)
			status	= HPDI32_IO_MODE__SET(handle, which, HPDI32_IO_MODE_PIO);

		if (status == GSC_SUCCESS)
			status	= HPDI32_IO_TIMEOUT__SET(handle, which, timeout_s);

		errs	+= _api_buffer_alloc(handle);

		if (status != GSC_SUCCESS)
			;
		else if (which & HPDI32_WHICH_TX)
			status	= WRITE(handle, xfer);
		else
			status	= READ(handle, xfer);

		if (status == GSC_SUCCESS)
		{
			status	= hpdi32_config(handle,
									HPDI32_IO_STATUS,
									which,
									GSC_NO_CHANGE,
									&get);
		}

		// The status should either be ACTIVE or TIMEOUT.

		if ((status == GSC_SUCCESS) &&
			((get & GSC_IO_STATUS_MASK) == GSC_IO_STATUS_ACTIVE))
		{
			// The I/O is active, which is OK. Lets wait for completion.
			// Wait for the timeout to expire.
			sleep_ms(timeout_s + 2000);
		}

		if (status == GSC_SUCCESS)
		{
			status	= hpdi32_config(handle,
									HPDI32_IO_STATUS,
									which,
									GSC_NO_CHANGE,
									&get);
		}

		if (status == GSC_SUCCESS)
		{
			if ((get & GSC_IO_STATUS_MASK) == GSC_IO_STATUS_ACTIVE)
			{
				printf("(%d. still active) ", __LINE__);
				status	= GSC_FAILED;
			}
			else if ((get & GSC_IO_STATUS_MASK) == GSC_IO_STATUS_SUCCESS)
			{
				printf("(%d. unexpected success) ", __LINE__);
				status	= GSC_FAILED;
			}
			else if ((get & GSC_IO_STATUS_MASK) == GSC_IO_STATUS_ERROR)
			{
				printf("(%d. unexpected error) ", __LINE__);
				status	= GSC_FAILED;
			}
			else if ((get & GSC_IO_STATUS_MASK) == GSC_IO_STATUS_ABORTED)
			{
				printf("(%d. unexpected abort) ", __LINE__);
				status	= GSC_FAILED;
			}
			else if ((get & GSC_IO_STATUS_MASK) == GSC_IO_STATUS_TIMEOUT)
			{
				// This is what it should be.
			}
			else
			{
				printf("(%d. didn't timeout) ", __LINE__);
				status	= GSC_FAILED;
			}
		}

		hpdi32_init(handle);
		errs	= (status == GSC_SUCCESS) ? 0 : 1;

		errs	+= _api_buffer_free(handle);

		if (errs)
			break;
	}

	if (errs)
		printf("FAIL <---\n");
	else
		printf("PASS\n");

	return(errs);
}



/*****************************************************************************
*
*	Function:	_io_status_tests
*
*	Purpose:
*
*		Perform I/O Status tests.
*
*	Arguments:
*
*		handle	The handle used to access the device under test.
*
*	Returned:
*
*		0		All went well.
*		> 0		The number of errors seen.
*
*****************************************************************************/

static int _io_status_tests(void* handle)
{
	int	errs	= 0;

	errs	+= _io_status_which_tests(handle, HPDI32_WHICH_TX);
	errs	+= _io_status_which_tests(handle, HPDI32_WHICH_RX);
	return(errs);
}



/*****************************************************************************
*
*	Function:	_io_pio_threshold_tests
*
*	Purpose:
*
*		Perform I/O PIO Threshold tests.
*
*	Arguments:
*
*		handle	The handle used to access the device under test.
*
*	Returned:
*
*		0		All went well.
*		> 0		The number of errors seen.
*
*****************************************************************************/

static int _io_pio_threshold_tests(void* handle)
{
	static const set_get_t	list[]	=
	{
		/* set								get								*/
		{ HPDI32_IO_PIO_THRESHOLD_NONE,		HPDI32_IO_PIO_THRESHOLD_NONE	},
		{ GSC_NO_CHANGE,					HPDI32_IO_PIO_THRESHOLD_NONE	},
		{ GSC_DEFAULT,						HPDI32_IO_PIO_THRESHOLD_DEFAULT	},
		{ 1024,								1024							},
		{ GSC_NO_CHANGE,					1024							},
		{ GSC_DEFAULT,						HPDI32_IO_PIO_THRESHOLD_DEFAULT	},
		{ HPDI32_IO_PIO_THRESHOLD_DEFAULT,	HPDI32_IO_PIO_THRESHOLD_DEFAULT	},
		{ GSC_NO_CHANGE,					HPDI32_IO_PIO_THRESHOLD_DEFAULT	},
		{ GSC_DEFAULT,						HPDI32_IO_PIO_THRESHOLD_DEFAULT	}
	};

	static const set_get_list_t	set	=
	{
		/* name	*/	"PIO Threshold",
		/* parm	*/	HPDI32_IO_PIO_THRESHOLD,
		/* list	*/	list,
		/* size	*/	ARRAY_ELEMENTS(list)
	};

	int	errs	= 0;

	errs	+= _io_parm_list_test(handle, HPDI32_WHICH_TX, &set);
	errs	+= _io_parm_list_test(handle, HPDI32_WHICH_RX, &set);
	return(errs);
}



/*****************************************************************************
*
*	Function:	_io_timeout_tests
*
*	Purpose:
*
*		Perform I/O Timeout tests.
*
*	Arguments:
*
*		handle	The handle used to access the device under test.
*
*	Returned:
*
*		0		All went well.
*		> 0		The number of errors seen.
*
*****************************************************************************/

static int _io_timeout_tests(void* handle)
{
	static const set_get_t	list[]	=
	{
		/* set							get							*/
		{ HPDI32_IO_TIMEOUT_NO_WAIT,	HPDI32_IO_TIMEOUT_NO_WAIT	},
		{ GSC_NO_CHANGE,				HPDI32_IO_TIMEOUT_NO_WAIT	},
		{ GSC_DEFAULT,					HPDI32_IO_TIMEOUT_DEFAULT	},
		{ HPDI32_IO_TIMEOUT_MAX,		HPDI32_IO_TIMEOUT_MAX		},
		{ GSC_NO_CHANGE,				HPDI32_IO_TIMEOUT_MAX		},
		{ GSC_DEFAULT,					HPDI32_IO_TIMEOUT_DEFAULT	},
		{ HPDI32_IO_TIMEOUT_DEFAULT,	HPDI32_IO_TIMEOUT_DEFAULT	},
		{ GSC_NO_CHANGE,				HPDI32_IO_TIMEOUT_DEFAULT	},
		{ GSC_DEFAULT,					HPDI32_IO_TIMEOUT_DEFAULT	}
	};

	static const set_get_list_t	set	=
	{
		/* name	*/	"Timeout",
		/* parm	*/	HPDI32_IO_TIMEOUT,
		/* list	*/	list,
		/* size	*/	ARRAY_ELEMENTS(list)
	};

	int	errs	= 0;

	errs	+= _io_parm_list_test(handle, HPDI32_WHICH_TX, &set);
	errs	+= _io_parm_list_test(handle, HPDI32_WHICH_RX, &set);
	return(errs);
}



/*****************************************************************************
*
*	Function:	io_config_tests
*
*	Purpose:
*
*		Perform I/O configuration tests.
*
*	Arguments:
*
*		handle	The handle used to access the device under test.
*
*	Returned:
*
*		0		All went well.
*		> 0		The number of errors seen.
*
*****************************************************************************/
extern long sub_test;

int io_config_tests(void* handle)
{
	int	errs	= 0;

	label_text("I/O Configuration");
	printf("\n");
	label_level_inc();

	errs	+= _io_abort_tests(handle);
	errs	+= _io_buffer_pointer_tests(handle);
	errs	+= _io_buffer_size_tests(handle);
	errs	+= _io_callback_arg_tests(handle);
	errs	+= _io_callback_func_tests(handle);
	errs	+= _io_data_size_tests(handle);
	errs	+= _io_dma_channel_select_tests(handle);
	errs	+= _io_dma_control_mode_tests(handle);
	errs	+= _io_dma_priority_tests(handle);
	errs	+= _io_mode_tests(handle);
	errs	+= _io_overlap_enable_tests(handle);
	errs	+= _io_pio_threshold_tests(handle);
	errs	+= _io_single_cycle_tests(handle);
	errs	+= _io_status_tests(handle);
	errs	+= _io_timeout_tests(handle);

	label_level_dec();
	return(errs);
}



/*****************************************************************************
*
*	Function:	_io_callback
*
*	Purpose:
*
*		Respond to an I/O completion callback.
*
*	Arguments:
*
*		arg1	This is a device handle.
*
*		arg2	The status of the transfer.
*
*		arg3	A user supplied argument. We ignore this.
*
*	Returned:
*
*		0		All went well.
*		> 0		The number of errors seen.
*
*****************************************************************************/

static void _io_callback(U32 arg1, U32 arg2, U32 arg3)
{
	_io_callback_status	= arg2;
}	/*lint !e715 */



/*****************************************************************************
*
*	Function:	_io_callback_test
*
*	Purpose:
*
*		Perform I/O Completion Notification tests using the Callback feature.
*
*	Arguments:
*
*		handle	The handle used to access the device under test.
*
*	Returned:
*
*		0		All went well.
*		> 0		The number of errors seen.
*
*****************************************************************************/

static int _io_callback_test(void* handle)
{
	int				errs	= 0;
	unsigned long	get		= 0;
	int				i;
	U32				status;
	U32				xfer;

	memset(_buffer, 0, sizeof(_buffer));
	_io_callback_status	= 0;
	label_text("Callback");
	status	= hpdi32_init(handle);

	errs	+= _api_buffer_alloc(handle);

	if (status == GSC_SUCCESS)
		status	= HPDI32_IO_MODE__TX_PIO(handle);

	if (status == GSC_SUCCESS)
		status	= HPDI32_IO_OVERLAP_ENABLE__TX_YES(handle);

	if (status == GSC_SUCCESS)
		status	= HPDI32_IO_CALLBACK_FUNC__TX_SET(handle, (unsigned long) _io_callback);

	if (status == GSC_SUCCESS)
		status	= HPDI32_TX_ENABLE__YES(handle);

	/*
	*	Things are configured such that ...
	*	1. The actual data transfer out the cable will take a long time.
	*	2. The write() request will timeout after one second.
	*	The result is that the wait request should complete successfully.
	*/

	if (status == GSC_SUCCESS)
		status	= HPDI32_IO_TIMEOUT__TX_SET(handle, 1);

	if (status == GSC_SUCCESS)
		status	= HPDI32_TX_CLOCK_DIVIDER__SET(handle, 0xFFFF);

	if (status == GSC_SUCCESS)
		status	= WRITE(handle, xfer);

	if (status == GSC_SUCCESS)
		sleep_ms(2000);				// Allow sufficient callback time.

	if (status == GSC_SUCCESS)
		status	= HPDI32_IO_STATUS__TX_GET(handle, &get);

	if ((status == GSC_SUCCESS) && (_io_callback_status == 0))
	{
		printf("(%d. no callback) ", __LINE__);
		status	= GSC_FAILED;
	}

	// 1. Test status from HPDI32_IO_STATUS__TX_GET().
	// 2. Test status passed to the callback.

	for (i = 1; (status == GSC_SUCCESS) && (i <= 2); i++)
	{
		if ((status == GSC_SUCCESS) &&
			((get & GSC_IO_STATUS_MASK) == GSC_IO_STATUS_ACTIVE))
		{
			printf("(%d. i %d, still active) ", __LINE__, i);
			status	= GSC_FAILED;
		}

		if ((status == GSC_SUCCESS) &&
			((get & GSC_IO_STATUS_MASK) == GSC_IO_STATUS_SUCCESS))
		{
			printf("(%d. i %d, unexpected success) ", __LINE__, i);
			status	= GSC_FAILED;
		}

		if ((status == GSC_SUCCESS) &&
			((get & GSC_IO_STATUS_MASK) != GSC_IO_STATUS_TIMEOUT))
		{
			printf("(%d. i %d, did not timeout) ", __LINE__, i);
			status	= GSC_FAILED;
		}

		if ((status == GSC_SUCCESS) &&
			((get & GSC_IO_STATUS_MASK) == GSC_IO_STATUS_ERROR))
		{
			printf("(%d. i %d, transfer error) ", __LINE__, i);
			status	= GSC_FAILED;
		}

		if ((status == GSC_SUCCESS) &&
			((get & GSC_IO_STATUS_MASK) == GSC_IO_STATUS_ABORTED))
		{
			printf("(%d. i %d, transfer aborted) ", __LINE__, i);
			status	= GSC_FAILED;
		}

		get	= _io_callback_status;
	}

	hpdi32_init(handle);
	errs	+= (status == GSC_SUCCESS) ? 0 : 1;

	errs	+= _api_buffer_free(handle);

	if (errs)
		printf("FAIL <---\n");
	else
		printf("PASS\n");

	return(errs);
}



/*****************************************************************************
*
*	Function:	_io_wait_test
*
*	Purpose:
*
*		Perform I/O Completion Notification tests using the Wait feature.
*
*	Arguments:
*
*		handle	The handle used to access the device under test.
*
*	Returned:
*
*		0		All went well.
*		> 0		The number of errors seen.
*
*****************************************************************************/

static int _io_wait_test(void* handle)
{
	int	errs	= 0;
	U32	status;
	U32	xfer;

	memset(_buffer, 0, sizeof(_buffer));
	label_text("Wait");
	status	= hpdi32_init(handle);

	errs	+= _api_buffer_alloc(handle);

	if (status == GSC_SUCCESS)
		status	= HPDI32_IO_MODE__TX_PIO(handle);

	if (status == GSC_SUCCESS)
		status	= HPDI32_IO_OVERLAP_ENABLE__TX_YES(handle);

	if (status == GSC_SUCCESS)
		status	= HPDI32_TX_ENABLE__YES(handle);

	/*
	*	Things are configured such that ...
	*	1. The actual data transfer out the cable will take a long time.
	*	2. The write() request will timeout after one second.
	*	The result is that the wait request should complete successfully.
	*/

	if (status == GSC_SUCCESS)
		status	= HPDI32_IO_TIMEOUT__TX_SET(handle, 1);

	if (status == GSC_SUCCESS)
		status	= HPDI32_TX_CLOCK_DIVIDER__SET(handle, 1000);

	if (status == GSC_SUCCESS)
		status	= WRITE(handle, xfer);

	if (status == GSC_SUCCESS)
	{
		status	= hpdi32_io_wait(handle, HPDI32_WHICH_TX, 1000);

		if (status == GSC_WAIT_TIMEOUT)
			status	= GSC_SUCCESS;
	}

	hpdi32_init(handle);
	errs	+= (status == GSC_SUCCESS) ? 0 : 1;

	errs	+= _api_buffer_free(handle);

	if (errs)
		printf("FAIL <---\n");
	else
		printf("PASS\n");

	return(errs);
}



/*****************************************************************************
*
*	Function:	io_notify_tests
*
*	Purpose:
*
*		Perform I/O notification tests.
*
*	Arguments:
*
*		handle	The handle used to access the device under test.
*
*	Returned:
*
*		0		All went well.
*		> 0		The number of errors seen.
*
*****************************************************************************/

int io_notify_tests(void* handle)
{
	int	errs	= 0;

	label_text("I/O Complete Notification");
	printf("\n");
	label_level_inc();

	errs	+= _io_callback_test(handle);
	errs	+= _io_wait_test(handle);

	label_level_dec();
	return(errs);
}



/*****************************************************************************
*
*	Function:	_data_size_config
*
*	Purpose:
*
*		Configure the device for a Data Size test.
*
*	Arguments:
*
*		handle		The handle of the board of interest.
*
*		mode		The mode to apply.
*
*		size		The Data Size value to apply.
*
*	Returned:
*
*		0			All went well.
*		> 0			The number of errors encounterred.
*
*****************************************************************************/

static U32 _data_size_config(void* handle, U32 mode, U32 size)
{
	U32	status;

	status	= hpdi32_init(handle);

	if (status == GSC_SUCCESS)
		status	= HPDI32_IO_MODE__TX_SET(handle, mode);

	if (status == GSC_SUCCESS)
		status	= HPDI32_IO_MODE__RX_SET(handle, mode);

	if (status == GSC_SUCCESS)
		status	= HPDI32_TX_CLOCK_DIVIDER__SET(handle, 100);

	if (status == GSC_SUCCESS)
		status	= HPDI32_IO_DATA_SIZE__TX_SET(handle, size);

	if (status == GSC_SUCCESS)
		status	= HPDI32_IO_DATA_SIZE__RX_SET(handle, size);

	if (status == GSC_SUCCESS)
		status	= HPDI32_IO_PIO_THRESHOLD__TX_NONE(handle);

	if (status == GSC_SUCCESS)
		status	= HPDI32_IO_PIO_THRESHOLD__RX_NONE(handle);

	if (status == GSC_SUCCESS)
		status	= HPDI32_TX_ENABLE__YES(handle);

	if (status == GSC_SUCCESS)
		status	= HPDI32_RX_ENABLE__YES(handle);

	return(status);
}



/*****************************************************************************
*
*	Function:	_data_size_io_test
*
*	Purpose:
*
*		Verify the Data Size operation for the current I/O mode.
*
*	Arguments:
*
*		handle		The handle of the board of interest.
*
*		mode		The I/O mode to test.
*
*		api_buf		Use an API Buffer?
*
*	Returned:
*
*		0			All went well.
*		> 0			The number of errors encounterred.
*
*****************************************************************************/

static int _data_size_io_test(void* handle, U32 mode, int api_buf)
{
	static const U8	u8_8_data[16]	=
	{
		0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
		0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F
	};

	static const U16	u16_16_data[16]	=
	{
		0x0001, 0x0203, 0x0405, 0x0607, 0x0809, 0x0A0B, 0x0C0D, 0x0E0F,
		0x1011, 0x1213, 0x1415, 0x1617, 0x1819, 0x1A1B, 0x1C1D, 0x1E1F
	};

	static const U32	u8_32_data[16]	=
	{
		0x00000000, 0x00000001, 0x00000002, 0x00000003,
		0x00000004, 0x00000005, 0x00000006, 0x00000007,
		0x00000008, 0x00000009, 0x0000000A, 0x0000000B,
		0x0000000C, 0x0000000D, 0x0000000E, 0x0000000F
	};

	static const U32	u16_32_data[16]	=
	{
		0x00000001, 0x00000203, 0x00000405, 0x00000607,
		0x00000809, 0x00000A0B, 0x00000C0D, 0x00000E0F,
		0x00001011, 0x00001213, 0x00001415, 0x00001617,
		0x00001819, 0x00001A1B, 0x00001C1D, 0x00001E1F
	};

	static const U32	u32_32_data[16]	=
	{
		0x00010203, 0x04050607, 0x08090A0B, 0x0C0D0E0F,
		0x10111213, 0x14151617, 0x18191A1B, 0x1C1D1E1F,
		0x20212223, 0x24252627, 0x28292A2B, 0x2C2D2E2F,
		0x30313233, 0x34353637, 0x38393A3B, 0x3C3D3E3F
	};

	static const struct
	{
		U32			hpdi32_size;	// Parameter value.
		size_t		io_size;		// Buffer size in bytes
		const void*	io_data;		// I/O read/write data image
		size_t		reg_samp_size;	// Unit size in bytes.
		U32			reg_mask;		// Set of bits of interest.
		size_t		reg_samples;	// Number of samples
		const U32*	reg_data;		// Data value list.
	} list[]	=
	{
		{
			/* hpdi32_size		*/	HPDI32_IO_DATA_SIZE_8_BITS,
			/* io_size			*/	sizeof(u8_8_data),
			/* io_data			*/	u8_8_data,
			/* reg_samp_size	*/	1,
			/* reg_mask			*/	0x000000FF,
			/* reg_samples		*/	ARRAY_ELEMENTS(u8_32_data),
			/* reg_data			*/	u8_32_data
		},
		{
			/* hpdi32_size		*/	HPDI32_IO_DATA_SIZE_16_BITS,
			/* io_size			*/	sizeof(u16_16_data),
			/* io_data			*/	u16_16_data,
			/* reg_samp_size	*/	2,
			/* reg_mask			*/	0x0000FFFF,
			/* reg_samples		*/	ARRAY_ELEMENTS(u16_32_data),
			/* reg_data			*/	u16_32_data
		},
		{
			/* hpdi32_size		*/	HPDI32_IO_DATA_SIZE_32_BITS,
			/* io_size			*/	sizeof(u32_32_data),
			/* io_data			*/	u32_32_data,
			/* reg_samp_size	*/	4,
			/* reg_mask			*/	0xFFFFFFFF,
			/* reg_samples		*/	ARRAY_ELEMENTS(u32_32_data),
			/* reg_data			*/	u32_32_data
		}
	};

	unsigned long	api_size;
	U32				buffer[1024];
	void*			buf			= buffer;
	int				errs		= 0;
	unsigned long	get;
	U32				got;
	int				i;
	int				j;
	int				skip		= 0;
	U32				status		= GSC_SUCCESS;
	U32				status_eval	= GSC_SUCCESS;
	U8*				u8p;
	U16*			u16p;
	U32*			u32p;
	U32				xfer;
	int				verbose	= 1;
	unsigned long	width;

	memset(buffer, 0, sizeof(buffer));

	/*
	*	Perform write tests. Verify with hpdi32_write() and hpdi32_reg_read().
	*/

	for (i = 0; (status == GSC_SUCCESS) && (i < (int) ARRAY_ELEMENTS(list)); i++)
	{
		status	= _data_size_config(handle, mode, list[i].hpdi32_size);
		if (status != GSC_SUCCESS)	break;

		if (api_buf)
		{
			// Use an API Buffer.
			status	= HPDI32_IO_BUFFER_SIZE__TX_SET(handle, 1024, &api_size);

			if ((status != GSC_SUCCESS) || (api_size < 1024))
			{
				printf(	"(%d. HPDI32_IO_BUFFER_SIZE__TX_SET(handle, 1024, %lu) error = 0x%lX) ",
						__LINE__,
						(long) api_size,
						(long) status);
				status	= GSC_FAILED;
				break;
			}

			status	= HPDI32_IO_BUFFER_POINTER__TX_GET(handle, &get);
			buf		= (void*) get;

			if ((status != GSC_SUCCESS) || (buf == NULL))
			{
				printf(	"(%d. HPDI32_IO_BUFFER_POINTER__TX_GET(handle, 0x%lX) error = 0x%lX) ",
						__LINE__,
						(long) buf,
						(long) status);
				status	= GSC_FAILED;
				break;
			}
		}
		else
		{
			// Use an Application Buffer.
			buf	= buffer;
		}

		memcpy(buf, list[i].io_data, list[i].io_size);

		// Write data via hpdi32_write().

		status	= hpdi32_write(handle, buf, list[i].io_size, &xfer);

		if ((status != GSC_SUCCESS) || (xfer != list[i].io_size))
		{
			printf(	"(%d. hpdi32_write(handle, buf, %lu, %lu) error = 0x%lX) ",
					__LINE__,
					(long) list[i].io_size,
					(long) xfer,
					(long) status);
			status	= GSC_FAILED;
			break;
		}

		sleep_ms(1000);	// Wait for data movement.

		// Retrieve the data via hpdi32_reg_read().

		for (j = 0; j < (int) list[i].reg_samples; j++)
		{
			status	= hpdi32_reg_read(handle, HPDI32_FDR, &buffer[j]);

			if (status != GSC_SUCCESS)
			{
				printf(	"(%d. hpdi32_reg_read(handle, HPDI32_FDR, 0x%lX) error = 0x%lX) ",
						__LINE__,
						(long) buffer[j],
						(long) status);
				break;
			}
		}

		if (status != GSC_SUCCESS)
			break;

		// Verify the retrieved data.

		for (j = 0; j < (int) list[i].reg_samples; j++)
		{
			if ((buffer[j] & list[i].reg_mask) != list[i].reg_data[j])
			{
				if (verbose == 0)
				{
					status	= GSC_FAILED;
					printf("(%d. %d-bit I/O write) ", __LINE__, (int) list[i].reg_samp_size * 8);
					break;
				}

				status_eval	= GSC_FAILED;
				printf("\n %d-bit I/O write\n", (int) list[i].reg_samp_size * 8);

				for (j = 0; j < (int) list[i].reg_samples; j++)
				{
					printf(	"%4d. want 0x%08lX, got 0x%08lX (0x%08lX & 0x%08lX)",
							j,
							(long) list[i].reg_data[j],
							(long) (buffer[j] & list[i].reg_mask),
							(long) buffer[j],
							(long) list[i].reg_mask);

					if ((long) list[i].reg_data[j] != (long) (buffer[j] & list[i].reg_mask))
						printf(" <---");

					printf("\n");
				}
			}
		}
	}

	/*
	*	Perform read tests. Verify with hpdi32_reg_write() and hpdi32_read().
	*/

	for (i = 0; (status == GSC_SUCCESS) && (i < (int) ARRAY_ELEMENTS(list)); i++)
	{
		if (mode == HPDI32_IO_MODE_DMDMA)
		{
			/*
			*	There is a potential for an Rx Under Run if ...
			*		using Demand Mode DMA and one of the below
			*		using  8-bit data mode on a 64-bit or 32-bit board
			*		using 16-bit data mode on a 64-bit or 32-bit board
			*		using 32-bit data mode on a 64-bit board
			*/

			if (list[i].hpdi32_size == HPDI32_IO_DATA_SIZE_8_BITS)
				continue;

			if (list[i].hpdi32_size == HPDI32_IO_DATA_SIZE_16_BITS)
				continue;

			status	= HPDI32_MISC_PCI_BUS_WIDTH__GET(handle, &width);

			if (status != GSC_SUCCESS)
			{
				printf(	"(%d. HPDI32_MISC_PCI_BUS_WIDTH__GET() error = 0x%lX) ",
						__LINE__,
						(long) status);
				break;
			}

			if (width == HPDI32_MISC_PCI_BUS_WIDTH_64)
				continue;

		}

		status	= _data_size_config(handle, mode, list[i].hpdi32_size);
		if (status != GSC_SUCCESS)	break;

		if (api_buf)
		{
			// Use an API Buffer.
			status	= HPDI32_IO_BUFFER_SIZE__RX_SET(handle, 1024, &api_size);

			if ((status != GSC_SUCCESS) || (api_size < 1024))
			{
				printf(	"(%d. HPDI32_IO_BUFFER_SIZE__RX_SET(handle, 1024, %lu) error = 0x%lX) ",
						__LINE__,
						(long) api_size,
						(long) status);
				status	= GSC_FAILED;
				break;
			}

			status	= HPDI32_IO_BUFFER_POINTER__RX_GET(handle, &get);
			buf		= (void*) get;

			if ((status != GSC_SUCCESS) || (buf == NULL))
			{
				printf(	"(%d. HPDI32_IO_BUFFER_POINTER__RX_GET(handle, 0x%lX) error = 0x%lX) ",
						__LINE__,
						(long) buf,
						(long) status);
				status	= GSC_FAILED;
				break;
			}
		}
		else
		{
			// Use an Application Buffer.
			buf	= buffer;
		}

		// Write the data via hpdi32_reg_write(), then send it out.

		for (j = 0; j < (int) list[i].reg_samples; j++)
		{
			status	= hpdi32_reg_write(handle, HPDI32_FDR, list[i].reg_data[j]);

			if (status != GSC_SUCCESS)
			{
				printf(	"(%d. hpdi32_reg_write(handle, HPDI32_FDR, 0x%lX) error = 0x%lX) ",
						__LINE__,
						(long) list[i].reg_data[j],
						(long) status);
				break;
			}
		}

		if (status != GSC_SUCCESS)
			break;

		status	= HPDI32_TX_FLOW_CONTROL__START(handle);

		if (status != GSC_SUCCESS)
		{
			printf(	"(%d. HPDI32_TX_FLOW_CONTROL__START(), error = 0x%lX) ",
					__LINE__,
					(long) status);
			break;
		}

		sleep_ms(1000);	// Wait for data movement.

		// Retrieve the data via hpdi32_read().

		status	= hpdi32_read(handle, buf, list[i].io_size, &xfer);

		if ((status != GSC_SUCCESS) || (xfer != list[i].io_size))
		{
			printf(	"(%d. hpdi32_read(handle, buf, %lu, %lu) error = 0x%lX) ",
					__LINE__,
					(long) list[i].io_size,
					(long) xfer,
					(long) status);
			status	= GSC_FAILED;
			break;
		}

		// Verify the retrieved data.

		u8p		= buf;
		u16p	= buf;
		u32p	= buf;

		for (j = 0; j < (int) list[i].reg_samples; j++)
		{
			switch (list[i].reg_samp_size)
			{
				default:	status	= GSC_FAILED;
				case 1:		got		= u8p[j];	break;
				case 2:		got		= u16p[j];	break;
				case 4:		got		= u32p[j];	break;
			}

			if (got != list[i].reg_data[j])
			{
				if (verbose == 0)
				{
					status	= GSC_FAILED;
					printf("(%d-bit I/O read) ", (int) list[i].reg_samp_size * 8);
					break;
				}

				status_eval	= GSC_FAILED;
				printf("\n %d-bit I/O read\n", (int) list[i].reg_samp_size * 8);

				for (j = 0; j < (int) list[i].reg_samples; j++)
				{
					switch (list[i].reg_samp_size)
					{
						default:	status	= GSC_FAILED;
						case 1:		got		= u8p[j];	break;
						case 2:		got		= u16p[j];	break;
						case 4:		got		= u32p[j];	break;
					}

					printf(	"%4d. want 0x%08lX, got 0x%08lX",
							j,
							(long) list[i].reg_data[j],
							(long) got);

					if ((long) list[i].reg_data[j] != (long) got)
						printf(" <---");

					printf("\n");
				}
			}
		}
	}

	/*
	*	The results.
	*/

	status	= (status_eval == GSC_SUCCESS) ? status : status_eval;
	errs	= (status == GSC_SUCCESS) ? 0 : 1;

	if (skip)
		;
	else if (errs)
		printf("FAIL <---\n");
	else
		printf("PASS\n");

	fflush(stdout);
	return(errs);
}



/*****************************************************************************
*
*	Function:	io_data_size_io_tests
*
*	Purpose:
*
*		Perform loopback based testing of the Data Size  features to verify
*		data integrity.
*
*	Arguments:
*
*		handle		The handle of the board to access.
*
*	Returned:
*
*		0			All went well.
*		> 0			The number of errors encounterred.
*
*****************************************************************************/

int io_data_size_io_tests(void* handle)
{
	static const struct
	{
		const char*	name;
		U32			mode;
		int			api_buf;
	} list[]	=
	{
		{
			/* name		*/	"PIO (User Buf)",
			/* mode		*/	HPDI32_IO_MODE_PIO,
			/* api_buf	*/	0
		},
		{
			/* name		*/	"PIO (API Buf)",
			/* mode		*/	HPDI32_IO_MODE_PIO,
			/* api_buf	*/	1
		},
		{
			/* name		*/	"DMA (User Buf, Auto)",
			/* mode		*/	HPDI32_IO_MODE_DMA,
			/* api_buf	*/	0
		},
		{
			/* name		*/	"DMA (API Buf, Auto)",
			/* mode		*/	HPDI32_IO_MODE_DMA,
			/* api_buf	*/	1
		},
		{
			/* name		*/	"DMDMA (User Buf)",
			/* mode		*/	HPDI32_IO_MODE_DMDMA,
			/* api_buf	*/	0
		},
		{
			/* name		*/	"DMDMA (API Buf)",
			/* mode		*/	HPDI32_IO_MODE_DMDMA,
			/* api_buf	*/	1
		}
	};

	int			errs	= 0;
	int			i;
	long long	mem;
	fifo_info_t	rx_info;
	U32			rx_size;
	U32			status;
	fifo_info_t	tx_info;
	U32			tx_size;

	label_text("Data Size Tests");
	printf("\n");
	fifo_info_tx(&tx_info, &tx_size);
	fifo_info_rx(&rx_info, &rx_size);
	label_level_inc();

	for (i = 0; i < (int) ARRAY_ELEMENTS(list); i++)
	{
		label_text(list[i].name);

		if (tx_info == FIFO_NONE)
		{
			printf("SKIPPED  (%d. no Tx FIFOs)\n", __LINE__);
			continue;
		}

		if (rx_info == FIFO_NONE)
		{
			printf("SKIPPED  (%d. no Rx FIFOs)\n", __LINE__);
			continue;
		}

		mem		= id_total_mem_silent();

		if (((mem / _1G) >= 4) &&
			(list[i].mode != HPDI32_IO_MODE_PIO) &&
			(list[i].api_buf == 0))
		{
			printf(	"SKIPPED  (%d. Test not supported on systems with 4G or more of memory.)\n",
					__LINE__);
			continue;
		}

		status	= hpdi32_init(handle);

		if (status == GSC_SUCCESS)
		{
			errs	+= _data_size_io_test(handle, list[i].mode, list[i].api_buf);
		}
		else
		{
			errs++;
			printf("FAIL <---\n");
		}

		hpdi32_init(handle);
	}

	label_level_dec();
	return(errs);
}



