⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 uart.cpp

📁 串口驱动程序
💻 CPP
📖 第 1 页 / 共 2 页
字号:
	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 + -