📄 uart.cpp
字号:
GTRACE((TLEVEL, "Start write: count=%x\n", m_WriteCount));
// If not holding on the handshake signals, write the first
// character. The interrupt will enable subsequent writes.
if ( !m_TransmitHold )
Synch(LinkTo(SynchStartWrite));
}
/////////////////////////////////////////////////////////////////////////////
// SynchStartWrite
//
// Start the write request by frobbing the interrupt enable line
//
BOOLEAN Uart::SynchStartWrite(PVOID Context)
{
m_PortIntrEnable = UCHAR(0);
m_PortIntrEnable = UCHAR(SERIAL_IER_ALL);
return TRUE;
}
/////////////////////////////////////////////////////////////////////////////
// Master Isr
//
// This static member dispatches the interrupt to each of the devices
// sharing the interrupt. The context parameter is a pointer to the
// list of devices which share the interrupt object. As soon as one
// devices returns TRUE, indicating that it serviced the interrupt,
// the master isr returns. If no device services the interrupt, it
// returns FALSE.
BOOLEAN Uart::MasterIsr(PKINTERRUPT intobj, PVOID Context)
{
KInterruptSafeList<Uart>* Devices = (KInterruptSafeList<Uart>*)Context;
BOOLEAN Serviced=FALSE;
for (Uart* p=Devices->Head(TRUE); p != NULL; p = Devices->Next(p, TRUE))
if (p->Isr())
Serviced = TRUE;
return Serviced;
}
/////////////////////////////////////////////////////////////////////////////
// Isr
//
// This is the interrupt service routine for a Uart object.
//
BOOLEAN Uart::Isr(void)
{
UCHAR InterruptStatus;
BOOLEAN InterruptServiced = FALSE;
while (! ( (InterruptStatus=(UCHAR)m_PortIntrIdent) &
SERIAL_IIR_NO_INTERRUPT_PENDING) )
{
InterruptServiced = TRUE;
// Switch on the type of interrupt
switch (InterruptStatus & SERIAL_IIR_MASK)
{
//
// Receiver Line Status error
//
case SERIAL_IIR_RLS:
{
UCHAR lsr = m_PortLineStatus; // clear interrupt
if (lsr & SERIAL_LSR_ERRORS)
m_EventState |= (m_WaitMask & SERIAL_EV_ERR);
#if NTVERSION > 351
// update statistics
if (lsr & SERIAL_LSR_PE)
m_Statistics.ParityErrorCount++;
if (lsr & SERIAL_LSR_FE)
m_Statistics.FrameErrorCount++;
if (lsr & SERIAL_LSR_OE)
m_Statistics.SerialOverrunErrorCount++;
#endif
break;
}
//
// Received Data Available
//
case SERIAL_IIR_CTI: // character available
case SERIAL_IIR_RDA: // receiver data available
{
UCHAR LineStatus;
UCHAR RxData;
m_EventState |= (m_WaitMask & SERIAL_EV_RXCHAR);
do
{
RxData = m_PortRxData;
if ( (RxData == m_Chars.EventChar) )
m_EventState |= (m_WaitMask & SERIAL_EV_RXFLAG);
#if NTVERSION > 351
m_Statistics.ReceivedCount++;
#endif
// Write to IRP if read is pending
if (m_ReadBuffer)
{
*m_ReadBuffer++ = RxData;
if (--m_ReadCount == 0)
{
m_ReadBuffer = 0;
m_ReadCompleteDpc.Request((PVOID)STATUS_SUCCESS, 0);
break;
}
}
// otherwise copy the data to the input queue
else if (m_ReceiveDataQueue->Write(RxData))
{
if (m_WaitMask & SERIAL_EV_RX80FULL)
{
ULONG nAvail =
m_ReceiveDataQueue->NumberOfItemsAvailableForRead(TRUE);
if (nAvail == m_80pcThreshold)
m_EventState |= SERIAL_EV_RX80FULL;
}
}
// if the input queue is full, log an overflow
else
{
#if NTVERSION > 351
m_Statistics.SerialOverrunErrorCount++;
#endif
m_ErrorState |= SERIAL_ERROR_OVERRUN;
}
LineStatus = m_PortLineStatus;
}
while (LineStatus & SERIAL_LSR_DR);
break;
}
//
// Transmit hold register empty
//
case SERIAL_IIR_THR:
m_EventState |= (m_WaitMask & SERIAL_EV_TXEMPTY);
Transmit:
if (m_WriteBuffer)
{
if (m_WriteCount > 0)
{
m_WriteCount--;
m_PortTxData = *m_WriteBuffer++;
#if NTVERSION > 351
m_Statistics.TransmittedCount++;
#endif
}
if (m_WriteCount == 0)
{
m_WriteBuffer = 0;
m_FinalWriteCount = 0;
m_WriteCompleteDpc.Request();
}
}
break;
//
// Modem status changed
//
case SERIAL_IIR_MS:
{
UCHAR ModemStatus = m_PortModemStatus;
if (ModemStatus & SERIAL_MSR_DDCD)
m_EventState |= (m_WaitMask & SERIAL_EV_RLSD);
if (ModemStatus & SERIAL_MSR_DDSR)
m_EventState |= (m_WaitMask & SERIAL_EV_DSR);
if (ModemStatus & SERIAL_MSR_DCTS)
m_EventState |= (m_WaitMask & SERIAL_EV_CTS);
if (ModemStatus & SERIAL_MSR_TERI)
m_EventState |= (m_WaitMask & SERIAL_EV_RING);
if (m_TransmitHold)
{
m_TransmitHold = m_TransmitHoldMask & ~ModemStatus;
if (m_TransmitHold == 0)
goto Transmit;
}
break;
}
default:
ASSERT(FALSE);
} // end switch on interrupt type
}
// Request a DPC if there is an event was found and there is a waiter
if (m_EventState && m_Waiter.m_Irp)
{
m_EventDpc.Request((PVOID)m_Waiter.m_Irp, (PVOID)m_EventState);
m_EventState = 0;
m_Waiter.m_Irp = 0;
}
return InterruptServiced;
}
/////////////////////////////////////////////////////////////////////////////
// Reset
//
// Sets Uart to known state. This can be a synch crit routine.
//
BOOLEAN Uart::Reset(PVOID Context)
{
DisableInterrupts();
// Reset the FIFO
m_PortFifoControl = UCHAR(SERIAL_FCR_RESET_ENABLE);
// Reset line control
m_PortLineControl = m_LineControl;
// Reset modem lines
m_PortModemControl = UCHAR(m_ModemControl & ~SERIAL_MCR_OUT2);
// Reset baud rate
SetBaudRate();
// Empty any pending characters
for (int i=0; (i < 5) && ! (UCHAR(m_PortLineStatus) & 0xf) ; i++)
UCHAR dummy = m_PortRxData;
return TRUE;
}
/////////////////////////////////////////////////////////////////////////////
// EnableInterrupts
//
// The context is a mask of SERIAL_IER_xxx constants
//
BOOLEAN Uart::EnableInterrupts(PVOID Context)
{
ASSERT ( ((ULONG)Context & 0xfffffff0) == 0 );
m_PortIntrEnable = UCHAR(Context);
UCHAR ModemCtrl = m_PortModemControl;
ModemCtrl |= SERIAL_MCR_OUT2;
m_PortModemControl = ModemCtrl;
return TRUE;
}
/////////////////////////////////////////////////////////////////////////////
// DisableInterrupts
//
BOOLEAN Uart::DisableInterrupts(PVOID Context)
{
UCHAR ModemCtrl = m_PortModemControl;
ModemCtrl &= ~SERIAL_MCR_OUT2;
m_PortModemControl = ModemCtrl;
return TRUE;
}
/////////////////////////////////////////////////////////////////////////////
// SetLineControl
//
BOOLEAN Uart::SetLineControl(PVOID Context)
{
m_PortLineControl = m_LineControl;
return TRUE;
}
/////////////////////////////////////////////////////////////////////////////
// SetTransmitHold
//
BOOLEAN Uart::SetTransmitHold(PVOID Context)
{
UCHAR ModemStatus = m_PortModemStatus;
m_TransmitHold = m_TransmitHoldMask & ~ModemStatus;
return TRUE;
}
/////////////////////////////////////////////////////////////////////////////
// SetTransmitHoldMask
//
VOID Uart::SetTransmitHoldMask(void)
{
m_TransmitHoldMask = 0;
if (m_Handflow.ControlHandShake & SERIAL_CTS_HANDSHAKE)
m_TransmitHoldMask |= SERIAL_MSR_CTS;
if (m_Handflow.ControlHandShake & SERIAL_DSR_HANDSHAKE)
m_TransmitHoldMask |= SERIAL_MSR_DSR;
if (m_Handflow.ControlHandShake & SERIAL_DCD_HANDSHAKE)
m_TransmitHoldMask |= SERIAL_MSR_DCD;
}
/////////////////////////////////////////////////////////////////////////////
// SetBaudRate
//
// Sets the baud rate based on member m_BaudRate
//
BOOLEAN Uart::SetBaudRate(PVOID Context)
{
USHORT Divisor;
switch (m_BaudRate.BaudRate)
{
case 300: Divisor = 384; break;
case 1200: Divisor = 96; break;
case 2400: Divisor = 48; break;
case 4800: Divisor = 24; break;
case 9600: Divisor = 12; break;
case 14400: Divisor = 8; break;
case 19200: Divisor = 6; break;
case 28800: Divisor = 4; break;
case 57600: Divisor = 2; break;
case 115200: Divisor = 1; break;
default:
return FALSE;
}
m_PortLineControl = UCHAR(m_LineControl | SERIAL_LCR_DLAB);
m_PortDivisorLatchLsb = UCHAR(Divisor & 0xff);
m_PortDivisorLatchMsb = UCHAR(Divisor >> 8);
m_PortLineControl = m_LineControl;
return TRUE;
}
/////////////////////////////////////////////////////////////////////////////
// SetDTR
//
BOOLEAN Uart::SetDTR(PVOID Context)
{
m_ModemControl |= SERIAL_MCR_DTR;
m_PortModemControl = m_ModemControl;
return TRUE;
}
/////////////////////////////////////////////////////////////////////////////
// ClearDTR
//
BOOLEAN Uart::ClearDTR(PVOID Context)
{
m_ModemControl &= ~SERIAL_MCR_DTR;
m_PortModemControl = m_ModemControl;
return TRUE;
}
/////////////////////////////////////////////////////////////////////////////
// SetRTS
//
BOOLEAN Uart::SetRTS(PVOID Context)
{
m_ModemControl |= SERIAL_MCR_RTS;
m_PortModemControl = m_ModemControl;
return TRUE;
}
/////////////////////////////////////////////////////////////////////////////
// ClearRTS
//
BOOLEAN Uart::ClearRTS(PVOID Context)
{
m_ModemControl &= ~SERIAL_MCR_RTS;
m_PortModemControl = m_ModemControl;
return TRUE;
}
#if NTVERSION > 351
/////////////////////////////////////////////////////////////////////////////
// ClearStatistics
//
// Initialize all statistics to zero
//
BOOLEAN Uart::ClearStatistics(PVOID Context)
{
m_Statistics.ReceivedCount = 0;
m_Statistics.TransmittedCount = 0;
m_Statistics.FrameErrorCount = 0;
m_Statistics.SerialOverrunErrorCount = 0;
m_Statistics.BufferOverrunErrorCount = 0;
m_Statistics.ParityErrorCount = 0;
return TRUE;
}
#endif
#if NTVERSION > 351
/////////////////////////////////////////////////////////////////////////////
// GetStatistics
//
// Retrieve statistics
//
BOOLEAN Uart::GetStatistics(PVOID Context)
{
*(SERIALPERF_STATS*)Context = m_Statistics;
return TRUE;
}
#endif
/////////////////////////////////////////////////////////////////////////////
// GetModemStatus
//
BOOLEAN Uart::GetModemStatus(PVOID Context)
{
*(UCHAR*)Context = m_PortModemStatus;
return TRUE;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -