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;i 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); }