www.pudn.com > PCI9054.rar > PCI9054Device.cpp


// PCI9054Device.cpp 
// Implementation of PCI9054Device device class 
// 
// Generated by DriverWizard version DriverStudio 2.6.0 (Build 336) 
// Requires Compuware's DriverWorks classes 
// 
 
#pragma warning(disable:4065) // Allow switch statement with no cases 
		   
#include  
#include "..\PCI9054Deviceinterface.h" 
 
#include "PCI9054.h" 
#include "PCI9054Device.h" 
 
#pragma hdrstop("PCI9054.pch") 
 
#define INTCSR    0x68 
#define DMAMODE0  0x80 
#define DMAPADR0  0x84 
#define DMALADR0  0x88 
#define DMASIZ0   0x8C 
#define DMADPR0   0x90 
#define DMACSR0   0xA8 
 
GUID PCI9054Device_Guid = PCI9054Device_CLASS_GUID; 
KTrace t("PCI9054"); 
 
PCI9054Device::PCI9054Device(PDEVICE_OBJECT Pdo, ULONG Unit) : 
	KPnpDevice(Pdo, &PCI9054Device_Guid) 
{ 
 
	// Check constructor status 
    if ( ! NT_SUCCESS(m_ConstructorStatus) ) 
	{ 
	    return; 
	} 
 
	// Remember our unit number 
	m_Unit = Unit; 
 
	// Initialize the lower device 
	m_Lower.Initialize(this, Pdo); 
 
    // Inform the base class of the lower edge device object 
	SetLowerDevice(&m_Lower); 
 
	// Initialize the PnP Policy settings to the "standard" policy 
	SetPnpPolicy(); 
 
} 
 
PCI9054Device::~PCI9054Device() 
{ 
} 
 
NTSTATUS PCI9054Device::DefaultPnp(KIrp I)  
{ 
	I.ForceReuseOfCurrentStackLocationInCalldown(); 
	return m_Lower.PnpCall(this, I); 
} 
 
NTSTATUS PCI9054Device::DefaultPower(KIrp I)  
{ 
	I.IndicatePowerIrpProcessed(); 
	I.CopyParametersDown(); 
	return m_Lower.PnpPowerCall(this, I); 
} 
 
NTSTATUS PCI9054Device::SystemControl(KIrp I)  
{ 
	I.ForceReuseOfCurrentStackLocationInCalldown(); 
	return m_Lower.PnpCall(this, I); 
} 
 
VOID PCI9054Device::Invalidate() 
{ 
	// It is not necessary to release the system resource for the DMA adapter 
	// object, since NT provides no mechanism for this. 
	m_Buffer.Invalidate(); 
 
	// For each memory mapped region, release the underlying system resoruce. 
	m_MemoryRange0.Invalidate(); 
 
	// For each I/O port mapped region, release the underlying system resource. 
	m_IoPortRange0.Invalidate(); 
	m_IoPortRange1.Invalidate(); 
 
	// For the interrupt, release the underlying system resource. 
	m_Irq.Invalidate(); 
} 
 
NTSTATUS PCI9054Device::OnStartDevice(KIrp I) 
{ 
	NTSTATUS status = STATUS_SUCCESS; 
 
	I.Information() = 0; 
 
	// The default Pnp policy has already cleared the IRP with the lower device 
	// Initialize the physical device object. 
 
	// Get the list of raw resources from the IRP 
	PCM_RESOURCE_LIST pResListRaw = I.AllocatedResources(); 
	// Get the list of translated resources from the IRP 
	PCM_RESOURCE_LIST pResListTranslated = I.TranslatedResources(); 
 
// TODO:	Check to ensure that the following parameters are correct for your hardware 
// 
#define MAX_DMA_LENGTH	0x100000	// 0x100000 is 1 MB 
 
	// Initialize the device descriptor for the DMA object using the assigned resource 
	DEVICE_DESCRIPTION dd; 
	RtlZeroMemory(&dd, sizeof(dd)); 
	dd.Version = DEVICE_DESCRIPTION_VERSION; 
	dd.Master = TRUE; 
	dd.ScatterGather = FALSE; 
	dd.DemandMode = TRUE; 
	dd.AutoInitialize = FALSE; 
	dd.Dma32BitAddresses = TRUE; 
	dd.IgnoreCount = FALSE; 
	dd.DmaChannel = 0; 
	dd.InterfaceType = PCIBus; 
	dd.DmaWidth = Width32Bits;	// PCI default width 
	dd.DmaSpeed = Compatible; 
	dd.MaximumLength = MAX_DMA_LENGTH; 
 
	// Initialize the DMA adapter object 
	m_Dma.Initialize(&dd, m_Lower.TopOfStack()); 
	m_Buffer.Initialize(&m_Dma,2048); 
 
	// Create an instance of KPciConfiguration so we can map Base Address 
	// Register indicies to ordinals for memory or I/O port ranges. 
	KPciConfiguration PciConfig(m_Lower.TopOfStack()); 
 
	// For each memory mapped region, initialize the memory mapped range 
	// using the resources provided by NT. Once initialized, each memory 
	// range's base virtual address in system space can be obtained by calling 
	// member Base(). Each memory range's physical address in CPU space can 
	// obtained by calling CpuPhysicalAddress(). To access the memory mapped 
	// range use member functions such as inb/outb, or the array element operator.  
	status = m_MemoryRange0.Initialize( 
		pResListTranslated, 
		pResListRaw, 
		PciConfig.BaseAddressIndexToOrdinal(0) 
		); 
	if (!NT_SUCCESS(status)) 
	{ 
		Invalidate(); 
		return status;		 
	} 
 
	// For each I/O port mapped region, initialize the I/O port range using 
	// the resources provided by NT. Once initialized, use member functions such as 
	// inb/outb, or the array element operator to access the ports range. 
	status = m_IoPortRange0.Initialize( 
		pResListTranslated, 
		pResListRaw, 
		PciConfig.BaseAddressIndexToOrdinal(1) 
		); 
	if (!NT_SUCCESS(status)) 
	{ 
		Invalidate(); 
		return status;		 
	} 
 
	status = m_IoPortRange1.Initialize( 
		pResListTranslated, 
		pResListRaw, 
		1 
		); 
	if (!NT_SUCCESS(status)) 
	{ 
		Invalidate(); 
		return status;		 
	} 
 
	// Initialize and connect the interrupt 
	status = m_Irq.InitializeAndConnect( 
		pResListTranslated,  
		LinkTo(Isr_Irq),  
		this 
		); 
	if (!NT_SUCCESS(status)) 
	{ 
		Invalidate(); 
		return status;		 
	} 
 
	// Setup the DPC to be used for interrupt processing 
	m_DpcFor_Irq.Setup(LinkTo(DpcFor_Irq), this); 
	// TODO:	Add device-specific code to start your device. 
	m_IoPortRange0.outd(INTCSR,0x40100);//允许PCI中断和DMA通道0中断 
 
	return status; 
} 
 
NTSTATUS PCI9054Device::OnStopDevice(KIrp I) 
{ 
	m_IoPortRange0.outd(INTCSR,0);//禁止PCI中断和DMA通道0中断 
	m_Irq.Disconnect(); 
	Invalidate(); 
 
	return STATUS_SUCCESS; 
} 
 
NTSTATUS PCI9054Device::OnRemoveDevice(KIrp I) 
{ 
	m_IoPortRange0.outd(INTCSR,0); 
	m_Irq.Disconnect(); 
	Invalidate(); 
 
	return STATUS_SUCCESS; 
} 
 
VOID PCI9054Device::CancelQueuedIrp(KIrp I) 
{ 
	KDeviceQueue dq(DeviceQueue()); 
 
	// Test if the IRP is the current IRP. 
	if ( (PIRP)I == CurrentIrp() ) 
	{ 
		CurrentIrp() = NULL; 
		CancelSpinLock::Release(I.CancelIrql()); 
	    I.Information() = 0; 
		I.Status() = STATUS_CANCELLED; 
		PnpNextIrp(I); 
	} 
	// See if the IRP can be removed from the device queue. 
	else if (dq.RemoveSpecificEntry(I)) 
	{ 
		CancelSpinLock::Release(I.CancelIrql()); 
	    I.Information() = 0; 
		I.PnpComplete(this, STATUS_CANCELLED); 
	} 
	else 
	{ 
		CancelSpinLock::Release(I.CancelIrql()); 
	} 
} 
 
VOID PCI9054Device::StartIo(KIrp I) 
{ 
	if ( !I.TestAndSetCancelRoutine( 
		LinkTo(CancelQueuedIrp), 
		NULL, 
		CurrentIrp()) ) 
	{ 
		return; 
	} 
 
	switch (I.MajorFunction()) 
	{ 
		case IRP_MJ_READ: 
			SerialRead(I); 
			break; 
		case IRP_MJ_WRITE: 
			SerialWrite(I); 
			break; 
		default: 
			ASSERT(FALSE); 
			PnpNextIrp(I); 
			break; 
	} 
} 
 
NTSTATUS PCI9054Device::Create(KIrp I) 
{ 
	NTSTATUS status; 
 
	status = I.PnpComplete(this, STATUS_SUCCESS, IO_NO_INCREMENT); 
 
	return status; 
} 
 
NTSTATUS PCI9054Device::Close(KIrp I) 
{ 
	NTSTATUS status; 
 
	status = I.PnpComplete(this, STATUS_SUCCESS, IO_NO_INCREMENT); 
 
    return status; 
} 
 
NTSTATUS PCI9054Device::CleanUp(KIrp I) 
{ 
    KDeviceQueue dq(DeviceQueue()); 
	dq.PnpCleanUp(this, I.FileObject()); 
	return I.PnpComplete(this, STATUS_SUCCESS); 
} 
 
void PCI9054Device::SerialRead(KIrp I) 
{ 
	NTSTATUS status		= STATUS_SUCCESS; 
 
	t << "Entering SerialRead\n"; 
 
	// Create a new DMA transfer object for this IRP 
	m_CurrentTransfer = new(NonPagedPool) KDmaTransfer(this, &m_Dma); 
 
	if ( m_CurrentTransfer == NULL ) 
	{ 
		status = STATUS_INSUFFICIENT_RESOURCES; 
		DbgPrint("unable to allocate transfer object: %x\n", status); 
 
		I.Information() = 0; 
		I.Status() = status; 
		PnpNextIrp(I); 
	} 
 
	//下面采用应用程序的数据缓冲区作为DMA数据区 
	status = m_CurrentTransfer->Initiate( 
		I.Mdl(), 
		(I.MajorFunction() == IRP_MJ_READ) ? FromDeviceToMemory : FromMemoryToDevice, 
		LinkTo(OnDmaReady) 
		); 
/*	下面采用公用缓冲区作为DMA数据区 
	status = m_CurrentTransfer->Initiate( 
		this, 
		&m_Dma, 
		I.Mdl(), 
		(I.MajorFunction() == IRP_MJ_READ) ? FromDeviceToMemory : FromMemoryToDevice, 
		LinkTo(OnDmaReady), 
		&m_Buffer 
		); 
*/ 
	// If the transfer cannot be initiated, complete it with an error status. 
	if ( ! NT_SUCCESS(status) ) 
	{ 
		DbgPrint("unable to initiate transfer: %x\n", status); 
 
		delete m_CurrentTransfer; 
		m_CurrentTransfer = NULL; 
 
		I.Information() = 0; 
		I.Status() = status; 
		PnpNextIrp(I); 
	} 
} 
 
NTSTATUS PCI9054Device::Read(KIrp I)  
{ 
	if (I.ReadSize() == 0) 
	{ 
		I.Information() = 0; 
		return I.PnpComplete(this, STATUS_SUCCESS); 
	} 
 
	return QueueIrp(I, LinkTo(CancelQueuedIrp)); 
} 
 
void PCI9054Device::SerialWrite(KIrp I) 
{ 
	NTSTATUS status		= STATUS_SUCCESS; 
	ULONG i; 
 
	// Declare a memory object 
	KMemory Mem(I.Mdl()); 
	// Use the memory object to create a pointer to the caller's buffer 
	PUCHAR	pBuffer		= (PUCHAR) Mem.MapToSystemSpace(); 
 
	ULONG   dwTotalSize = I.WriteSize(CURRENT); 
	ULONG   dwBytesSent = 0; 
	//清空FIFO 
	m_IoPortRange1.outb(0,0); 
	//用I/O输出命令往FIFO写数据 
	for (i=0;iContinue(UseTransferSize); 
} 
 
BOOLEAN PCI9054Device::Isr_Irq(void) 
{ 
	ULONG status; 
 
	status=	m_IoPortRange0.ind(INTCSR); 
 
	if ((status & 0x200000)==0) 
	{ 
		// Return FALSE to indicate that this device did not cause the interrupt. 
		return FALSE; 
	} 
	m_IoPortRange0.outd(DMAMODE0,0x20800); 
	m_IoPortRange0.outb(DMACSR0,0x10);//Clear Interrupt 
 
	// Request deferred procedure call 
	// The arguments to Request may be any values that you choose 
	if (!m_DpcFor_Irq.Request(NULL, NULL)) 
	{ 
// TODO:	Request is already in the queue 
//			You may want to set flags or perform 
//			other actions in this case 
	} 
 
	// Return TRUE to indicate that our device caused the interrupt 
	return TRUE; 
} 
 
VOID PCI9054Device::StartDMA(ULONG PAddress,ULONG NBytes) 
{ 
	//下面几条语句设置DMA通道0寄存器,启动块传输方式,从FIFO读数据 
	//Channel0 interrupt to the PCI Bus interrupt,Done Interrupt Enable,FIFO 
	m_IoPortRange0.outd(DMAMODE0,0x20C00); 
	//DMA Channel0 PCI Address 
	m_IoPortRange0.outd(DMAPADR0,PAddress); 
	//DMA Channel0 Local Address,自己设计的FIFO地址 
	m_IoPortRange0.outd(DMALADR0,0x8); 
	//DMA Channel0 Transfer Size(Bytes) 
	m_IoPortRange0.outd(DMASIZ0,NBytes); 
	//from the Local Bus to the PCI Bus 
	m_IoPortRange0.outd(DMADPR0,0x8); 
	//Channel0 Enable,Start 
	m_IoPortRange0.outb(DMACSR0,0x3); 
} 
 
VOID PCI9054Device::OnDmaReady(KDmaTransfer* pXfer, KIrp I) 
{ 
	// All KDmaTransfer callbacks must first check to see if there are any bytes 
	// left to transfer. 
	if (pXfer->BytesRemaining() == 0) 
	{ 
		// If there are no bytes left to transfer, the callback must call 
		// Terminate(). Then it completes the IRP with STATUS_SUCCESS. 
		pXfer->Terminate(); 
	 
		I.Information() = I.ReadSize(CURRENT); 
		I.Status() = STATUS_SUCCESS; 
		PnpNextIrp(I); 
 
		m_CurrentTransfer = NULL; 
		delete pXfer; 
		return; 
	} 
 
	// We must get the descriptor for the physical memory location for 
	// the DMA transfer. 
 
	PTRANSFER_DESCRIPTOR ptd; 
 
	while (pXfer->SequenceTransferDescriptors(&ptd)) { 
		// program the h/w using  ppTD 
		t << " Physical address 0x" << ptd->td_PhysAddr.LowPart << ". Length is 0x" 
			<< ptd->td_Length << "." << EOL; 
	} 
 
	// If this is the first time through, then start the DMA going. 
	// We only want to do this ONCE for a given Read transfer.  That 
	// way, our data will be collected smoothly, without interruptions 
	// or dropouts. 
	if ((ULONG) pXfer->BytesRemaining() == I.ReadSize()) 
		StartDMA(ptd->td_PhysAddr.LowPart,ptd->td_Length); 
}