/*++
 * Copyright (c) 1990-96 Digi International Inc., All Rights Reserved
 * 
 * This software contains proprietary and confidential information of Digi
 * International Inc.  By accepting transfer of this copy, Recipient agrees
 * to retain this software in confidence, to prevent disclosure to others,
 * and to make no use of this software other than that for which it was
 * delivered.  This is an unpublished copyrighted work of Digi International
 * Inc.  Except as permitted by federal law, 17 USC 117, copying is strictly
 * prohibited.
 * 
 * Restricted Rights Legend
 *
 * Use, duplication, or disclosure by the Government is subject to
 * restrictions set forth in sub-paragraph (c)(1)(ii) of The Rights in
 * Technical Data and Computer Software clause at DFARS 252.227-7031 or
 * subparagraphs (c)(1) and (2) of the Commercial Computer Software -
 * Restricted Rights at 48 CFR 52.227-19, as applicable.
 *
 * Digi International Inc. 11001 Bren Road East, Minnetonka, MN 55343
 *
 *
 * Last Update: $Date: 10/22/97 11:56a $
 *
 * $Header: /Components/Windows/NT/Sync/Build/Mp570/mpisr.c 6     10/22/97 11:56a Darinh $
 *
 * Revision History:
 * $Log: /Components/Windows/NT/Sync/Build/Mp570/mpisr.c $
 * 
 * 6     10/22/97 11:56a Darinh
 * Changes to make multiple board installations work better.
 * 
 * 5     9/08/97 3:45p Darinh
 * A variety of changes, including Data General fixes are included.
 * 
 * 4     7/15/97 1:05p Darinh
 * Added DigiLogError statements and message file, altered code to function
 * in DStreams environment and SNA environment with a conditional
 * compilation.
 * 
 * 3     6/06/97 1:48p Darinh
 * Everything seems functional, but more testing is required.
 * 
 * 2     4/08/97 4:55p Darinh
 * Changes made to correspond to the new DigiUtil library.  It
 * successfully transmits data!  I don't know about receiving data yet.
 * 
 * 1     4/02/97 10:08a Darinh
 * Initial checkin; ISA cards initialize, but do not pass data.
 * 
--*/
#include "digilib.h"

#include <ntddk.h>
#include "board.h"
#include "mpisr.h"
#include "io.h"
#include "hwsca.h"
#include "proto.h"
#include "debug.h"




BOOLEAN
fnISR (
	IN PKINTERRUPT	pInterrupt,
	IN PVOID		Context)
/*++

Description:

	This function will be called when a hardware interrupt occurs for one
	of our cards.  It checks to see if the interrupt is actually ours, if
	not it returns FALSE, else it schedules a DPC to service the interrupt.

Parameters:

	pInterrupt

	Context
		Address of the Controller Extension

Return Value:

	None.

--*/
{
	PCONTROLLER_EXT		pControllerExt = (PCONTROLLER_EXT) Context;
	PDEVICE_OBJECT		pDeviceObject = pControllerExt->pDeviceObject[0];
	volatile UCHAR		IntStatus = 0;


	// Check the card register for Interrupt Pending
	IntStatus = (*pControllerExt->pIO_IN) (pControllerExt->pDigiDcdIntReg);
	if (!(IntStatus & 1)) {
		return (FALSE);		//  Not our interrupt
	}

	if (PCIBus == pControllerExt->BusType)
	{
		// Disable interrupts for now.
		WRT_REG_UCHAR (pControllerExt->VirtualPlxRegisters + 0x69, 0);
	}

	// Queue the DPC
	IoRequestDpc (pDeviceObject, NULL, pControllerExt);

	return (TRUE);
}


VOID
fnServicePortInterrupt (
	IN PDEVICE_EXT	pDeviceExt)
/*++

Description:

	This routine will process all outstanding interrupts for the
	given Device Extension.  The following are the possible interrupt sources:
	1. Timer Interrupt
		- This interrupt is used for enable the Transmit Idle
		interrupt and Closing Flag sent. (See fnCheckTimer())
	2. Receive Interrupt
		- This interrupt is used for checking DSR and DCD signals
	3. Transmit Interrupt
		- This interrupt is used for checking the CTS and Transmit Idle
	4. Receive DMA Error Interrupt
		- This interrupt is used for Buffer Overflow and unprocess
		received frames overrun
	5. Receive DMA Good interrupt
		- This interrupt is used for receive End of Frame(EOM) and
		End of Transfer(EOT) interrupt
	6. Transmit DMA Error Interrupt
		- This interrupt is used for Buffer Overflow/Underflow and
		unprocess transmitted frames
	7. Transmit DMA Good Interrupt
		- This interrupt is used for transmit EOM and EOT

Parameters:

	pDeviceExt
		Address of the Device Extension.

Return Value:

	None.

--*/
{
	volatile USHORT		Isr01;
	volatile UCHAR		Isr2, bTxRxInt, bDmacInt;
	PCONTROLLER_EXT		pControllerExt = pDeviceExt->pControllerExt;

	// Allow two interrupts to be serviced per call
	int cServiceTimes = 2;

	EnableScaMemMap (pDeviceExt);
	Isr01 = READ_REGISTER_USHORT ((PUSHORT) &pDeviceExt->pSca->ComIsr01);
	Isr2 = READ_REGISTER_UCHAR ((PUCHAR) &pDeviceExt->pSca->ComIsr2);
	DisableMemory (pDeviceExt);

	pDeviceExt->SignalService = FALSE;


	do
	{
		bTxRxInt = (UCHAR) Isr01;
		bDmacInt = (UCHAR) (Isr01 >> 8);

		// Check for Timer interrupt
		if ((Isr2 & pDeviceExt->ScaIntCtrlTimerE) != 0) {
			fnCheckTimer (pDeviceExt);
		}

		// Receive DMA error interrupts
		if ((bDmacInt & pDeviceExt->ScaIntCtrlRxDmia0E) != 0) {
			fnCheckRxDmaError (pDeviceExt);
		}

		// Receive DMA good interrupts
		if ((bDmacInt & pDeviceExt->ScaIntCtrlRxDmib0E) != 0) {
			fnCheckRxDmaGood (pDeviceExt);
		}

		// Transmit DMA error interrupts
		if ((bDmacInt & pDeviceExt->ScaIntCtrlTxDmia1E) != 0) {
			fnCheckTxDmaError (pDeviceExt);
		}

		// Transmit DMA good interrupts
		if ((bDmacInt & pDeviceExt->ScaIntCtrlTxDmib1E) != 0) {
			fnCheckTxDmaGood (pDeviceExt);
		}

		// Receive interrupt
		if ((bTxRxInt & pDeviceExt->ScaIntCtrlRxIntE) != 0) {
			fnCheckDsrStatus (pDeviceExt);
		}

		// Transmit interrupt
		if ((bTxRxInt & pDeviceExt->ScaIntCtrlTxIntE) != 0) {
			fnCheckCtsStatus (pDeviceExt);
		}

		cServiceTimes--;

		EnableScaMemMap (pDeviceExt);
		Isr01 = READ_REGISTER_USHORT ((PUSHORT) &pDeviceExt->pSca->ComIsr01);
		Isr2 = READ_REGISTER_UCHAR ((PUCHAR) &pDeviceExt->pSca->ComIsr2);
		DisableMemory (pDeviceExt);

	} while (((Isr01 & pDeviceExt->IntMsciDmacMask)
		|| (Isr2 & pDeviceExt->ScaIntCtrlTimerE)) && cServiceTimes);

#if !NT5_BUILD

	if ((pDeviceExt->SignalService) && (pDeviceExt->pUserEvent != NULL))
	{
		KeSetEvent (pDeviceExt->pUserEvent, 0, FALSE);
	}

#endif

}


VOID
fnDPCRoutine (
	IN PKDPC			pDpc,
	IN PDEVICE_OBJECT	pDeviceObject,
	IN PIRP				pIrp,
	IN PVOID			DeferredContext)
/*++

Description:

	Deferred Procedure Call for the Interrupt Service Routine.  The primary
	function of this routine is to handle the appropriate interrupt type.

Parameters:

	pDpc
	pDeviceObject
		Address of the Device object.

	pIrp
	DeferredContext
		Address of the Controller extension

Return Value:

	None.

--*/
{
	PCONTROLLER_EXT		pControllerExt = (PCONTROLLER_EXT) DeferredContext;
	PDEVICE_EXT			pDeviceExt;
	UCHAR				Index = 0;
	volatile USHORT		wRegVal;
	volatile UCHAR		RegVal, Isr2RegVal;


#ifdef DBG_INTERRUPT
	Isr2RegVal = (*pControllerExt->pIO_IN) (pControllerExt->DigiIntAckReg);
#endif

	//  Make sure we are protected
	KeAcquireSpinLockAtDpcLevel (&pControllerExt->MemoryLock);

	if (!(pControllerExt->DPCRunning))
	{
		pControllerExt->DPCRunning = TRUE;

		do
		{
			pDeviceExt = pControllerExt->pDeviceExt[Index];

			EnableScaMemMap (pDeviceExt);
			wRegVal = READ_REGISTER_USHORT ((PUSHORT) &pDeviceExt->pSca->ComIsr01);
			Isr2RegVal = READ_REGISTER_UCHAR ((PUCHAR) &pDeviceExt->pSca->ComIsr2);
			DisableMemory (pDeviceExt);

			if ((wRegVal & pDeviceExt->IntMsciDmacMask) ||
				(Isr2RegVal & pDeviceExt->ScaIntCtrlTimerE))
			{
				fnServicePortInterrupt (pDeviceExt);
			}

			if (++Index == pControllerExt->AdapterLines)
				Index = 0;

			RegVal = (*pControllerExt->pIO_IN) (pControllerExt->pDigiDcdIntReg);

		} while (RegVal & 1);

		pControllerExt->DPCRunning = FALSE;
	}

	KeReleaseSpinLockFromDpcLevel (&pControllerExt->MemoryLock);

	//  Re-enable interrupts
	if (PCIBus == pDeviceExt->pControllerExt->BusType)
	{
		WRT_REG_UCHAR (pControllerExt->VirtualPlxRegisters + 0x69, 0x09);
	}


	UNREFERENCED_PARAMETER (pDpc);
	UNREFERENCED_PARAMETER (pIrp);
	UNREFERENCED_PARAMETER (pDeviceObject);
}

