基于WDF的PCI-CAN设备驱动程序设计
2013-11-05黎顺杰张艳荣
黎顺杰 张艳荣
(西南交通大学机械工程学院,成都 610031)
设备驱动程序是硬件设备连接到计算机操作系统的软件接口,驱动程序的好坏直接影响到硬件的运行与系统的安全。所以,稳定高效的驱动程序对于设备硬件的开发具有重要的意义。
1 PCI-CAN卡系统架构
本文所述驱动程序是基于自行研发的PCI-CAN总线设备,该设备的系统结构框图如图1所示:
图1 PCI-CAN设备硬件结构框图
系统是在S1300 PCI开发平台上利用硬件描述语言(Verilog HDL)实现,核心芯片采用的是ALTERA公司CYCLONE2 FPGA系列的EP2C5Q208C8。通过使用开发平台内部集成的PCI总线控制器模块,可实现符合PCI总线协议的数据传输。PCICAN总线设备的工作流程如下:计算机通过IO端口写控制命令到控制寄存器模块,控制寄存器模块接收指令并初始化CAN总线控制器,如果是读CAN指令,控制寄存器模块将CAN总线上传的数据缓存到 SDRAM,利用 DMA(Direct Memory Access)方式写入主机内存,由主机进行相应的处理;如果是写CAN指令,计算机将数据通过内存映射写入FIFO,控制寄存器模块控制CAN(Controller Area Network)总线控制器从FIFO中取出数据。在整个系统流程中,驱动程序实现了应用程序与硬件之间的控制引导,作用不可取代。
2 WDF驱动程序设计
2.1 WDF驱动程序框架
WDF驱动程序框架中,所有的事物都由对象来表示,所有操作都被定义为一个事件(Event)或回调(Callback)。基于对象技术的WDF实现了驱动程序与操作系统内核之间的分离,驱动程序通过框架内定义的对象与方法来实现自己的功能,具体的驱动程序与操作系统内核的交互工作则交给框架内封装的方法(函数)完成。这样使得驱动程序的设计变得更加简单明了。
WDF 之中包含了 KMDF(Kernel-Mode Driver Framework)与UMDF(User-Mode Driver Framework),本文所涉及的设备驱动程序开发是基于KDMF实现的。
2.2 设备驱动程序开发
PCI-CAN设备驱动程序结构框架如图2所示:
图2 PCI-CAN设备驱动程序结构框图
PCI-CAN设备驱动程序设计从功能上可分为设备初始化、控制设置与数据交换三个部分。设备初始化主要实现设备识别、相关信息状态获取与硬件资源分配;控制设置负责系统初始化与启动;数据交换处理的是设备功能的具体应用,即PCI总线与CAN总线之间的数据传输。
从本质上来说,WDF驱动程序就是入口函数DriverEntry与事件函数及其子函数的集合。操作系统第一次装载驱动程序时会调用DriverEntry例程以完成驱动程序初始化。不同类型的驱动函数其DriverEntry也不同,可分为设备驱动、过滤驱动与纯软件驱动。本文所述驱动程序属于设备驱动,在入口函数DriverEntry中,除了创建和初始化WDFDRIVER对象,还需要注册EvtDriverDeviceAdd事件回调。
status=WdfDriverCreate(DriverObject,RegistryPat h,……);
//创建WDFDRIVER对象
WDF_DRIVER_CONFIG_INIT(&config,PCICAN_EvtDeviceAdd);//注册EvtDriverDeviceAdd事件回调
(1)设备初始化
驱动程序成功初始化后,操作系统会顺序调用EvtDriverDeviceAdd、EvtDevicePrepareHardware、EvtDeviceReleaseHardware 和InitializeDMA等回调函数以实现设备的初始化。
作为在设备初始化过程中第一个被调用的回调函数,EvtDriverDeviceAdd需要完成:
(1)创建设备对象;
(2)创建设备对象GUID接口或符号链接;
(3)创建一个或多个的I/O队列;
(4)设置各种事件的回调例程,如电源管理、即插即用、I/O处理例程等。
status=WdfDeviceCreate(&DeviceInit,&deviceAttributes, &device);//创建设备对象
status=WdfDeviceCreateDeviceInterface(device,(LPGUID)&PCICAN_DEVINTERFACE_GUID, NULL); //创建GUID接口
X86处理器有两种独立的地址空间,分别是I/O地址与内存地址。I/O地址空间只有64KB,内存地址空间可以达到4G。对于微机接口卡,可以将I/O端口与存储器芯片分别映射到这两个地址空间中。而对这两种不同的地址空间的访问,需要驱动程序进行相应的预处理工作:在EvtDevicePrepareHardware中,首先获取配置资源与资源描述符:对于I/O端口,将首地址与空间大小值保存起来;对于存储器芯片,调用MmMapIoSpace函数将物理地址转换为系统内核地址然后保存。相对应的,在卸载设备时,系统会调用EvtDeviceReleaseHardware回调函数释放之前申请的硬件资源,对于存储器地址,要用MmUnmapIoSpace函数解除物理地址与系统内核地址之间的关联。
for(i=0; i descriptor=WdfCmResourceListGetDescriptor(Resource ListTranslated,i);//获取资源描述符 case CmResourceTypeMemory:pDeviceContext->MemBaseAddress=MmMapIoSpace( descriptor->u.Memory.Start, descriptor->u.Memory.Length,MmNonCached); pDeviceContext->MemLength=descriptor->u.Memory.Length;break; case CmResourceTypePort:pDeviceContext->IoBaseAddress=descriptor->u.Port.Start.LowPart;//I/O地址配置 pDeviceContext->IoLength = descriptor->u.Port.Length;break; MmUnmapIoSpace(pDeviceContext->MemBaseAddress,pDeviceContext->MemLength);//解除地址关联 设备初始化第三步,利用函数InitializeDMA()创建DMA适配器与一个DMA传输。 InitializeDMA(IN WDFDEVICE Device){…… status=WdfDmaEnablerCreate(…);//创建DMA适配器 status=WdfDmaTransactionCreate(pDeviceContext->DmaEnabler,WDF_NO_OBJECT_ATTRIBUTES,&pDeviceContext->DmaTransaction ); //创建一个DMA传输 ……} (2)控制设置及数据交换 应用程序对驱动程序的通信需要调用CreateFile、ReadFile、WriteFile、DeviceIoControl 与 CloseHandle。CreateFile打开设备的方式有两种:GUID接口与符号链接名,本文所述驱动程序采用的是GUID接口方式。 DevicePath=GetDevicePath((LPGUID)&PCICAN_DEVINTERFACE_GUID);//获取设备路径 hDevice=CreateFile(DevicePath,…… );//打开设备 获得了设备的有效句柄,应用程序就可以调用DeviceIoControl函数写设备控制寄存器。应用程序的请求会被放入请求队列之中,并在EvtIoDeviceControl函数之中被处理。EvtIoDeviceControl函数之中可以定义多个不同的分支,对应于不同的I/O控制命令。应用程序通过I/O控制命令锁定具体的操作分支。I/O控制命令定义了4种数据访问方式,本文采用的是METHOD_BUFFERED,系统会分配一个缓冲区用作输入与输出。由于系统的控制寄存器被映射到了I/O地址空间之中,可以使用下面两条指令对寄存器进行读写:WRITE_PORT_XXX();//写端口数据 READ_PORT_XXX()//读端口数据 数据交换操作可以分为输出数据与输入数据。应用程序调用WriteFile写数据,在DeviceIoWrite中实现。对于数据读写,使用的是内存映射方式。DeviceIoWrite首先需要获得一个输入缓冲,然后利用WdfMemoryCopyToBuffer()函数将数据送入到设备存储器。 status=WdfRequestRetrieveInputMemory(Request,&memory);//输入缓冲 本文重点讨论了基于WDF的PCI-CAN设备驱动程序设计方法。本文所述的PCI-CAN卡驱动程序,在WDK1.9中成功编译,自动生成INF文件(设备安装信息)与SYS文件(驱动程序代码),成功安装且运行稳定可靠。经过测试,支持5kbps-1MbpsCAN总线波特率,数据流量最高可达3000帧/秒,相关参数均可由应用程序设置后传递到FPGA。另外,测试DMA读极限速率为116M/s,大大降低了总线数据交换的系统延迟。总而言之,WDF驱动模型简化并优化了驱动开发技术,同WDM驱动模型相比,变得更加稳定、灵活与高效。 [1] 谭文.寒江独钓—Windows内核安全编程[M].北京:电子工业出版社,2009. [2] 武安河.Windows设备驱动程序WDF开发[M].北京:电子工业出版社,2009. [3] Penny Orwick,Guy Smith . Developing Drivers with the Microsoft Windows Driver Foundations[M]. Microsoft Press, 2007. [4] 邹敬轩,蔡皖东.基于WDF过滤驱动的USB存储设备监控系统[J]. 计算机工程与科学,2010 ;32(3):42. [5] 钱宇红.USB数据传输卡WDF驱动程序开发[J].计算机应用与软件,2012 ;29(6):225-227.3 结束语