张小磊,孟李林,崔晨琪
(西安邮电大学 电子工程学院,陕西 西安 710061)
设备驱动是计算机系统里软硬件交互的接口,是计算机操纵硬件的基础,它的性能在很大程度上影响着整个系统的性能。KMDF是微软推出的下一代驱动模型WDF中的内核模式驱动程序框架,意在取代从WINDOWS2000就开始使用的WDM模型。其旨在为开发者提供一个面向对象的事件驱动开发框架,并封装了驱动与操作系统的接口使开发者可以更关注驱动需要操控的硬件,以改变长久以来驱动程序作为硬件和操作系统的连接,既要处理硬件行为,又要与操作系统内核交互而导致的开发难度大,开发门槛高等问题。KMDF是WINDOWS 7/8首先的驱动模型。
PCI Express是一种应用于各种计算与通信平台的高性能、通用、串行 I/O 互连协议。它采用包交换技术、Ack/Nak协议以及流量控制技术,实现报文的高速高可靠传输。此外,PCI Express协议保持与现有 PCI 设备的软件兼容性,而且可以用较低的开销提升现有 PCI 系统的性能,在 PC 机及嵌入式系统中得到了广泛的应用。因此研究基于KMDF驱动框架的PCI Express驱动设计具有现实意义。
本文介绍KMDF的对象模型和I/O模型,并介绍一种基于KMDF的PCI Express设备驱动程序的设计。
KMDF是内核模式驱动开发的框架,它定义了基于对象的编程模型,可以通过一系列层次化组织的对象以及与对象相关联的方法和属性来表示设备、驱动、队列等信息。KMDF对象模型中定义的主要的对象以及其层次如图1所示:
图1 KMDF中的主要对象及其层次
图1中KMDF框架定义的主要对象有:
(1)WDFDRIVER:WDF驱动对象,所有对象的根对象,描述一个驱动程序在内存的实例,维护驱动程序的相关信息,如驱动的入口点,加载位置,可以管理的设备等。每一个驱动程序在内存中有且仅有一个WDFDRIVER对象。
(2)WDFDEVICE:WDF设备对象,系统中具体硬件的抽象,代表驱动程序可以控制的硬件设备,用户程序可以通过定义的设备接口访问设备。WDFDEVICE也可以是虚拟设备。
(3)WDFQUEUE:WDF队列对象,一个特殊的I/O请求队列,它定义了一系列回调函数,当I/O请求进入队列时,框架将自动调用对应的回调函数处理。
(4)WDFINTERRUPT:WDF中断对象,定义了设备中断使能,禁止回调函数和设备中断的服务程序和DPC例程。
KMDF建立了自己的I/O模型,可以截获发送给本设备驱动程序的所有IRP。当IRP到达时,分发器根据IRP的主功能码把IRP分发到I/O请求队列或者电源管理队列,框架会在适当的时间自动的调用已经注册的回调函数。I/O流程如图2所示:
图2 I/O通过KMDF库和驱动程序流程
KMDF的I/O队列管理那些针对驱动程序的请求。驱动程序通常创建一个或多个队列,每个队列可以接受一种或多种类型的请求。调度方法决定给定时间内驱动程序可服务的请求数量。
PCI Express作为高速串行I/O技术已经在现行的计算机系统得到了广泛应用,本文涉及的PCI Express板卡是自行研发的基于Altera公司PCI Express硬核设计的高速数据处理系统。该系统的框架图如图3所示:
系统采用Altera 公司的Cyclone IV GX 系列FPGA芯片作为核心,使用FPGA中的硬件电路完成对下发数据的处理。FPGA主要模块功能介绍:
图3 系统框架图
PCI-E IP硬核:实现PCI Express 1.1版本协议,完成PC机地址域与设备内部地址域转换,DMA通过IP硬核接口与PC机通信。IP硬核是完成计算机系统和数据处理卡系统通信的桥梁。
CPU:采用Altera公司NIOS II软核实现,作为PCI Expess设备的主控模块,根据RAM缓冲区的数据包头,启动相应的算法模块;或者根据算法模块的结束信号启动一次DMA传输。
DMA以及RAM:DMA实现数据的高速传输,提高系统的效率;RAM数据的缓冲区,在每次运算过程缓存带运算数据以及运算结果。
数据处理:系统的功能模块,实现具体的数据处理算法,由CPU调度。
PCI Express是与PCI软件兼容的系统总线,PCI Express具有与PCI总线相同的软件访问接口,因此可以使用与PCI相同的软件流程。
本文介绍的PCI Express高速处理卡的驱动流程如图4所示。
KMDF设备驱动程序初始化与WDM初始化入口相同,均为NTSTATSU DriverEntry(INPDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath)函数[7],但是函数主要完成的功能并不相同,在WDM中主要完成驱动的回调函数设置,而在KMDF中则主要完成驱动对象的创建,即WDFDRIVER对象的创建,WDFDRIVER对象是对WDM中DRIVER_OBJECT对象的封装,创建WDFDRIVER对象时需要提供创建WDFDEVICE对象的回调函数和驱动的卸载例程的回调函数,以及属于驱动对象的属性。在KMDF中并不允许直接对框架创建的对象赋值,需要由专门的对象配置来实现。
图4 驱动流程图
对于KMDF中涉及的所有内核对象都可以在创建时指定一个属于对象的环境变量结构,和获得环境变量的方法名字,该环境变量的信息存储在对象属性结构里,由框架在对象创建时使用。主要代码如下:
WDF_DRIVER_CONFIG_INIT
(&config,UsbDataTransAdd);
//配置参数初始化,主要用来设置添加设备函数UsbDataTransAdd,此函数由框架在每次枚举属于驱动程序的设备时调用。
status = WdfDriverCreate(DriverObject,
RegistryPath,
WDF_NO_OBJECT_ATTRIBUTES,
&config,
WDF_NO_HANDLE);
//创建驱动对象,其中DriverObject由框架传递来的WDM驱动对象,RegistryPath是注册表中的服务键路径,config为已经初始化的对象配置。
在驱动初始化以后,或者设备被首次枚举,系统都会调用驱动对象中设置的添加设备程序,设备添加例程主要的职责是:创建设备对象、一个或多个I/O队列和设备GUID接口,设置各种事件回调函数,以及创建中断对象。主要函数如下:
WDF_PNPPOWER_EVENT_CALLBACKS_INIT(&pnpPowerCallbacks);
//初始化即插即用和电源管理例程配置结构,该结构只有在初始化后才在可以使用,并且该结构的初始化必须在设备对象创建之前完成。
WdfDeviceCreate(&DeviceInit, &deviceAttributes, &device);
//创建设备对象。
WDF_INTERRUPT_CONFIG_INIT(
&interruptConfig,
PCISample_EvtInterruptIsr,
PCISample_EvtInterruptDpc);
//设置中断例程和延时过程函数
WdfInterruptCreate(
device,
&interruptConfig,
WDF_NO_OBJECT_ATTRIBUTES,
&pDeviceContext->Interrupt);
//创建中断对象。
WdfDeviceCreateDeviceInterface(
device, (LPGUID)&PCI_DEVINTERFACE_GUID, NULL);
//设置内核驱动与应用态的接口其中PCI_DEVINTERFACE_GUID是应用程序寻找驱动的标识码,可使用Visual Studio中自带的GUID生成器生成。
KMDF设备驱动模型为分层模型,最底层为PCI Express总线驱动由操作系统提供,总线驱动上层才是我们提供的特定设备的设备驱动。在PCI Express设备驱动初始化时,由总线驱动通过IO端口CONFIG_ADDRESS(地址0CF8~0CFB),CONFIG_DATA(地址0CFC~0CFF)获取配置空间设备资源信息[8],将资源信息记录到设备对象的设备环境中,并且映射PCI Express配置空间BAR寄存器里描述的PCI Express内存到PC机内存空间,以使得驱动程序可以使用简单的内存访问函数直接访问设备空间。
在创建设备对象时注册的例程PcieDevicePrepareHardware会在设备进入工作状态之前由框架调用完成对硬件资源的获取,并且会在即将进入D0时对硬件进行一些配置,相关的主要代码:
case CmResourceTypeMemory:
{
momeryflag = TRUE;
pDevicecontext->MemBaseAdd=
MmMapIoSpace(
escriptor->u.Memory.Start,
descriptor->u.Memory.Length,
MmNonCached);
pDevicecontext->MemLength =
descriptor->u.Memory.Length;
pDevicecontext->PhysicalAdd=
descriptor->u.Memory.Start;
break;
}
//获取PCI Express设备在PC内存空间的地址并使用MmMapIoSpace函数将其转换成可被直接访问的内核态虚拟地址。
case CmResourceTypeInterrupt:
{ASSERT(descriptor->Flags!=
CM_RESOURCE_INTERRUPT_MESSAGE)break;}
在本设计中主要获取两种资源:
(1)CmResourceTypeMemory内存映射地址空间:PCI Express协议通过BAR映射FPGA电路控制寄存器到计算机系统的硬件地址空间,使得我们可以使用内存访问的方式直接访问FPGA硬件。
(2)中断资源:在WDM模型中,我们在必须获得分配给此设备的中断号以及此设备支持的中断方式等中断资源,以此为信息注册本设备的中断。但是在KMDF中这些已经被框架所实现,所以中断资源变得可有可无。
访问内存映射地址空间时可以用KMDF提供的专用函数WRITE_RESISTER_xx、READ_RESISTER_xx系列函数读写该空间对应的虚拟地址或者使用一般的内存访问函数直接访问该空间对应的虚拟地址。
本系统对数据的传输主要采用DMA实现,通过两个DMA实现数据的上行和下行,在本系统主要要求传输速度的前提下,本设计采用了把DMA控制和缓冲区映射到应用层,由应用程序直接控制DMA传输过程,以减少程序在状态切换上的开销。涉及的主要代码如下:
pDevicecontext->mdlcard=IoAllocateMdl(
pDevicecontext->UserMemBase1, pDevicecontext->UserMemLength1,
FALSE, FALSE, NULL);
MmBuildMdlForNonPagedPool(dx->mdlcard);//分配MDL结构,其中dx->UserMemBase1为在PcieDevicePrepareHardware例程得到的硬件资源。
pDevicecontext->usercard= MmMapLockedPagesSpecifyCache
(pDevicecontext->MemBaseAdd,
UserMode,
MmNonCached,
NULL, FALSE,
NormalPagePriority);
//映射硬件资源到应用态空间,pDevicecontext->MemBaseAdd存储DMA控制寄存器空间基地址;pDevicecontext->usercard为得到的用户空间地址,在合适的时机传回应用态。
pDevicecontext->mdldma=
IoAllocateMdl(pDevicecontext->vaCom
monBuffer,
SJY0601_MAXTRANSFER,
FALSE,FALSE, NULL);
MmProbeAndLockPages
(pDevicecontext->mdldma,
KernelMode,
IoModifyAccess);
pDevicecontext->userdma= MmMapLockedPagesSpecifyCache
(pDevicecontext->mdldma,
UserMode,
MmNonCached,
NULL,FALSE,
NormalPagePriority);
//其中pDevicecontext->mdldma为DMA缓冲区基地址,由应用程序直接读写。
经测试,本文所述的基于KMDF模型的PCI Express 数据处理系统驱动在DMA 传输模式下处理数据速度可达 3024 Mbits/s(包括下发数据,数据简单处理,数据上发),且在压力测试的100小时里程序能稳定、可靠地工作。
本文所述的PCI Express设备驱动程序,使用了KMDF模型,该模型提供了面向对象、事件驱动的驱动程序开发框架,由框架负责管理与操作系统内核相关的功能,不但使得驱动程序的编写简单方便而且可靠性和稳定性提高。同时本文所述的驱动使用了内核态到应用态地址映射的技术,所有数据均由应用程序直接写入设备,大幅度减少CPU状态转换带来的开销,提高系统的整体速度。综上所述这样的设计具有较传统设计更优的性能。
[1] 李正平,徐超,陈军宁,等.WDF驱动程序的设计与实现[J].计算机技术与发展,2007(5):227-230.
[2] 马晨,陈彦萍.基于PCI Express总线 1394b 网络传输系统WDM驱动设计[J].测控技术,2012,32(3):93-97.
[3] 武安河.Windows设备驱动程序WDF开发[M].北京:电子工业出版社,2009:3-11.
[4] 王祥.基于FPGA的PCI-E数据采集系统的设计与实现[D].成都:电子科技大学,2012:1-5.
[5] 季乔龙.基于WDF的加密卡驱动程序开发及密钥管理[D].成都:电子科技大学,2012:10-11.
[6] Ronald D Reeves.Windows 7设备驱动程序开发[M].张猛,纪小玲,周姝嫣,译.北京:人民邮电出版社,2012:95-95.
[7] 黎绍秀,卫红,兰春嘉. PCI- E 图像采集系统的 WDF 驱动程序设计[J].科学技术与工程, 2011,11(16):3824-3827.
[8] 王齐.PCI Express体系结构导读[M].北京:机械工业出版社,2011:123-123.