基于DDK的PCI设备驱动程序设计

时间:2022-06-19 11:16:40

基于DDK的PCI设备驱动程序设计

摘要:该文以基于PCI9056的某项目设备板卡为例,介绍WDM驱动程序和PCI总线协议,分析利用DDK开发WDM驱动程序的主要例程,实现了识别板卡及测试板卡功能。

关键词:WDM;PCI;DDK;设备驱动程序

中图分类号:TP333文献标识码:A 文章编号:1009-3044(2011)07-1534-03

Designed of PCI Device Driver Based on DDK

ZHAO bin1, TIAN Ze1, CHEN Jia2

(1.Xi'an Shiyou University, Xi'an 710065, China; 2. Xidian University, Xi'an 710072, China)

Abstract: This paper takes a PCI device as an example, describes WDM drivers and PCI bus protocols. And introducing a method of WDM driver design base on DDK(Driver Development Kit), realizing identification and testing function.

Key words: WDM; PCI; DDK; device driver

因为某项目的需求,需要在PC机上调试一PCI设备,在Windows系统下实现主机通过PLX9056桥芯片对PCI设备的访问,但前提条件是当操作系统装载驱动程序正确的情况下。如果驱动程序装载不正常,主机首先就不能够识别PCI设备;如果驱动程序运行不正常,用户就不能正确的访问PCI设备。因此,PCI驱动程序是实现主机识别板卡以及正确访问设备的关键。

目前常用的驱动开发工具有:微软提供的DDK(Device Driver Kits),以及第三方厂商提供的DriverStudio和WinDriver,现在大多数驱动开发人员都是使用DriverStudio和WinDriver,其中DriverStudio将DDK函数按照逻辑功能组织,把很多常用功能封装成类,建立了一个基于C++语言的面向对象的编程环境,大大降低了开发难度和开发周期;WinDriver将一些基本的操作如存储读写、I/O端口读写、中断服务、DMA操作等进行了封装,开发者只需编写一个外壳来调用这个驱动程序,就可以完成对硬件设备的访问。前两种开发工具使用简单,开发速度快,但开发的驱动程序执行效率受到限制。DDK虽然编程难度较大,对编程人员的要求也较高,但是功能强大,编程灵活,使用范围广,可应用于各类硬件驱动程序的编写,而且有助开发人员深刻理解驱动程序和WDM(Windows Driver Mode),所以DDK是更理想的开发工具。

1 WDM式设备驱动的概述

1.1 WDM概念

设备驱动程序需与操作系统最底层进行交互,不同的操作系统底层结构对应于不同的设备驱动程序模型,也影响设备驱动程序的兼容性。在Windows 2000以后,微软公司加入了新的驱动程序模型,这就是WDM。

1.2 WDM式驱动的基本结构

WDM模型中,两个驱动设备对象分别是物理设备对象(Physical Device Object,简称PDO)和功能设备对象(Function Device Object,简称FDO)。其关系是“附加”与“被附加”的关系。

当某个设备插入PC机时,PDO会自动创建。确切的说,PDO是由总线驱动创建的。PDO需要配合FDO一起使用,系统会提示检测到新设备,要求安装驱动程序。需要安装的驱动程序就是WDM程序,此驱动程序就是创建FDO并附加到PDO之上。当一个FDO附加在PDO上的时候,PDO设备对象的子域AttachedDevice会记录FDO的位置。PDO靠近物理设备,被称作底层驱动,而FDO接近发出I/O请求,被称作高层驱动。并且从WDM驱动的设计思路可以看出,WDM驱动是支持即插即用的。

在FDO和PDO之间还会存在过滤驱动,在FDO上面的过滤驱动被称作上层过滤驱动,在FDO下层的驱动,被称作下层过滤驱动。在WDM模型中,过滤驱动不是必须存在的,PDO和FDO是必须的。

2 PCI总线协议

PCI总线协议是PC上最基本的总线,一般显卡、网卡都设计成PCI总线设备。其他总线都是挂在PCI总线上的。例如,ISA总线是通过PCI-ISA桥设备挂在PCI总线上,而USB总线是通过USB HOST设备挂在PCI总线上的。

2.1 PCI总线简介

PCI(Pheripheral Component Interconnect)总线是当前最流行的总线之一,是由Intel公司首先推出的一种局部总线。它定义了32位数据地址总线,并且可以扩展为64位,其支持突发读写操作,也同时可以支持多组设备。

在当前的PC体系结构内,几乎所有外部设备采用的各种各样的接口总线,均是通过桥接电路挂接在PCI系统内的。在这种PCI系统中,Host/PCI桥称为北桥,连接主处理器总线到基础PCI局部总线。PCI-ISA桥称为南桥,连接基础PCI总线到ISA总线。其中南桥通常还含有中断控制器、IDE控制器、USB控制器和DMA控制器等设备。

2.2 PCI配置空间简介

PCI有三个相互独立的物理地址空间:设备存储器地址空间、I/O地址空间和配置空间。配置空间是PCI所特有的一个物理空间。由于PCI支持设备即插即用,所以PCI设备不占用固定的内存地址空间或I/O地址空间,而是可以由操作系统决定其映射的基址。

系统加电时,BIOS检测PCI总线,确定所有连接在PCI总线上的设备以及它们的配置要求,并进行系统配置,实现真正的即插即用。

3 开发实例

Windows从总体上分为内核模式(Kernel Mode)和用户模式(User Mode)。驱动程序运行在内核模式下,拥有操作系统的最高权限。编写驱动程序主要是为了操作硬件设备,这些硬件设备的操作主要包括访问物理映射内存、设备端口等。而应用程序是运行在用户模式下,应用程序无法直接与硬件设备通信,必须借助于驱动程序。

所开发的PCI设备应用软件和驱动软件驻留在PC机中,其中应用软件与特定的子系统有关,应用软件通过调用PCI驱动软件实现子系统功能要求。应用软件和驱动软件在Windows下的关系如图2所示。

3.1 PCI设备驱动程序

PCI模块驱动软件是实现PCI板卡与宿主机(PC机)应用软件间的接口控制与数据传递的专用软件,它可提供PCI板卡与PC机各类消息数据的读、写支持,以及对PCI板卡内部程序的调度。

PCI驱动程序开发软件是DDK,用户模式下所有对驱动程序的I/O请求,全部由操作系统转化为IRP(I/O Request Package,即输入输出请求包)数据结构,不同IRP数据会被“派遣”到不同的派遣函数(Dispatch Function)。

驱动软件的入口程序是DriverEntry,在DriverEntry中包含了AddDevice函数的设置。PCI设备是属于被动加载的设备,操作系统必须加载PDO后调用AddDevice例程,而AddDevice例程就是负责创建FDO并附加到PDO之上。

其次,DriverEntry必须加入IRP_MJ_PNP的派遣回调函数。IRP_MJ_PNP就是即插即用IRP,是由即插即用管理器发送给PCI驱动程序的。

当启动PCI设备的时候,设备管理器将IRP_MN_START_DEVICE发送给PCI驱动。在处理完此IRP后,驱动程序会将处理结果存储在IRP的设备堆栈中。程序可从当前堆栈就可以获取PDO从PCI配置空间中的有用信息,如中断号、设备物理内存及IO端口信息等。因为实际应用中,不能提前知道这些描述符在数组中出现的位置,因此在实现中必须用循环先把资源值提取到一组局部变量中,然后在处理这些资源信息。

对于枚举PCI设备资源,主要分为四类,分别是设备端口、设备物理内存、DMA、中断等。在实现中,程序里枚举了I/O端口资源,物理内存资源和中断资源。其中,I/O端口资源包括I/O端口地址、I/O端口地址长度,物理内存资源包括基地址0和基地址2,中断资源包括中断请求级、中断向量、CPU亲缘关系、中断模式以及共享中断等。

在设备即将停止的时候,即插即用管理器将发送IRP_MN_REMOVE_DEVICE给PCI驱动,这个IRP标志着设备即将关闭,PCI驱动在此时会做一些资源回收工作。

因此开发PCI设备驱动最重要的一步就是将IRP_MN_START_DEVICE中获取的设备资源记录下来,以便以后使用。

3.2 PCI设备应用程序与驱动程序的通讯

PCI设备应用程序的开发软件是VC6.0,在应用程序中寻找设备是通过设备接口和设备号决定的,其实现过程主要是通过SetupDiXX系列函数得到设备接口。

在实际应用中,PLX9056的本地配置寄存器映射在PLX9056配置寄存器中基地址寄存器0,待测试设备的内存空间由硬件设计人员映射在PLX9056的基地址寄存器2。因此,在应用程序中需定义供用户使用的通过基地址0和基地址2的读写接口函数。例如:

基本的读函数有:

ULONG ReadFromBase0(HANDLE handle,ULONG Offset,

UCHAR *buff,ULONG length);

ULONG ReadFromBase2(HANDLE handle,ULONG Offset,

UCHAR *buff,ULONG length);

基本的写函数有:

ULONG WriteToBase0(HANDLE handle,ULONG Offset,

UCHAR *buff,ULONG length);

ULONG WriteToBase2(HANDLE handle,ULONG Offset,

UCHAR *buff,ULONG length);

应用程序可以通过Win32 API DeviceIoControl操作设备与驱动程序互相通信。DeviceIoControl内部会使操作系统创建一个IRP_MJ_DEVICE_CONTROL类型的IRP,然后操作系统会将这个IRP转发到派遣函数中。

而以上的读写接口函数的本质就是调用DeviceIoControl来实现的。例如从基地址0读函数ReadFromBase0函数原型,如图3。

其中,DDK定义的DeviceIoControl的函数原型为:

BOOL DeviceIoControl{

HANDLE hDevice,//设备句柄

DWORD dwIoControlCode, //控制码

LPVOID lpInBuffer,//输入数据缓冲区指针

DWORD nInBufferSize, //输入数据缓冲区长度

LPVOID lpOutBuffer, //输出数据缓冲区指针

DWORD nOutBufferSize,//输出数据缓冲区长度

LPDWORD lpByteReturned,//输出数据实际长度单元长度

LPOVERLAPPED lpOverlapped //重叠操作结构指针

};

DeviceIoControl的第二个参数是I/O控制码,控制码也称IOCTL值,是一个32位的无符号整型。DDK提供一个宏CTL_CODE,定义为:

CTL_CODE(DeviceType,Function,Method,Access)

其中,DeviceType为设备对象的类型;Function为驱动程序定义的IOCTL值,0x000到0x7ff为微软保留,0x800到0xfff由程序员自己定义;Method是操作模式,其中包括METHOD_BUFFERED为使用缓冲区方式操作,METHOD_IN_DIRECT为使用直接写方式操作,METHOD_OUT_DIRECT为使用直接读方式操作,METHOD_NEITHER为使用其他方式操作;Access为访问权限。

如上述例子ReadFromBase0函数,在驱动程序中用CLT_CODE宏定义定义的IOCTL码:

#define IOCTL_READ_BASE_BAR0 CTL_CODE(

FILE_DEVICE_UNKONWN,

0x800,

METHOD_BUFFERED,

FILE_ANY_ACCESS)

驱动程序中IOCTL派遣函数的实现是首先得到当前I/O堆栈,从I/O堆栈中再一次得到输入缓冲区大小,输出缓冲区大小,以及IOCTL。在实现过程中,运用C语言中的switch语句分别处理不同的IOCTL。在每个IOCTL情况下,就必须使用DDK提供的内核函数WRITE_REGISTER_XX系列函数和READ_REGISTER_XX系列函数操作物理设备内存。具体流程如图4所示。

当应用程序需要操作读写接口函数时,设备管理器就会发送相对应的IRP给设备驱动,驱动程序就会调用DispatchControl函数找到相应的IOCTL码,应用程序再调用DeviceIoControl操作设备。

4 结束语

本文以实际应用的一个PCI设备驱动开发为例着重介绍了驱动程序和应用程序的相互关系,并且还简单介绍了WDM式设备驱动和PCI总线协议。设备驱动程序是Windows操作系统重要的内核组建,在系统中起至关重要。如果驱动程序出错,很容易使Windows操作系统崩溃。开发利用DDK开发驱动程序能够使开发者加深对Windows内核和WDM规范的理解。

参考文献:

[1] 张帆,史彩成.Windows驱动开发技术详解[M].北京:电子工业出版社,2009.

[2] Walter Oney.Programming The Microsoft Windows Driver Model[M].1999.

[3] PCI Tech Company.PCI 9056BA Data Book[M].USA,2003.

[4] Microsoft Corporation,Windows 2000 DDK[M].USA,2000.

[5] 蹇红梅,居锦武,王兰英.Windows设备驱动程序开发[J].四川理工学院学报:自然科学版,2007,20(4).

上一篇:网络工程实践教学体系的构建 下一篇:浙江省中医药科研管理系统的的设计与实现