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

📄 uart.cpp

📁 串口驱动程序
💻 CPP
📖 第 1 页 / 共 2 页
字号:
// uart.cpp - implementation of class Uart						  
//=============================================================================
//
// Compuware Corporation
// NuMega Lab
// 9 Townsend West
// Nashua, NH 03060  USA
//
// Copyright (c) 1998 Compuware Corporation. All Rights Reserved.
// Unpublished - rights reserved under the Copyright laws of the
// United States.
//
//=============================================================================

#include <vdw.h>
#include "uart.h"
#include "glbtrace.h"

/////////////////////////////////////////////////////////////////////////////
// Class Uart is derived from SerialDevice. It implements a 
// 8250 style UART.
//
// Class SerialDevice does most of the IRP handling and IOCTL 
// interfacing. Most of this class is the low level hardware
// interaction.
//
// Because the driver shares interrupts, class Uart makes use of
// class SharedInterrupt. Each Uart object points to a SharedInterrupt
// object. For a given shared interrupt, there may be multiple
// Uart objects that reference it. See shareint.h for more
// information.
//
// Data members m_PortXXXX are instances of KIoRegister set up by
// the constructor. Each instance corresponds to one of the seven
// registers on the UART. The assignment operator of this class is 
// overloaded to allow writing the port with an assignment statement. 
// Similarly, the overloaded cast to UCHAR operator reads these ports.

/////////////////////////////////////////////////////////////////////////////
// Begin INIT code section
#pragma code_seg("INIT")

/////////////////////////////////////////////////////////////////////////////
// Constructor
//
Uart::Uart(
	ULONG Unit,
	INTERFACE_TYPE BusType,
	ULONG BusNumber,
	ULONGLONG PortBase,
	ULONG PortLength,
	ULONG IrqLevel,
	ULONG IrqVector
	) :

	SerialDevice(
		KUnitizedName(L"Serial", Unit),
		KUnitizedName(L"COM", Unit+1)
		),

	m_ReceiveDataQueue(NULL)
{
	GTRACE((TLEVEL,"Constructor for Uart Unit %d\n", Unit));
	GTRACE((TLEVEL, "Base=%x, Irq=%x\n", (ULONG)PortBase, IrqLevel));

	NTSTATUS status;

	m_Unit = Unit;
	m_Interrupt = NULL;
	m_SupportedEvents = SUPPORTED_EVENTS;
	m_Handflow.ControlHandShake = DEFAULT_HANDSHAKE;
	m_Handflow.FlowReplace = DEFAULT_FLOWREPLACE;
	m_Handflow.XonLimit = m_Handflow.XoffLimit = 0;

// Get a registry path for the device
	KUnitizedName* RegPath = CreateRegistryPath(L"Uart", Unit);
	if ( !RegPath )
	{
		m_ConstructorStatus = STATUS_INSUFFICIENT_RESOURCES;
		return;
	}

	GTRACE((TLEVEL, "Registry path is %S\n", (PCWSTR)*RegPath));

// Try to claim the resources. We need a port range and an interrupt.
	KResourceRequest ResReq(BusType, BusNumber, 0);
	ResReq.AddPort(
		PortBase,
		PortBase,
		PortLength,
		4,
		CmResourceShareDeviceExclusive
		);
	ResReq.AddIrq(
		IrqVector,
		IrqVector,
		0,
		CmResourceShareShared
		);

	m_ConstructorStatus = ResReq.Submit(this, *RegPath);
	delete RegPath;
	if ( !NT_SUCCESS(m_ConstructorStatus) )
	{
		GTRACE((\
			TLEVEL,\
			"Resource assignment failed for unit %d, status=%x\n",\
			Unit,\
			m_ConstructorStatus\
			));
		return;
	}

	GTRACE((TLEVEL, "adding device map for unit %d\n", Unit));

// Add the device map entry
	m_ConstructorStatus = AddDeviceMapEntry(
		KUnitizedName(L"Serial", Unit),
		KUnitizedName(L"COM", Unit+1)
		);
	if ( !NT_SUCCESS(m_ConstructorStatus) )
	{
		GTRACE((\
			TLEVEL,\
			"Failed to create device map for unit %d, status=%x\n",\
			Unit,\
			m_ConstructorStatus\
			));
		return;	  
	}

// Set up the I/O registers
	m_ConstructorStatus = 
		m_IoSpace.Initialize(
			BusType,
			BusNumber,
			PortBase,
			PortLength,
			TRUE
			);

	if ( !NT_SUCCESS(m_ConstructorStatus) )
	{
		GTRACE((\
			TLEVEL,\
			"Failed to initialize I/O space for unit %d, status=%x\n",\
			Unit,\
			m_ConstructorStatus\
			));
		return;
	}

// Set up the KIoRegisters

	m_PortRxData 			= m_IoSpace[0];	// Receive data register
	m_PortTxData 			= m_IoSpace[0];	// Transmit data register
	m_PortIntrEnable 		= m_IoSpace[1];	// Interrupt enable register
	m_PortIntrIdent 		= m_IoSpace[2];	// Interrupt ID register
	m_PortFifoControl 		= m_IoSpace[2];	// Fifo control register
	m_PortLineControl 		= m_IoSpace[3];	// Line control register
	m_PortModemControl 		= m_IoSpace[4];	// Modem contorl register
	m_PortLineStatus 		= m_IoSpace[5];	// Line status register
	m_PortModemStatus 		= m_IoSpace[6]; // Modem status register
	m_PortDivisorLatchLsb 	= m_IoSpace[0]; // Baud rate divisor low
	m_PortDivisorLatchMsb 	= m_IoSpace[1]; // Baud rate divisor high

// Now initialize the defaults
	m_BaudRate.BaudRate = DEFAULT_BAUD_RATE;
	m_LineControl = DEFAULT_LINE_CONTROL;
	m_ModemControl = DEFAULT_MODEM_CONTROL;
	Reset();

// Set up the interrupt
	m_ConstructorStatus = 
		InitializeInterrupt(BusType, BusNumber, IrqLevel, IrqVector);

	if ( !NT_SUCCESS(m_ConstructorStatus) )
		return;

	SetTransmitHoldMask();
	Synch(LinkTo(SetTransmitHold));

// Set up the input queue. This is the queue to buffer input when there is no 
// pending read.

	SetUpInputQueue();

// Set the 80% full mark of the buffer. This is used for event generation.
	m_80pcThreshold = (RECEIVE_BUFFER_SIZE*10)/8;

}

/////////////////////////////////////////////////////////////////////////////
// InitializeInterrupt
//
// This function first checks to see if the interrupt is already
// in the shared list. If so, it adds itself to the list.
// Otherwise, it creates a new shared interrupt object and 
// connects to it.
// 
NTSTATUS Uart::InitializeInterrupt(
	INTERFACE_TYPE BusType,
	ULONG BusNumber,
	ULONG IrqLevel,
	ULONG IrqVector
	)
{
	NTSTATUS status;

// See if this interrupt is already in use by the driver
	m_Interrupt = SharedInterrupt::LookUp(IrqVector);

// if not, create a new interrupt object and connect it
	if (m_Interrupt == NULL)
	{
		KInterrupt* pIntr = new (NonPagedPool) 
			KInterrupt(
				BusType,
				BusNumber,
				IrqLevel,
				IrqVector,
				Latched,
				TRUE,
				FALSE
				);

		if (!pIntr)
			return STATUS_INSUFFICIENT_RESOURCES;

// Now create a new entry in the shared interrupt list
		m_Interrupt = new (NonPagedPool) SharedInterrupt(pIntr);

		if ( !m_Interrupt )
		{
			GTRACE((TLEVEL, "Failed to allocate interrupt\n"));

			delete pIntr;
			return STATUS_INSUFFICIENT_RESOURCES;
		}

// Now connect the interrupt. The context value passed to the ISR will
// be a list of Uart devices that share this interrupt.

		status  = pIntr->Connect(MasterIsr, &m_Interrupt->m_Sharers);
		if ( !NT_SUCCESS(status) )
		{
			GTRACE((\
				TLEVEL,\
				"Failed to connect interrupt, status=%x\n",\
				status\
				));

			m_Interrupt = NULL;
			delete pIntr;

			return status;
		}
	}

// Add this device to the list of devices sharing the interrupt

	m_Interrupt->AddSharer(this);

	return STATUS_SUCCESS;
}

/////////////////////////////////////////////////////////////////////////////
#pragma code_seg()

// End INIT code section
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////

/////////////////////////////////////////////////////////////////////////////
// Destructor
//
Uart::~Uart(void)									
{
	delete m_ReceiveDataQueue;

// Remove this device from the list of devices that are sharing
// the interrupt. If this was the last one, delete the shared
// interrupt object.

	if (m_Interrupt)
	{
		m_Interrupt->RemoveSharer(this);
		if (m_Interrupt->m_Sharers.Count() == 0)
			delete m_Interrupt;
	}

// Release the resources

	KUnitizedName* RegistryPath = CreateRegistryPath(L"Uart", m_Unit);
	if (RegistryPath)
	{
		ReleaseResources(*RegistryPath);
		delete RegistryPath;
	}
}

/////////////////////////////////////////////////////////////////////////////
// SetUpInputQueue
//
// Construct a KInterruptSafeFifo<UCHAR> object for the input queue
//
void Uart::SetUpInputQueue(void)
{
// Set up the input queue. This is the queue to buffer input when there is no 
// pending read.

	m_ReceiveDataQueue = new (NonPagedPool) 
		KInterruptSafeFifo<UCHAR>(RECEIVE_BUFFER_SIZE, *m_Interrupt->m_Interrupt);
	if ( !m_ReceiveDataQueue )
		m_ConstructorStatus = STATUS_INSUFFICIENT_RESOURCES;
}

/////////////////////////////////////////////////////////////////////////////
// Create
//
// Enable interrupts and call the base class.
//
NTSTATUS Uart::Create(KIrp I)
{
	GTRACE((TLEVEL, "Uart::Create\n"));

	if (m_Interrupt->m_ActiveCount)
		return I.Complete(STATUS_DEVICE_BUSY);
	else
	{		
		m_Interrupt->m_ActiveCount++;
		EnableInterrupts((PVOID)SERIAL_IER_ALL);
		return SerialDevice::Create(I);
	}
}

/////////////////////////////////////////////////////////////////////////////
// Close
//
// Disable interrupts and call the base class.
//
NTSTATUS Uart::Close(KIrp I)
{
	GTRACE((TLEVEL, "Uart::Close\n"));

	DisableInterrupts();
	m_Interrupt->m_ActiveCount--;
	return SerialDevice::Close(I);
}

/////////////////////////////////////////////////////////////////////////////
// SynchronizeReceiverAccess
//
// Synchronize access to unsolicited data that is currently pending.
// The base class uses this when manipulating information about the
// state of a read operation. This function synchronizes on the
// interrupt object.
//
BOOLEAN Uart::SynchronizeReceiverAccess(
		PKSYNCHRONIZE_ROUTINE func,
		PVOID context
		)
{
	if ( !m_Interrupt )
		return func(context);

	KInterrupt* pIntr = m_Interrupt->m_Interrupt;

	if ( !pIntr )
		return func(context);

	return pIntr->Synchronize(func, context);
}

/////////////////////////////////////////////////////////////////////////////
// SynchronizeTransmitterAccess
//
// Synchronize access to data currently being transmitted. The base class
// uses this function to call its members that manipulate the state of a 
// write operation. This function synchronizes on the interrupt object.
//
BOOLEAN Uart::SynchronizeTransmitterAccess(
		PKSYNCHRONIZE_ROUTINE func,
		PVOID context
		)
{
	if ( !m_Interrupt )
		return func(context);

	KInterrupt* pIntr = m_Interrupt->m_Interrupt;

	if ( !pIntr )
		return func(context);

	return pIntr->Synchronize(func, context);
}

/////////////////////////////////////////////////////////////////////////////
// SynchronizeEventAccess
//
// Synchronize access to event mask
//
BOOLEAN Uart::SynchronizeEventAccess(
		PKSYNCHRONIZE_ROUTINE func,
		PVOID context
		)
{
	if ( !m_Interrupt )
		return func(context);

	KInterrupt* pIntr = m_Interrupt->m_Interrupt;

	if ( !pIntr )
		return func(context);

	return pIntr->Synchronize(func, context);
}

/////////////////////////////////////////////////////////////////////////////
// CopyReceivedData
//
// Copy from the received data queue. This may only be called
// via SynchronizeReceiverAccess
//
ULONG Uart::CopyReceivedData(PUCHAR buffer, ULONG count)
{
	return m_ReceiveDataQueue->Read(buffer, count, TRUE);
}

/////////////////////////////////////////////////////////////////////////////
// StartWrite
//
// Initiate a write operation
//
VOID Uart::StartWrite(PUCHAR buffer, ULONG count)
{
	SerialDevice::StartWrite(buffer, count);
	ASSERT (buffer != 0);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -