/*-------------------------------------------------------------------------*/
/*!
 * \file
 * \brief
 *
 *
 * File:
 *
 * Purpose:
 *
 *
 * Copyright (c) 2002-2004 Logic Product Development
 *
 * NOTICE:
 *	This file contains source code, ideas, techniques, and information
 *	(the Information) which are Proprietary and Confidential Information
 *	of Logic Product Development, Inc.  This Information may not be used
 *	by or disclosed to any third party except under written license, and
 *	shall be subject to the limitations prescribed under license.
 *
 *
 */
/*-------------------------------------------------------------------------*/
#include <windows.h>
#include <nkintr.h>
#include <ceddk.h>
#include <giisr.h>



/* Global variables.
 *
 */
static GIISR_INFO g_info[MAX_GIISR_INSTANCES];
static DWORD g_port_value[MAX_GIISR_INSTANCES];

/* Forward declarations of helper functions. */
static void my_mem_copy(GIISR_INFO *dst, const GIISR_INFO *src);

/*-------------------------------------------------------------------------*/
/*!
 * \brief	Entry into this dynamic link library.
 *
 * \return	TRUE  to indicate success.
 *			FALSE to indicate failure.
 */
/*-------------------------------------------------------------------------*/
BOOL __stdcall
DllMain(HINSTANCE hinstDll, DWORD reason, VOID *reserved)
{
	return (TRUE);

} /* end DllMain() */


/*-------------------------------------------------------------------------*/
/*!
 * \brief	First function called when the ISR is installed.
 *
 * \b Purpose:
 *
 *	This function is implemented by an installable ISR to return a value
 *	which references a particular instance of the ISR.
 *
 * \return	A reference value which identifies the instance to indicate
 *			success.
 *			A value of negative one (-1) to indicate failure.
 */
/*-------------------------------------------------------------------------*/
DWORD
CreateInstance(void)
{

	static DWORD g_instance = -1;

	if ( g_instance + 1 >= MAX_GIISR_INSTANCES )
	{
		return (-1);
	}

	g_instance++;

	/* Initialize information structure. */
	g_info[g_instance].SysIntr		= SYSINTR_CHAIN;
	g_info[g_instance].CheckPort	= FALSE;
	g_info[g_instance].UseMaskReg	= FALSE;

	/* Initialize port value. */
	g_port_value[g_instance] = 0;

	return g_instance;

} /* end CreateInstance() */


/*-------------------------------------------------------------------------*/
/*!
 * \brief	Enables a communication path from IST to the ISR.
 *
 * \b Purpose:
 *
 *	This function gets exported by the installable interrupt service routine's
 *	(ISR) dynamic link library (DLL). It enables a communication path between
 *	the device drivers interrupt service thread (IST) and the ISR.
 *
 * \param	instance
 *			Value returned from the CreateInstance() function.
 *
 * \param	ioctl
 *			Input/output control (IOCTL_GIISR_PORTVALUE or IOCTL_GIISR_INFO).
 *
 * \param	buf_in
 *			Pointer to data-in buffer.
 *
 * \param	len_in
 *			Length in bytes of parameter "buf_in."
 *
 * \param	buf_out
 *			Pointer to data-out buffer.
 *
 * \param	len_out
 *			Length in bytes of parameter "buf_out."
 *
 * \param	actual_out
 *			Pointer to a variable where this function may store the actual
 *			number of bytes copied into the "buf_out" parameter.
 *
 * \return	TRUE  to indicate success.
 *			FALSE to indicate failure.
 */
/*-------------------------------------------------------------------------*/
BOOL
IOControl(
	DWORD instance,
	DWORD ioctl,
	VOID *buf_in,
	DWORD len_in,
	VOID *buf_out,
	DWORD len_out,
	DWORD *actual_out	)
{

	/* We haven't copied anything yet and might not, so initialize the
	 * actual_out parameter to zero to be safe. */
	if ( actual_out )
		*actual_out = 0;

	switch ( ioctl )
	{
	case IOCTL_GIISR_PORTVALUE:
		/* This input/output control is used when the IST would like to read
		 * the current value of the hardware's port. */
		if ( (len_out != g_info[instance].PortSize) ||
			 (!g_info[instance].CheckPort)			||
			 (!buf_out) )
		{
			/* Invalid size of output buffer, not checking port, or
			 * invalid output buffer pointer. */
			return (FALSE);
		}

		/* Indicate the number of bytes that will be copied. */
		if (actual_out)
			*actual_out = g_info[instance].PortSize;

		/* Copy the value. */
		switch ( g_info[instance].PortSize )
		{
		case sizeof(BYTE):
			*(BYTE *)buf_out = (BYTE)g_port_value[instance];
			break;

		case sizeof(WORD):
			*(WORD *)buf_out = (WORD)g_port_value[instance];
			break;

		case sizeof(DWORD):
			*(DWORD *)buf_out = g_port_value[instance];
			break;

		default:
			if ( actual_out )
				*actual_out = 0;

			return (FALSE);
		}

		break;

	case IOCTL_GIISR_INFO:
		/* This input/output control is used when the IST is passing us a
		 * filled-in GIISR_INFO structure. This structure includes the
		 * value of the SysIntr that we should return, the address of
		 * of our hardware port, etc. */
		if ( (sizeof(GIISR_INFO) != len_in) || (!buf_in) )
		{
			/* Invalid size of input buffer or bad input buffer pointer. */
			return (FALSE);
		}

		/* The compiler may generate a memcpy call for a structure
		 * assignment. Since we are not linking with the CRT, we use
		 * our own copy routine. */
		my_mem_copy(&g_info[instance], (PGIISR_INFO)buf_in);
		break;

	default:
		/* Invalid or unrecognized input/output control. */
		return (FALSE);
	}

	return (TRUE);

} /* end IOControl() */


/*-------------------------------------------------------------------------*/
/*!
 * \brief	Exports the installable interrupt handler
 *
 * \b Purpose:
 *
 *	When the kernel receives the first SYSINTR_XXX value that is _not_
 *	SYSINTR_CHAIN, it will use that value to trigger the interrupt service
 *	thread (IST) and will stop processing the chain. If the original IRQ is
 *	still set, the kernel will call into the OEM and the interrupt chain
 *	handler will restart from the begining. If no handler is available to
 *	handle and IRQ, SYSINTR_NOP is returned to the kernel. At this point
 *	the OEM can only disable the interrupt to prevent a continuous cycle
 *	from happening.
 *
 *	Note, the name of this function is determined by the OEM and does _not_
 *	need to be "ISRHandler." As long as the correct function name gets
 *	passed to the LoadIntChainHandler() function, everything will be fine.
 *
 * \return	A SYSINTR_XXX value if an interrupt actually occured.
 *			SYSINTR_CHAIN otherwise.
 */
/*-------------------------------------------------------------------------*/
DWORD
ISRHandler(DWORD instance)
{

	DWORD mask;

	/* If the driver which loaded us told us not to actually check the
	 * hardware, don't. Simply return the correct SysIntr value and let
	 * them figure it out. */
	if ( !g_info[instance].CheckPort )
			return g_info[instance].SysIntr;

	/* Read the mask which we apply to the port's value to determine if
	 * an interrupt has occurred. */
	mask = g_info[instance].Mask;

	/* Read the port.
	 * Note, the Microsoft example code (giisr, located in Public/Common/Oak/Drivers)
	 * has a much more generic implementation of this routine. It performs
	 * different reads based on the size of the port, whether the port is
	 * memory-mapped or in I/O space, and checks to see if a mask register
	 * should be used. Since we know exactly what we are looking at here ,
	 * we can skip all of that and get right down to business. */
	g_port_value[instance] = *(DWORD *)g_info[instance].PortAddr;

	/* If interrupt bit set, return corresponding SYSINTR_XXX value. Otherwise
	 * return SYSINTR_CHAIN as our device did _not_ cause the interrupt. */
	if ( g_port_value[instance] & mask )
		return ( g_info[instance].SysIntr );
	else
		return (SYSINTR_CHAIN);



} /* end ISRHandler() */


/*-------------------------------------------------------------------------*/
/*!
 * \brief	Personal implementation of memcpy.
 *
 * \b Purpose:
 *
 *	The compiler may generate a call to memcpy() for assignments of large
 *	objects. Since this library is not linked to the C-run-time (CRT), we
 *	need to have our own personal mechanism for accomplishing this.
 *
 * \return	none.
 */
/*-------------------------------------------------------------------------*/
static void
my_mem_copy(GIISR_INFO *dst, const GIISR_INFO *src)
{
	size_t count = sizeof(GIISR_INFO);

	while ( count-- )
		*((BYTE *)dst)++ = *((BYTE *)src)++;

	return;

} /* end my_mem_copy() */


