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

📄 pci9054device.cpp

📁 Windows 2000/XP WDM設備驅動程式開發 使用 Numega 公司出版的軟體快速建置驅動程式
💻 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 <vdw.h>
#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;i<dwTotalSize;i++) m_IoPortRange1.outb(0x4,*pBuffer++);

	I.Information() = dwBytesSent;
	I.Status() = status;

	PnpNextIrp(I);
}

NTSTATUS PCI9054Device::Write(KIrp I) 
{
	if (I.WriteSize() == 0)
	{
		I.Information() = 0;
		return I.PnpComplete(this, STATUS_SUCCESS);
	}

	return QueueIrp(I, LinkTo(CancelQueuedIrp));
}

VOID PCI9054Device::DpcFor_Irq(PVOID Arg1, PVOID Arg2)
{
	m_CurrentTransfer->Continue(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);
}

⌨️ 快捷键说明

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