基于WDF的PCIe驱动开发

由于第一次接触WDF驱动开发,因此底层驱动基于微软提供的PCI9056驱动例子(因PCIe和PCI配置空间基本一致,故对9056例子做适当修改便可直接安装使用)。

驱动层提供设备驱动的基本功能函数,包括但不限于设备打开(OpenFile)、设备关闭(CloseFile)、数据读写(ReadFile/WriteFile)以及DeviceIoControl等,本文主要工作是基于基本函数对驱动进行封装,更方便软件开发人员使用。

1 PCIe接口驱动需求分析

在某型芯片中,提供 PCI-E 主机接口, 该 PCI-E接口设有三个存储器类型的 BAR 空间, 均为 32 位访问,PCI-E 中断采用 INTA,另外,该 PCI-E 提供有PCI-E 专用 DMA, 该 DMA 操作由硬件控制, 仅需软件申请主机的一段内存空间作为 DMA 缓存,基于以上PCI-E完成驱动接口的设计工作:

1) 中间层需要驱动层提供以下接口功能:配置空间的访问,存储空间的访问,DMA数据传输接口以及中断处理;
2) 应用层需要中间层提供以下接口功能:设备控制接口(包括设备的打开,初始化,关闭),数据传输接口(包括数据的读写操作,数据接收缓冲区大小设置以及对缓冲区的监控)。

2 驱动层接口设计:

2.1 设备通信接口:
WDF驱动模型主要通过KMDF回调例程与WIN32函数的一一对应,实现驱动程序与应用程序的通信。数据传输任务主要在EvtIoRead、EvtIoWrite和EvtIoDeviceControl例程中完成,由于硬件支持DMA,故本文在EvtIoRead和EvtIoWrite中采用DMA方式。
CloseHandle EvtFileClose
1) DMA传输
DMA传输编程的流程图如图所示,首先通过函数WdfDmaTransactionInitialize或WdfDmaTransactionInitializeUsingRequest初始化DMA传输,再调用WdfDmaTransactionExecute启动DMA编程;在EvtProgramDMA函数中根据数据手册完成对DMA相关寄存器的配置;最后当DMA传输中断时,在中断延迟处理例程EvtInterrupt例程中判断DMA传输是否结束,没有则调用WdfDmaTransactionExecute函数,继续启动DMA编程,若DMA传输结束,则完成I/O请求。
2) EvtIoDeviceControl例程设计
该例程主要通过应用程序向驱动传送的控制码区分功能,根据需求创建如下控制码:
PCIE_IOCTRL_CONFIGREG_READ:配置空间寄存器读
PCIE_IOCTRL_CONFIGREG_WRITE:配置空间寄存器写
PCIE_IOCTRL_MEMORY_READ:存储空间寄存器读
PCIE_IOCTRL_ MEMORY _WRITE:存储空间寄存器写
PCIE_IOCTRL_ OFFSETADDR _SET:偏移地址设置
通过调用WIN32函数DeviceIoControl传入指定的控制码即可实现对应功能。
2.2 中断处理:
在驱动中创建中断对象和中断回调例程,当有中断产生时首先判断该中断是否来自本设备,若是则读取中断标志位分析中断类别,然后执行相应操作。中断类型有DMA读写中断控制,在一次DMA传输完成产生;数据传输中断,在FPGA主动给上位机发送数据时产生。前者已在DMA部分论述,此处主要介绍后者的中断处理。
内核同步听起来比上面两者高级一些,原因是使用的人不太多。原理却很简单:创建一个事件对象传递给内核,用户层一直等待它的完成。这和用户层的两个线程之间的同步,并无差别。但在内核那边,有一些技巧。
问题在于,用户创建的事件,是一个Win32 句柄,并且这个句柄还是进程相关的(即放到别一个进程中去,这个句柄就是非法的了)。内核所在的地址却是全局性的(亦即一个地址需在此进程中正确,亦在所有其他的进程中也正确),要让全局性的内核可以安全使用局部性的用户句柄,唯一的办法是将用户句柄转换成全局对象地址。

至于用户层的操作,是非常简便的。不过这样两步而已:创建事件、发送事件。除此之外,多出的一步是等待内核事件完成。一般应该单独创建线程来完成。
在设备初始化部分,将通过DeviceIoControl向驱动层传入一个事件句柄,驱动程序调用ObReferenceByHandle函数获取这个事件的一个内核对象指针(即内核事件),一旦该对象在内核层被触发,则应用层对应的事件也将同时被触发。当FPGA将数据写入PCIe板卡的存储空间后会发起一次软件中断,驱动程序进入中断延迟处理例程中触发内核事件通知上位机,读取数据。(方案2:在中断延迟处理例程中将数据拷贝到公用缓冲区,再触发事件通知上位机将公用缓冲区中的数据转移)
3 中间层接口设计:
在WDF驱动模型中,驱动层依靠EvtIoRead,EvtIoWrite和EvtIoDeviceControl三个例程函数与应用层通信,对应的win32函数接口参数复杂,传输大数据时会大量占用总线周期,严重时还可能会堵塞系统,为此,将原有的驱动层封装,作为中间层。中间层的功能主要有:

1:采用优先级动态调整的方法优化数据的传输策略,并完成数据传输的校验工作;

2:支持多个设备同时工作;

评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值