3 PCI接口卡下DMA驱动程序编程实例 下面结合在科研工作中的一个开发实例介绍WMD的编程。本实例中的PCI的接口卡,采用PLX公司的PCI9054芯片,局部总线接口模式为C模式。PCI9054芯片的局部总线信号线和一个FPGA芯片相连。设备的访问资源请求三个:前两个固定用于PCI9054芯片的操作寄存器,第三个为I/O映射空间,用于设备访问。在FPGA内部设计了一个FIFO,可以通过I/O指令将数据写入FIFO及清空FIFO。DMA传输采用块模式,从FIFO中读取数据。接口卡原理图如附图所示。 第一步:生成驱动程框架 使用DriverWorks中的DriverWizard创建PCI驱动程序框架,定义驱动程序工程名为PCI9054。注意需要声明所需的资源,如存储器空间和I/O空间,中断和DMA等。 第二步:修改和增加程序框架中的内容。 在VC中打开PCI9054工程,在程序框架基础上进一步进行编程。由于源代码内容太多,这里只给出修改和增加程序内容。 在PCI9054Device.h中改动如下: (1)在类class PCI9054Device : public KpnpDevice中增加下面的宏的成员函数 DEVMEMBER_DMAREADY(PCI9054Device, OnDmaReady) DEVMEMBER_CANCELIRP(PCI9054Device, CancelQueuedIrp) VOID StartDMA(ULONG PAddress,ULONG NBytes); VOID OnDmaReady(KDmaTransfer* pXfer, KIrp I); // COMMENT_ONLY (2)类class PCI9054Device : public KpnpDevice中增加所使用类的声名 KDmaTransfer* m_CurrentTransfer; KCommonDmaBuffer m_Buffer; PCI9054Device.cpp文件中改动如下: (1)下面是PCI9054中有关中断和DMA操作寄存器的偏移地址 #define INTCSR 0x68 #define DMAMODE0 0x80 #define DMAPADR0 0x84 #define DMALADR0 0x88 #define DMASIZ0 0x8C #define DMADPR0 0x90 #define DMACSR0 0xA8 (2)在函数VOID PCI9054Device::Invalidate()中增加下面函数调用 m_Buffer.Invalidate(); (3)在函数NTSTATUS PCI9054Device::OnStartDevice(KIrp I)中,在m_Dma.Initialize(&dd, m_Lower.TopOfStack())函数之后增加函数调用语句 m_Buffer.Initialize(&m_Dma,2048); 另外,在该函数最后境加下面函数调用语句 m_IoPortRange0.outd(INTCSR,0x40100); //允许PCI中断和DMA通道0中断 (4)在函数NTSTATUS PCI9054Device::OnStopDevice(KIrp I)中增加下面函数调用 m_IoPortRange0.outd(INTCSR,0); //禁止PCI中断和DMA通道0中断 m_Irq.Disconnect(); (5)NTSTATUS PCI9054Device::OnRemoveDevice(KIrp I) 中增加下面函数调用 m_IoPortRange0.outd(INTCSR,0); m_Irq.Disconnect(); (6)在SerialRead()函数中增加有关创建KdmaTransfer类实例内容 void PCI9054Device::SerialRead(KIrp I) { NTSTATUS status = STATUS_SUCCESS; m_CurrentTransfer = new(NonPagedPool) KDmaTransfer(this, &m_Dma); if ( m_CurrentTransfer == NULL ) { status = STATUS_INSUFFICIENT_RESOURCES; I.Information() = 0; I.Status() = status; PnpNextIrp(I); } status = m_CurrentTransfer->Initiate(this, &m_Dma, I.Mdl(), (I.MajorFunction() == IRP_MJ_READ) ? FromDeviceToMemory : FromMemoryToDevice, LinkTo(OnDmaReady), &m_Buffer); if ( ! NT_SUCCESS(status) ) |