WINDOWS下PCI接口卡WDM驱动程序的DMA编程技术2

form http://blog.sina.com.cn/s/blog_4c1ec695010009e8.html

        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) )

{ delete m_CurrentTransfer;

 m_CurrentTransfer = NULL;

 I.Information() = 0;

 I.Status() = status; PnpNextIrp(I); } }

(7)在SerialWrite()函数中增加有关对硬件操作的内容

 void PCI9054Device::SerialWrite(KIrp I)

 { NTSTATUS status = STATUS_SUCCESS;

 ULONG i; KMemory Mem(I.Mdl());

 PUCHAR pBuffer = (PUCHAR) Mem.MapToSystemSpace();

 ULONG dwTotalSize = I.WriteSize(CURRENT);

 ULONG dwBytesSent = 0;

 m_IoPortRange1.outb(0,0); //清空FIFO

 for (i=0;i<dwTotalSize;i++) m_IoPortRange1.outb(0x4,*pBuffer++);//写数据

 I.Information() = dwBytesSent;

 I.Status() = status; PnpNextIrp(I); }

 (8)修改下面几个函数为

 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) //判断是否为DMA通道0的传输结束中断

 { return FALSE;//不是,返回FALSE }

 m_IoPortRange0.outd(DMAMODE0,0x20800);//先禁止中断

 m_IoPortRange0.outb(DMACSR0,0x10);//再清除中断

return TRUE; }

(9)增加下面新的成员函数

 VOID PCI9054Device::StartDMA(ULONG PAddress,ULONG NBytes)

{ m_IoPortRange0.outd(DMAMODE0,0x20C00);

 m_IoPortRange0.outd(DMAPADR0,PAddress); //DMA通道0 的PCI地址

 m_IoPortRange0.outd(DMALADR0,0x4); //自己设计的FIFO地址

m_IoPortRange0.outd(DMASIZ0,NBytes); //DMA通道0传输数据长度

 m_IoPortRange0.outd(DMADPR0,0x8); //从设备到主机

 m_IoPortRange0.outb(DMACSR0,0x3); //使通道0可用,并启动

 }

 VOID PCI9054Device::OnDmaReady(KDmaTransfer* pXfer, KIrp I)

 {

if (pXfer->BytesRemaining() == 0) { //DMA结束时

 pXfer->Terminate();

 I.Information() = I.ReadSize(CURRENT);

 I.Status() = STATUS_SUCCESS;

 PnpNextIrp(I);

 m_CurrentTransfer = NULL; delete pXfer;

 return;

 }

 PTRANSFER_DESCRIPTOR ptd; //DMA启动时

 while (pXfer->SequenceTransferDescriptors(&ptd))

 { }

 if ((ULONG) pXfer->BytesRemaining() == I.ReadSize()) StartDMA(ptd->td_PhysAddr.LowPart,ptd->td_Length); }

说明:本实例是在Windows 2000环境下,利用Windows 2000 DDK、DriverWorks和VC++ 6.0进行开发的,并经过Windows 2000/XP下的测试。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值