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

转载 2011年03月05日 18:41:00
      本文主要讨论了在Windows环境下开发PCI接口卡DMA应用的WDM编程技术,并给出了一个应用DriverWorks和VC++开发的实例程序代码。关键词:Windows、PCI总线、WDM驱动程序、DMA Abstract: This paper introduced DMA program with WDM for PCI in Windows, And gives some program code for an example with DriverWorks and VC++.
 
    前言
 
    现化微机的扩展槽通常有ISA总线和PCI总线两种接口标准,而PCI总线以其优良的性能和更好的适应性已成为现代高档微机的主流总线。以操作系统观点来看,不论Windows 95/98/2000/NT/XP等都是由一个操作系统核心和多个驱动程序组成,驱动程序总是与系统中的硬件相对应。在Windows环境下共有三种类型的驱动程序,分别是VxD、NT驱动程序和WDM。VxD应用在Windows 9x下,而WDM应用在Windows 98/2000/XP下。WDM是Win32 Driver Model的缩写,是Microsoft公司力推的新型的驱动程序工作模式。在Windows操作系统下开发设备驱动程序需要专门的开发工具,目前应用广泛的工具主要是DriverStudio----NuMega公司提供的一个大的开发工具包,它包含VtoolsD、DriverWorks和SoftICE等开发工具。DriverStudio是一个全面的Windows设备驱动程序开发工具。DriverWorks是DriverStudio的一个组成部分,它能够非常方便地实现针对 NT驱动程序和 WDM 驱动程序的开发。DriverWorks中的类库封装了针对驱动程序的各种通用操作,使用其中的DriverWizard向导功能,可让开发人员直接进行到设备驱动程序的开发过程,从一段关于硬件设备的描述自动产生驱动程序的源代码程序框架,在帮助你缩短整个开发周期的同时开发出高质量的,结构化的驱动程序。
 
    Windows环境下涉及DMA驱动程序编程的内容 DriverWorks提供了三个类:KDmaAdapter、KDmaTransfer和KCommonDmaBuffer类,用于实现DMA操作。KDmaAdapter类用于建立一个DMA适配器,它说明DMA通道的特性。KDmaTransfer类用于DMA传输控制。KCommonDmaBuffer类用于申请系统提供的公用缓冲区。下面简单介绍一下这三个类的主要成员函数:
(1)KDmaAdapter类 Initialize( pDesc,,pPdo)或Initialize( pDesc,pPdo,nMaxScatterGatherPairs)。参数pDesc为设备描述结构指针;pPdo为物理设备对象指针;nMaxScatterGatherPairs为支持分散/聚集的总线主控设备的物理不连续缓冲区个数。
(2)KDmaTransfer类
   ①KDmaTransfer( pDevice,pAdapter,pBuffer)或KDmaTransfer( void )。是该类的构造函数。参数pDevice为相关的设备对象指针;pAdapter为相关的适配器对象指针;pBuffer为相关的公用缓冲区对象指针。

   ②Initiate( Memory,Dir,Callback,pContext,BusMasterKeepAdapter )或 Initiate(pDevice,pAdapter,Memory,Dir,Callback,pBuffer,pContext,BusMasterKeepAdapter)。用于初始化DMA传输。参数Memory为内存对象指针;Dir为传输方向,取值为FromDeviceToMemory或FromMemoryToDevice;Callback为DMA准备就绪回调例程;pContext为传递给回调例程的环境参数地址指针;BusMasterKeepAdapter为是否保持适配器对象,正常情况下,总线主控设备释放适配器对象,但保持映射寄存器。

   ③BytesRemaining( void )。返回当前传输的剩余字节数。

   ④Terminate( void )。终止传输并释放适配器。

   ⑤SequenceTransferDescriptors( ppTD )。获取当前传输段的单个描述符。只能在DMA准备就绪回调例程中调用。返回TRUE,表示取得下一个传输描述符。

   ⑥Continue(XferCountType,Count)。继续传输,在一段传输完成后调用。参数XferCountType为传输长度类型;Count为指定的传输字节数。另外,KDevice类还有一个成员函数DEVMEMBER_DMAREADY( class_name, function_name )与DMA准备就绪回调例程有关。用于声明DMA准备就绪回调例程为KDevice派生类的一个成员函数。参数class_name为定义DMA准备就绪回调例程为其成员函数的类名;function_name:DMA准备就绪回调例程名。

(3)KCommonDmaBuffer类

     ①Initialize( pAdapter,size,CacheEnabled)。初始化一个用缺省构造函数定义的类对象。参数pAdapter为相关的适配器对象指针;size为要求的缓冲区字节长度;CacheEnabled为是否允许CPU内部的高速缓冲区作为公用缓冲区。

     ②VirtualAddress( void )。返回缓冲区的内核模式地址。

     ③LogicalAddress( void )。返回缓冲区的物理地址。

     ④Size( void )。返回缓冲区的字节长度。

     ⑤KMemory Mdl( void )。返回和缓冲区相关的的内存对象。要开发DMA驱动程序,通常还要涉及到硬件访问和中断处理的问题。因此,在DMA驱动程序中应包含有硬件访问编程部分和中断处理编程部分。通常可以使用KIoRange类实现对I/O映射芯片的访问, KMemoryRange类实现对内存映射芯片的访问,KInterrupt类实现硬件中断的处理。

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下的测试。

 

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

摘要:       本文主要讨论了在Windows环境下开发PCI接口卡DMA应用的WDM编程技术,并给出了一个应用DriverWorks和VC++开发的实例程序代码。 关键词:Windo...
  • wolfman125
  • wolfman125
  • 2017年02月24日 09:16
  • 158

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

form http://blog.sina.com.cn/s/blog_4c1ec695010009e8.html     PCI接口卡下DMA驱动程序编程实例     ...
  • wolfman125
  • wolfman125
  • 2017年02月24日 09:18
  • 172

Windows DMA驱动调试

本文记录我调试微软官方提供的一个PCI驱动sample程序的过程。 一、开发环境和资源下载 1,在win10 X64操作系统下,安装VS2015+WDK10。(参考我前面的驱动开发入门篇) 2,使用T...
  • Sagittarius_Warrior
  • Sagittarius_Warrior
  • 2016年04月09日 17:39
  • 1308

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

摘要:       本文主要讨论了在Windows环境下开发PCI接口卡DMA应用的WDM编程技术,并给出了一个应用DriverWorks和VC++开发的实例程序代码。 关键词:Windo...
  • wolfman125
  • wolfman125
  • 2017年02月24日 09:16
  • 158

基于Windriver的驱动开发——驱动基础

转载:基于Windriver的驱动开发——驱动基础
  • LH806732
  • LH806732
  • 2014年04月19日 15:41
  • 1117

DMA驱动程序解析

1、DMA的具体工作过程: (1)外设向DMA发出请求 (2)DMA通过HOLD向CPU发出总线请求 (3)CPU响应释放三总线,并且发应答HLDA (4)DMA向外设发DMA应答 (5)DMA发出地...
  • you_shou
  • you_shou
  • 2016年09月17日 09:11
  • 985

Linux下PCIe驱动以及DMA机制

1. 驱动程序作用: ·        设备驱动程序向应用程序屏蔽了硬件在实现上的细节,使得应用程序可以像操作普通文件一样操作外部设备。Linux操作系统抽象了对硬件的处理,可以使用和操作文件相同的...
  • jeason29
  • jeason29
  • 2015年10月14日 13:42
  • 5177

windows 驱动开发 DDK与WDK WDM的区别

最近尝试去了解WINDOWS下的驱动开发,现在总结一下最近看到的资料。   1.首先,先从基础的东西说起,开发WINDOWS下的驱动程序,需要一个专门的开发包,如:开发JAVA程序,我们可能需要...
  • swanabin
  • swanabin
  • 2014年11月22日 14:54
  • 2111

驱动编程(一),NT - WDM - WDF 驱动概念

NT是驱动模型:NT式驱动程序模型是一种比较老式的驱动程序模型,但适用于现有的Windows系统。NT式驱动模型没有固定的形式,最简单的NT式驱动程序模型这一特点,程序开发者可以编写一个完全不支持硬件...
  • msk10k
  • msk10k
  • 2016年04月26日 16:02
  • 1444

NT式驱动和WDM式驱动程序

1.Windows驱动程序分为两类,一类是不支持即插即用功能的NT式的驱动程序;另一类是支持即插即用功能的WDM式的驱动程序。   2.NT式的驱动程序要导入的头文件时NTDDK.H,而WDM式的...
  • whatday
  • whatday
  • 2015年03月28日 15:27
  • 2737
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:WINDOWS下PCI接口卡WDM驱动程序的DMA编程技术
举报原因:
原因补充:

(最多只允许输入30个字)