📄 isr.cpp
字号:
/*++
Abstract:
This module contains the interrupt service routine for the
serial driver.
--*/
#include "precomp.h"
BOOLEAN KdSerialDevice::SharerIsr(PVOID Context)
/*++
Routine Description:
This is the isr that the system will call if there are any
serial devices sharing the same interrupt and they aren't
all confined to one multiport card. This routine traverses
a linked list structure that contains a pointer to a more
refined isr and context that will indicate whether one of
the ports on this interrupt actually was interrupting.
Arguments:
Context - Pointer to a linked list of contexts and isrs.
device.
Return Value:
This function will return TRUE if a serial port using this
interrupt was the source of this interrupt, FALSE otherwise.
--*/
{
BOOLEAN servicedAnInterrupt = FALSE;
BOOLEAN thisPassServiced;
PLIST_ENTRY interruptEntry = (PLIST_ENTRY) Context;
PLIST_ENTRY firstInterruptEntry = interruptEntry;
do {
thisPassServiced = FALSE;
do {
PKdSerialDevice pDevice = CONTAINING_RECORD(
interruptEntry,
KdSerialDevice,
m_TopLevelSharers
);
thisPassServiced |= (this->*(pDevice->m_TopLevelIsr.pFunction))(pDevice->m_TopLevelIsr.Context);
servicedAnInterrupt |= thisPassServiced;
interruptEntry = interruptEntry->Flink;
} while (interruptEntry != firstInterruptEntry);
} while (thisPassServiced);
return servicedAnInterrupt;
}
BOOLEAN KdSerialDevice::IndexedMultiportIsr(PVOID Context)
/*++
Routine Description:
This routine is used to figure out if a port on a multiport
card is the source of an interrupt. If so, this routine
uses a dispatch structure to actually call the normal isr
to process the interrupt.
NOTE: This routine is peculiar to Digiboard interrupt status registers.
Arguments:
Context - Points to a dispatch structure that contains the
device of each port on this multiport card.
Return Value:
This function will return TRUE if a serial port using this
interrupt was the source of this interrupt, FALSE otherwise.
--*/
{
BOOLEAN servicedAnInterrupt = FALSE;
BOOLEAN thisStatusReadServiced;
PSERIAL_MULTIPORT_DISPATCH dispatch = (PSERIAL_MULTIPORT_DISPATCH) Context;
ULONG whichPort;
UCHAR statusRegister;
do {
thisStatusReadServiced = FALSE;
statusRegister = READ_PORT_UCHAR(
dispatch->InterruptStatus
);
whichPort = statusRegister & 0x07;
// We test against 0xff, which signals that no port
// is interrupting. The reason 0xff (rather than 0)
// is that that would indicate the 0th (first) port
// or the 0th daisy chained card.
if (statusRegister != 0xff)
{
if (dispatch->pdvDevices[whichPort])
{
thisStatusReadServiced = dispatch->pdvDevices[whichPort]->ISR(NULL);
servicedAnInterrupt |= thisStatusReadServiced;
}
}
} while (thisStatusReadServiced);
return servicedAnInterrupt;
}
BOOLEAN KdSerialDevice::BitMappedMultiportIsr(PVOID Context)
/*++
Routine Description:
This routine is used to figure out if a port on a multiport
card is the source of an interrupt. If so, this routine
uses a dispatch structure to actually call the normal isr
to process the interrupt.
NOTE: This routine is peculiar to status registers that use
a bitmask to denote the interrupting port.
Arguments:
Context - Points to a dispatch structure that contains the
device of each port on this multiport card.
Return Value:
This function will return TRUE if a serial port using this
interrupt was the source of this interrupt, FALSE otherwise.
--*/
{
BOOLEAN servicedAnInterrupt = FALSE;
PSERIAL_MULTIPORT_DISPATCH dispatch = (PSERIAL_MULTIPORT_DISPATCH)Context;
ULONG whichPort;
UCHAR statusRegister;
do {
statusRegister = READ_PORT_UCHAR(
dispatch->InterruptStatus
);
if (dispatch->MaskInverted)
statusRegister = ~statusRegister;
statusRegister &= dispatch->UsablePortMask;
if (statusRegister)
{
if (statusRegister & 0x0f)
{
if (statusRegister & 0x03)
{
if (statusRegister & 1)
whichPort = 0;
else
whichPort = 1;
}
else
{
if (statusRegister & 0x04)
whichPort = 2;
else
whichPort = 3;
}
}
else
{
if (statusRegister & 0x30)
{
if (statusRegister & 0x10)
whichPort = 4;
else
whichPort = 5;
}
else
{
if (statusRegister & 0x40)
whichPort = 6;
else
whichPort = 7;
}
}
if (dispatch->pdvDevices[whichPort])
{
if (dispatch->pdvDevices[whichPort]->ISR(NULL))
servicedAnInterrupt = TRUE;
}
}
} while (statusRegister);
return servicedAnInterrupt;
}
BOOLEAN KdSerialDevice::ISR(PVOID Context)
/*++
Routine Description:
This is the interrupt service routine for the serial port driver.
It will determine whether the serial port is the source of this
interrupt. If it is, then this routine will do the minimum of
processing to quiet the interrupt. It will store any information
necessary for later processing.
Arguments:
Context - Not used.
Return Value:
This function will return TRUE if the serial port is the source
of this interrupt, FALSE otherwise.
--*/
{
// Holds the contents of the interrupt identification record.
// A low bit of zero in this register indicates that there is
// an interrupt pending on this device.
//
UCHAR InterruptIdReg;
// Will hold whether we've serviced any interrupt causes in this
// routine.
BOOLEAN ServicedAnInterrupt;
// Make sure we have an interrupt pending. If we do then
// we need to make sure that the device is open. If the
// device isn't open then quiet the device. Note that
// if the device isn't opened when we enter this routine
// it can't open while we're in it.
InterruptIdReg = m_pController->ReadByte(INTERRUPT_IDENT_REGISTER);
if (InterruptIdReg & SERIAL_IIR_NO_INTERRUPT_PENDING)
ServicedAnInterrupt = FALSE;
else if (!m_DeviceIsOpened)
{
// We got an interrupt with the device being closed. This
// is not unlikely with a serial device. We just quite
// keep servicing the causes until it calms down.
ServicedAnInterrupt = TRUE;
do {
InterruptIdReg &= (~SERIAL_IIR_FIFOS_ENABLED);
switch (InterruptIdReg)
{
case SERIAL_IIR_RLS:
m_pController->ReadByte(LINE_STATUS_REGISTER);
break;
case SERIAL_IIR_RDA:
case SERIAL_IIR_CTI:
m_pController->ReadByte(RECEIVE_BUFFER_REGISTER);
break;
case SERIAL_IIR_THR:
// Alread clear from reading the iir.
// We want to keep close track of whether
// the holding register is empty.
m_HoldingEmpty = TRUE;
break;
case SERIAL_IIR_MS:
m_pController->ReadByte(MODEM_STATUS_REGISTER);
break;
default:
ASSERT(FALSE);
break;
}
} while (!((InterruptIdReg = m_pController->ReadByte(INTERRUPT_IDENT_REGISTER))
& SERIAL_IIR_NO_INTERRUPT_PENDING));
}
else
{
ServicedAnInterrupt = TRUE;
do {
// We only care about bits that can denote an interrupt.
InterruptIdReg &= SERIAL_IIR_RLS | SERIAL_IIR_RDA |
SERIAL_IIR_CTI | SERIAL_IIR_THR |
SERIAL_IIR_MS;
// We have an interrupt. We look for interrupt causes
// in priority order. The presence of a higher interrupt
// will mask out causes of a lower priority. When we service
// and quiet a higher priority interrupt we then need to check
// the interrupt causes to see if a new interrupt cause is
// present.
switch (InterruptIdReg)
{
case SERIAL_IIR_RLS:
ProcessLSR();
break;
case SERIAL_IIR_RDA:
case SERIAL_IIR_CTI:
// Reading the receive buffer will quiet this interrupt.
//
// It may also reveal a new interrupt cause.
UCHAR ReceivedChar;
do {
ReceivedChar = m_pController->ReadByte(RECEIVE_BUFFER_REGISTER);
m_PerfStats.ReceivedCount++;
ReceivedChar &= m_ValidDataMask;
if (!ReceivedChar &&
(m_HandFlow.FlowReplace &
SERIAL_NULL_STRIPPING))
{
// If what we got is a null character
// and we're doing null stripping, then
// we simply act as if we didn't see it.
goto ReceiveDoLineStatus;
}
if ((m_HandFlow.FlowReplace &
SERIAL_AUTO_TRANSMIT) &&
((ReceivedChar ==
m_SpecialChars.XonChar) ||
(ReceivedChar ==
m_SpecialChars.XoffChar)))
{
// No matter what happens this character
// will never get seen by the app.
if (ReceivedChar == m_SpecialChars.XoffChar)
{
m_TXHolding |= SERIAL_TX_XOFF;
if ((m_HandFlow.FlowReplace &
SERIAL_RTS_MASK) ==
SERIAL_TRANSMIT_TOGGLE)
{
m_KdDpc_StartTimerLowerRTSDpc.InsertQueue()
? m_CountOfTryingToLowerRTS++ : 0;
}
}
else
{
if (m_TXHolding & SERIAL_TX_XOFF)
{
// We got the xon char **AND*** we
// were being held up on transmission
// by xoff. Clear that we are holding
// due to xoff. Transmission will
// automatically restart because of
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -