基于WDF的PCI/PCIe接口卡Windows驱动程序(5)

时间:2022-04-28 04:25:14

正如前几篇博客所说,使用WDF开发PCIe驱动程序是我本科毕业设计的主要工作。在读研的两年,我也分别为所在课题组移植了自己编写的驱动程序,在Windows 32位和64位平台下的PXI、PXIe、PCI、PCIe板卡分别得到了验证。

这篇文章根据自己最新编写的驱动代码(源代码请找博主索取),主要讲述如何为自己的硬件板卡移植驱动程序,并简单讲述如何使用Altera系列FPGA配置PCI IP核,然后对INF文件作简要描述,最后描述如何使用Qt编写上位机软件调用底层驱动。

准备去读博了,这一篇将作为这个系列的完结,之后将把更多精力放到机器学习上来。 

1概述

所编写的驱动代码程序包括7个源代码文件,分别是Device.h, Driver.h, Public.h, Trace.h, Device.c,Driver.c, Queue.c。其中Device.h 定义了与硬件相关的地址偏移;Public.h定义了DeviceIoControl 用到的用户自定义命令字, 此头文件由上层应用程序和驱动程序共同使用;Queue.c定义了I/O回调例程,分别使用了read、write和I/O Control 三个队列。 除了这三个文件外, 不建议更改其他4个文件的代码。三个源文件函数列表分别如图 1-1、1-2、1-3所示:

2驱动程序移植说明 2.1 Public.h说明

代码中定义了GUID值,开发者可以使用 VS2013 下的工具 GUIDGen.exe 生成 GUID值,该GUID标识驱动程序,应用程序根据这个GUID值来找到对应的驱动程序。

代码中定义了CTL_CODE, I/O 处理例程 DeviceIoControl 的第二个参数dwIoControlCode 就是由 CTL_CODE 宏定义的。 CTL_CODE 是一个用于创建一个唯一的32 位系统I/O控制代码的宏,这个控制代码包括4部分组成:DeviceType(设备类型,高16位(16-31 位)),Access(访问限制,14-15 位),Function(功能2-13位),Method(I/O访问内存使用方式)。CTL_CODE 定义中有一个Method域,该2域定义了驱动程序中获取应用程序数据缓冲区的地址方式。 已经定义的 CTL_CODE 命令字说明如表 2-1 所示, 用户可以根据格式自定义 CTL_CODE, 实现不同功能。

什么是 CRA 寄存器组? 打开 quartus 的 sopc builder,可以看到sopc架构如图 2-1 所示,在PCI IP核一栏中有 Control Register Access 寄存器组,地址范围0x00000000-0x00003fff。 里面关键的寄存器地址如图 2-2 所示。 通过读黄色标识的寄存器,可以通过驱动程序调试验证 PCI 核。 关于 CRA 寄存器组的配置说明会在 2.3 节详细说明。

2.2 Device.h说明

代码对 FPGA 上硬件资源的偏移地址进行宏定义,在 Altera 系列的 FPGA 里,这些偏移地址也叫 Avalon 地址, 在 sopc builder 可以自定义分配, 如图 2-1 中的 Base和End 所示, “ 小锁头” 标志表示地址锁定, 点击该标志解锁后可以自定义便宜地址。这些地址必须与驱动程序中所用的地址一一对应;

代码定义了设备对象结构体, 对几个重要的成员变量注释如下:

代码对一些事件回调例程进行了说明, 一般不需要用户进行二次修改;

2.3 Queue.c说明

代码是用户需要针对功能开发的代码。 以从应用程序向驱动程序写入偏移地址为例,即原代码第 xx-xx 行 , 首先在 Public.h 文件里定义 IoControlCode 为qd41_IOCTL_WRITE_OFFSETADDRESS, 第 48-53 行为获取应用程序输入缓存数据的指针( 代码中为 inBuffer) , 通过赋值语句将 inBuffer 指向的数据内容赋给设备对象的成员变量 OffseAddressFromApp, 即完成了本功能;

代码定义了写队列功能, 对 DMA 的写功能配置在此函数中完成。 Altera的 DMA IP 核共有 5 个寄存器, 如图 2-3 所示。

在配置 DMA 前需要配置 PCI CRA 寄存器, 使能 PCI 中断, 配置 Avalon-PCI 地址转换表, 如代码所示;

为什么需要配置 Avalon-PCI 地址转换表? 因为 PCI IP核一端是 PCI 总线,一端是Avalon总线,地址转换过程图 2-4 所示,类似MMU地址翻译原理,不在赘述,此时需要把PC机获得的DMA传输缓存物理地址的高16位地址写入地址转换表;

配置好 PCI 后就可以配置 DMA 控制寄存器了, 首先将状态寄存器和控制寄存器清零,如代码所示;