You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
483 lines
15 KiB
483 lines
15 KiB
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
|
|
Copyright (c) 1991, 1992, 1993 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
isr.c
|
|
|
|
Abstract:
|
|
|
|
This module contains the interrupt service routine for the
|
|
serial driver.
|
|
|
|
Author:
|
|
|
|
Anthony V. Ercolano 26-Sep-1991
|
|
|
|
Environment:
|
|
|
|
Kernel mode
|
|
|
|
Revision History :
|
|
|
|
-----------------------------------------------------------------------------*/
|
|
|
|
#include "precomp.h"
|
|
|
|
|
|
|
|
|
|
BOOLEAN
|
|
SerialISR(IN PKINTERRUPT InterruptObject, IN PVOID Context)
|
|
{
|
|
// Holds the information specific to handling this device.
|
|
PCARD_DEVICE_EXTENSION pCard = Context;
|
|
PPORT_DEVICE_EXTENSION pPort;
|
|
BOOLEAN ServicedAnInterrupt = FALSE;
|
|
|
|
PUART_OBJECT pUart = pCard->pFirstUart;
|
|
DWORD IntsPending = 0;
|
|
|
|
UNREFERENCED_PARAMETER(InterruptObject);
|
|
|
|
#ifndef BUILD_SPXMINIPORT
|
|
// If the card is not powered, delay interrupt service until it is.
|
|
if(!(pCard->PnpPowerFlags & PPF_POWERED) && (pCard->PnpPowerFlags & PPF_STARTED))
|
|
return ServicedAnInterrupt; // Most likely the interrupt is not ours anyway.
|
|
#endif
|
|
|
|
|
|
switch(pCard->CardType)
|
|
{
|
|
case Fast4_Isa:
|
|
case Fast4_Pci:
|
|
case RAS4_Pci:
|
|
{
|
|
if((READ_PORT_UCHAR(pCard->Controller + FAST_UARTS_0_TO_7_INTS_REG) & FAST_UARTS_0_TO_3_INT_PENDING) == 0)
|
|
return ServicedAnInterrupt; // If no Uarts have interrupts pending then return.
|
|
|
|
break;
|
|
}
|
|
|
|
case Fast8_Isa:
|
|
case Fast8_Pci:
|
|
case RAS8_Pci:
|
|
{
|
|
if(READ_PORT_UCHAR(pCard->Controller + FAST_UARTS_0_TO_7_INTS_REG) == 0)
|
|
return ServicedAnInterrupt; // If no Uarts have interrupts pending then return.
|
|
|
|
break;
|
|
}
|
|
|
|
case Fast16_Isa:
|
|
case Fast16_Pci:
|
|
case Fast16FMC_Pci:
|
|
{
|
|
if((READ_PORT_UCHAR(pCard->Controller + FAST_UARTS_0_TO_7_INTS_REG) == 0)
|
|
&& (READ_PORT_UCHAR(pCard->Controller + FAST_UARTS_9_TO_16_INTS_REG) == 0))
|
|
return ServicedAnInterrupt; // If no Uarts have interrupts pending then return.
|
|
|
|
break;
|
|
}
|
|
|
|
break;
|
|
|
|
case Speed2_Pci:
|
|
case Speed2P_Pci:
|
|
case Speed4_Pci:
|
|
case Speed4P_Pci:
|
|
{
|
|
if((READ_REGISTER_ULONG( (PULONG)(pCard->LocalConfigRegisters + SPEED_GIS_REG)) & INTERNAL_UART_INT_PENDING) == 0)
|
|
return ServicedAnInterrupt; // If no Uarts have interrupts pending then return.
|
|
|
|
break;
|
|
}
|
|
|
|
case Speed2and4_Pci_8BitBus:
|
|
case Speed2P_Pci_8BitBus:
|
|
case Speed4P_Pci_8BitBus:
|
|
return ServicedAnInterrupt; // No UARTs therefore NO interrupts that are ours - we hope.
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
|
|
|
|
|
|
if(pUart)
|
|
{
|
|
while((IntsPending = pCard->UartLib.UL_IntsPending_XXXX(&pUart)))
|
|
{
|
|
pPort = (PPORT_DEVICE_EXTENSION) pCard->UartLib.UL_GetAppBackPtr_XXXX(pUart); // Get Port Extension for UART.
|
|
|
|
SpxDbgMsg(ISRINFO, ("%s: Int on 0x%lX", PRODUCT_NAME, IntsPending));
|
|
|
|
// Service receive status interrupts
|
|
if(IntsPending & UL_IP_RX_STAT)
|
|
{
|
|
BYTE LineStatus = 0;
|
|
DWORD RxStatus;
|
|
pPort->pUartLib->UL_GetStatus_XXXX(pUart, &RxStatus, UL_GS_OP_LINESTATUS);
|
|
|
|
// If OVERRUN/PARITY/FRAMING/DATA/BREAK error
|
|
if(RxStatus & (UL_US_OVERRUN_ERROR | UL_US_PARITY_ERROR | UL_US_FRAMING_ERROR | UL_US_DATA_ERROR | UL_US_BREAK_ERROR))
|
|
{
|
|
BYTE TmpByte;
|
|
|
|
// If the application has requested it, abort all the reads and writes on an error.
|
|
if(pPort->HandFlow.ControlHandShake & SERIAL_ERROR_ABORT)
|
|
KeInsertQueueDpc(&pPort->CommErrorDpc, NULL, NULL);
|
|
/*
|
|
if(pPort->EscapeChar)
|
|
{
|
|
TmpByte = pPort->EscapeChar;
|
|
pPort->pUartLib->UL_ImmediateByte_XXXX(pPort->pUart, &TmpByte, UL_IM_OP_WRITE);
|
|
|
|
|
|
if(RxStatus & UL_US_DATA_ERROR)
|
|
{
|
|
TmpByte = SERIAL_LSRMST_LSR_DATA
|
|
pPort->pUartLib->UL_ImmediateByte_XXXX(pPort->pUart, &TmpByte, UL_IM_OP_WRITE);
|
|
}
|
|
else
|
|
{
|
|
TmpByte = SERIAL_LSRMST_LSR_NODATA
|
|
pPort->pUartLib->UL_ImmediateByte_XXXX(pPort->pUart, &TmpByte, UL_IM_OP_WRITE);
|
|
}
|
|
}
|
|
|
|
*/
|
|
if(RxStatus & UL_US_OVERRUN_ERROR)
|
|
{
|
|
pPort->ErrorWord |= SERIAL_ERROR_OVERRUN;
|
|
LineStatus |= SERIAL_LSR_OE;
|
|
pPort->PerfStats.SerialOverrunErrorCount++;
|
|
#ifdef WMI_SUPPORT
|
|
pPort->WmiPerfData.SerialOverrunErrorCount++;
|
|
#endif
|
|
}
|
|
|
|
if(RxStatus & UL_US_PARITY_ERROR)
|
|
{
|
|
pPort->ErrorWord |= SERIAL_ERROR_PARITY;
|
|
LineStatus |= SERIAL_LSR_PE;
|
|
pPort->PerfStats.ParityErrorCount++;
|
|
#ifdef WMI_SUPPORT
|
|
pPort->WmiPerfData.ParityErrorCount++;
|
|
#endif
|
|
}
|
|
|
|
if(RxStatus & UL_US_FRAMING_ERROR)
|
|
{
|
|
pPort->ErrorWord |= SERIAL_ERROR_FRAMING;
|
|
LineStatus |= SERIAL_LSR_FE;
|
|
pPort->PerfStats.FrameErrorCount++;
|
|
#ifdef WMI_SUPPORT
|
|
pPort->WmiPerfData.FrameErrorCount++;
|
|
#endif
|
|
}
|
|
|
|
if(RxStatus & UL_US_DATA_ERROR)
|
|
{
|
|
LineStatus |= SERIAL_LSR_DR;
|
|
}
|
|
|
|
|
|
if(RxStatus & UL_US_BREAK_ERROR)
|
|
{
|
|
pPort->ErrorWord |= SERIAL_ERROR_BREAK;
|
|
LineStatus |= SERIAL_LSR_BI;
|
|
}
|
|
|
|
/*
|
|
if(pPort->EscapeChar)
|
|
{
|
|
TmpByte = LineStatus;
|
|
pPort->pUartLib->UL_ImmediateByte_XXXX(pPort->pUart, &TmpByte, UL_IM_OP_WRITE);
|
|
}
|
|
|
|
if(RxStatus & (UL_US_OVERRUN_ERROR | UL_US_PARITY_ERROR | UL_US_FRAMING_ERROR | UL_US_DATA_ERROR))
|
|
{
|
|
if(pPort->HandFlow.FlowReplace & SERIAL_ERROR_CHAR)
|
|
{
|
|
TmpByte = pPort->SpecialChars.ErrorChar;
|
|
pPort->pUartLib->UL_ImmediateByte_XXXX(pPort->pUart, &TmpByte, UL_IM_OP_WRITE);
|
|
}
|
|
}
|
|
*/
|
|
}
|
|
|
|
if(pPort->IsrWaitMask)
|
|
{
|
|
if((pPort->IsrWaitMask & SERIAL_EV_ERR)
|
|
&& (RxStatus & (UL_US_OVERRUN_ERROR | UL_US_PARITY_ERROR | UL_US_FRAMING_ERROR | UL_US_DATA_ERROR)))
|
|
{
|
|
// if we detected a overrun/parity/framing/data error
|
|
pPort->HistoryMask |= SERIAL_EV_ERR;
|
|
}
|
|
|
|
// if we detected a break error
|
|
if((pPort->IsrWaitMask & SERIAL_EV_BREAK) && (RxStatus & UL_US_BREAK_ERROR))
|
|
pPort->HistoryMask |= SERIAL_EV_BREAK;
|
|
#ifdef USE_HW_TO_DETECT_CHAR
|
|
// if we detected the special char
|
|
if((pPort->IsrWaitMask & SERIAL_EV_RXFLAG) && (RxStatus & UL_RS_SPECIAL_CHAR_DETECTED))
|
|
pPort->HistoryMask |= SERIAL_EV_RXFLAG;
|
|
#endif
|
|
if(pPort->IrpMaskLocation && pPort->HistoryMask)
|
|
{
|
|
*pPort->IrpMaskLocation = pPort->HistoryMask;
|
|
|
|
pPort->IrpMaskLocation = NULL;
|
|
pPort->HistoryMask = 0;
|
|
|
|
pPort->CurrentWaitIrp->IoStatus.Information = sizeof(ULONG);
|
|
|
|
// Mark IRP as about to complete normally to prevent cancel & timer DPCs
|
|
// from doing so before DPC is allowed to run.
|
|
//SERIAL_SET_REFERENCE(pPort->CurrentWaitIrp, SERIAL_REF_COMPLETING);
|
|
|
|
KeInsertQueueDpc(&pPort->CommWaitDpc, NULL, NULL);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// Service receive and receive timeout interrupts.
|
|
if((IntsPending & UL_IP_RX) || (IntsPending & UL_IP_RXTO))
|
|
{
|
|
DWORD StatusFlags = 0;
|
|
int BytesReceived = pPort->pUartLib->UL_InputData_XXXX(pUart, &StatusFlags);
|
|
|
|
|
|
if(StatusFlags & UL_RS_BUFFER_OVERRUN)
|
|
{
|
|
// We have a new character but no room for it.
|
|
pPort->ErrorWord |= SERIAL_ERROR_QUEUEOVERRUN;
|
|
pPort->PerfStats.BufferOverrunErrorCount++;
|
|
#ifdef WMI_SUPPORT
|
|
pPort->WmiPerfData.BufferOverrunErrorCount++;
|
|
#endif
|
|
}
|
|
|
|
if(BytesReceived)
|
|
{
|
|
ULONG AmountInBuffer = 0;
|
|
GET_BUFFER_STATE BufferState;
|
|
|
|
pPort->ReadByIsr += BytesReceived;
|
|
pPort->PerfStats.ReceivedCount += BytesReceived; // Increment Rx Counter
|
|
#ifdef WMI_SUPPORT
|
|
pPort->WmiPerfData.ReceivedCount += BytesReceived;
|
|
#endif
|
|
|
|
pPort->pUartLib->UL_BufferControl_XXXX(pUart, &BufferState, UL_BC_OP_GET, UL_BC_BUFFER | UL_BC_IN);
|
|
AmountInBuffer = BufferState.BytesInINBuffer;
|
|
|
|
|
|
if(pPort->IsrWaitMask)
|
|
{
|
|
// Check to see if we should note the receive character
|
|
if(pPort->IsrWaitMask & SERIAL_EV_RXCHAR)
|
|
pPort->HistoryMask |= SERIAL_EV_RXCHAR;
|
|
|
|
// If we've become 80% full on this character and this is an interesting event, note it.
|
|
if((pPort->IsrWaitMask & SERIAL_EV_RX80FULL) && (AmountInBuffer >= pPort->BufferSizePt8))
|
|
pPort->HistoryMask |= SERIAL_EV_RX80FULL;
|
|
|
|
#ifndef USE_HW_TO_DETECT_CHAR
|
|
// if we detected the special char
|
|
if((pPort->IsrWaitMask & SERIAL_EV_RXFLAG) && (StatusFlags & UL_RS_SPECIAL_CHAR_DETECTED))
|
|
pPort->HistoryMask |= SERIAL_EV_RXFLAG;
|
|
#endif
|
|
if(pPort->IrpMaskLocation && pPort->HistoryMask)
|
|
{
|
|
*pPort->IrpMaskLocation = pPort->HistoryMask;
|
|
|
|
pPort->IrpMaskLocation = NULL;
|
|
pPort->HistoryMask = 0;
|
|
|
|
pPort->CurrentWaitIrp->IoStatus.Information = sizeof(ULONG);
|
|
|
|
// Mark IRP as about to complete normally to prevent cancel & timer DPCs
|
|
// from doing so before DPC is allowed to run.
|
|
//SERIAL_SET_REFERENCE(pPort->CurrentWaitIrp, SERIAL_REF_COMPLETING);
|
|
|
|
KeInsertQueueDpc(&pPort->CommWaitDpc, NULL, NULL);
|
|
}
|
|
}
|
|
|
|
|
|
// If we have a current Read IRP.
|
|
if(pPort->CurrentReadIrp && pPort->NumberNeededForRead)
|
|
{
|
|
// If our ISR currently owns the IRP the we are allowed to do something with it,
|
|
// But we only need to do something if we need to make room in the buffer
|
|
// or we have enough bytes in the buffer to complete the current read IRP.
|
|
if((SERIAL_REFERENCE_COUNT(pPort->CurrentReadIrp) & SERIAL_REF_ISR)
|
|
&& ((AmountInBuffer >= pPort->BufferSizePt8)
|
|
|| (AmountInBuffer >= pPort->NumberNeededForRead)))
|
|
{
|
|
ULONG NumberOfBytes = 0;
|
|
|
|
NumberOfBytes = pPort->pUartLib->UL_ReadData_XXXX(pPort->pUart,
|
|
(PUCHAR)(pPort->CurrentReadIrp->AssociatedIrp.SystemBuffer)
|
|
+ pPort->CurrentReadIrp->IoStatus.Information,
|
|
IoGetCurrentIrpStackLocation(pPort->CurrentReadIrp)->Parameters.Read.Length
|
|
- pPort->CurrentReadIrp->IoStatus.Information);
|
|
|
|
if(NumberOfBytes > pPort->NumberNeededForRead)
|
|
pPort->NumberNeededForRead = 0;
|
|
else
|
|
pPort->NumberNeededForRead -= NumberOfBytes;
|
|
|
|
|
|
pPort->CurrentReadIrp->IoStatus.Information += NumberOfBytes;
|
|
|
|
if(pPort->NumberNeededForRead == 0)
|
|
{
|
|
ASSERT(pPort->CurrentReadIrp->IoStatus.Information
|
|
== IoGetCurrentIrpStackLocation(pPort->CurrentReadIrp)->Parameters.Read.Length);
|
|
|
|
// Mark IRP as about to complete normally to prevent cancel & timer DPCs
|
|
// from doing so before DPC is allowed to run.
|
|
SERIAL_SET_REFERENCE(pPort->CurrentReadIrp, SERIAL_REF_COMPLETING);
|
|
|
|
KeInsertQueueDpc(&pPort->CompleteReadDpc, NULL, NULL);
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Service transmitt and transmitt empty interrupts.
|
|
if((IntsPending & UL_IP_TX) || (IntsPending & UL_IP_TX_EMPTY))
|
|
{
|
|
// No need to clear the INT it was already cleared by reading the IIR.
|
|
DWORD BytesRemaining = pPort->pUartLib->UL_OutputData_XXXX(pUart); // Output some bytes
|
|
|
|
|
|
// If we have a current Write Immediate IRP.
|
|
if(pPort->CurrentImmediateIrp)
|
|
{
|
|
if(SERIAL_REFERENCE_COUNT(pPort->CurrentImmediateIrp) & SERIAL_REF_ISR)
|
|
{
|
|
if(pPort->TransmitImmediate == TRUE)
|
|
{
|
|
// Check if the byte has been sent.
|
|
if(pPort->pUartLib->UL_ImmediateByte_XXXX(pUart, &pPort->ImmediateIndex, UL_IM_OP_STATUS) == UL_IM_NO_BYTE_TO_SEND)
|
|
{
|
|
pPort->TransmitImmediate = FALSE;
|
|
pPort->EmptiedTransmit = TRUE;
|
|
|
|
pPort->PerfStats.TransmittedCount++; // Increment Tx Counter
|
|
#ifdef WMI_SUPPORT
|
|
pPort->WmiPerfData.TransmittedCount++;
|
|
#endif
|
|
|
|
// Mark IRP as about to complete normally to prevent cancel & timer DPCs
|
|
// from doing so before DPC is allowed to run.
|
|
SERIAL_SET_REFERENCE(pPort->CurrentImmediateIrp, SERIAL_REF_COMPLETING);
|
|
|
|
// Ask to complete the IRP.
|
|
KeInsertQueueDpc(&pPort->CompleteImmediateDpc, NULL, NULL);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// If we have a current Write IRP.
|
|
if(pPort->CurrentWriteIrp && pPort->WriteLength)
|
|
{
|
|
//
|
|
// Even though all of the characters being
|
|
// sent haven't all been sent, this variable
|
|
// will be checked when the transmit queue is
|
|
// empty. If it is still true and there is a
|
|
// wait on the transmit queue being empty then
|
|
// we know we finished transmitting all characters
|
|
// following the initiation of the wait since
|
|
// the code that initiates the wait will set
|
|
// this variable to false.
|
|
//
|
|
// One reason it could be false is that
|
|
// the writes were cancelled before they
|
|
// actually started, or that the writes
|
|
// failed due to timeouts. This variable
|
|
// basically says a character was written
|
|
// by the isr at some point following the
|
|
// initiation of the wait.
|
|
//
|
|
|
|
if(SERIAL_REFERENCE_COUNT(pPort->CurrentWriteIrp) & SERIAL_REF_ISR)
|
|
{
|
|
if(pPort->WriteLength > BytesRemaining)
|
|
{
|
|
pPort->PerfStats.TransmittedCount += (pPort->WriteLength - BytesRemaining); // Increment Tx Counter
|
|
#ifdef WMI_SUPPORT
|
|
pPort->WmiPerfData.TransmittedCount += (pPort->WriteLength - BytesRemaining);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
pPort->PerfStats.TransmittedCount += pPort->WriteLength; // Increment Tx Counter
|
|
#ifdef WMI_SUPPORT
|
|
pPort->WmiPerfData.TransmittedCount += pPort->WriteLength;
|
|
#endif
|
|
}
|
|
|
|
pPort->WriteLength = BytesRemaining;
|
|
pPort->EmptiedTransmit = TRUE;
|
|
|
|
|
|
if(pPort->WriteLength == 0) // If write is complete - lets complete the IRP
|
|
{
|
|
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(pPort->CurrentWriteIrp);
|
|
|
|
// No More characters left. This write is complete. Take care when
|
|
// updating the information field, we could have an xoff
|
|
// counter masquerading as a write irp.
|
|
|
|
pPort->CurrentWriteIrp->IoStatus.Information
|
|
= (IrpSp->MajorFunction == IRP_MJ_WRITE)
|
|
? (IrpSp->Parameters.Write.Length) : (1);
|
|
|
|
// Mark IRP as about to complete normally to prevent cancel & timer DPCs
|
|
// from doing so before DPC is allowed to run.
|
|
SERIAL_SET_REFERENCE(pPort->CurrentWriteIrp, SERIAL_REF_COMPLETING);
|
|
|
|
KeInsertQueueDpc(&pPort->CompleteWriteDpc, NULL, NULL);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// Service modem interrupts.
|
|
if(IntsPending & UL_IP_MODEM)
|
|
{
|
|
SerialHandleModemUpdate(pPort, FALSE);
|
|
}
|
|
|
|
|
|
// Save a pointer to the UART serviced so it can be the first UART serviced
|
|
// in the list the next time the ISR is called.
|
|
//pCard->pFirstUart = pUart;
|
|
|
|
ServicedAnInterrupt = TRUE;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
return ServicedAnInterrupt;
|
|
}
|
|
|
|
|