首页 > 系统相关 >转载:基于WDF的PCI/PCIe接口卡Windows驱动程序(5)-如何为硬件移植驱动程序

转载:基于WDF的PCI/PCIe接口卡Windows驱动程序(5)-如何为硬件移植驱动程序

时间:2023-11-20 15:25:47浏览次数:45  
标签:驱动程序 接口卡 代码 应用程序 地址 PCI 所示 WDF

原文地址:http://www.cnblogs.com/jacklu/p/6139347.html

如果你觉得这篇博客对你的项目有用,请引用以下论文:

Meng Shengwei, Lu Jianjie. Design of a PCIe Interface Card Control Software Based on WDF. Fifth International Conference on Instrumentation and Measurement, Computer, Communication and Control. IEEE, 2016:767-770.

正如前几篇博客所说,使用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所示:

image

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, 实现不同功能。

image

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

image

image

2.2 Device.h说明

  • 代码对 FPGA 上硬件资源的偏移地址进行宏定义,在 Altera 系列的 FPGA 里,这些偏移地址也叫 Avalon 地址, 在 sopc builder 可以自定义分配, 如图 2-1 中的 Base和End 所示, “ 小锁头” 标志表示地址锁定, 点击该标志解锁后可以自定义便宜地址。这些地址必须与驱动程序中所用的地址一一对应;
  • 代码定义了设备对象结构体, 对几个重要的成员变量注释如下:

image

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

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 所示。

image

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

image

  • 配置好 PCI 后就可以配置 DMA 控制寄存器了, 首先将状态寄存器和控制寄存器清零,如代码所示;
  • 然后将读写地址分别写入读写寄存器, 注意PC机上的内存地址为低16位,而高16位要配置在 Avalon-PCI 地址转换表中, 如代码所示, 其中 0x20000 为 PCI IP核的 Avalon 地址, 如图 2-1 所示;
  • 向长度寄存器写入 DMA 传输长度(单位:字节) , 如代码所示;
  • 向控制寄存器写入 DMA 控制字 0x8c, 即长度寄存器降低为0时传输完成, 使能DMA,32 位字传输, 如代码所示;

3应用程序如何调用驱动程序

3.1 GUID说明

GUID( Globally Unique Identifier) 是微软推出的全局唯一标识符, 通过使用某个特定的算法( 比如根据时间或地点等信息) 生成一组128 位二进制数,来标识某一个实体,比如硬盘上的一张图片。 GUID 广泛应用于微软的产品中, 用于识别接口、文件等对象。开发者可以使用 VS2013 下的工具 GUIDGen.exe 生成 GUID 值, 该 GUID 标识驱动程序, 应用程序根据这个 GUID 值来找到对应的驱动程序。

应用程序总体流程设计为: 首先通过 Win32API 函数 CreateFile 打开设备, 然后调用DeviceIoControl 函数与驱动程序通信,即读写数据,当应用程序退出时,调用CloseHandle函数关闭设备。

应用程序根据底层设置的 GUID 获取设备路径, 从而与指定设备建立连接: 首先调用SetupDiGetClassDevs 函数获得符合传入参数 GUID 的设备信息集合 hDevInfo; 然后根据设备信息集合 hDevInfo 和 GUID 调用 SetupDiEnumDeviceInterfaces 函数枚举设备信息集合中的设备,并输出设备接口数据信息 DeviceInterfaceData ; 再 根 据 hDevInfo和 DeviceInterfaceData 调用 SetupDiGetDeviceInterfaceDetail函数得到保存设备接口详细信息DeviceInterfaceDetailData 的缓冲区大小 ,接着为其开辟空间 , 最后再调用一次SetupDiGetDeviceInterfaceDetail 函数获得设备接口详细信息 pDeviceInterfaceDetailData; 最后根据设备接口详细信息的设备接口路径 pDeviceInterfaceDetailData->DevicePath 调用CreateFile 函数来创建设备句柄。

3.2 应用程序如何打开设备

在测试程序 Source.c 的代码第 62-70 行,完成打开设备句柄功能, 不需要用户改动。

3.3 应用程序如何读设备

成功打开设备后, 调用 DeviceIoControl 即可通过 IOControl 队列与驱动程序通信, 以读32bit 数据为例, 首先向驱动程序写入需要读的存储单元地址, 如代码第 257-272 行所示,然后向驱动程序传入读数据缓存 outBuffer, 如代码第 274-289 所示, 即可获得FPGA 上对应偏移地址的数据。

3.4 应用程序如何写设备

成功打开设备后, 调用 DeviceIoControl 即可通过 IOControl 队列与驱动程序通信, 以写32bit 数据为例, 首先向驱动程序写入需要写的存储单元地址, 如代码第 303-317 行所示,然后向驱动程序传入缩写数据缓存 inBuffer, 如代码第 322-336 所示, 即可将数据写入 FPGA上对应偏移地址的内存单元。

3.5 应用程序如何对设备进行DMA传输

通过Win32API 函数ReadFile和WriteFile对设备句柄进行文件读写操作, 在内核驱动中会调用驱动程序的 qd41EvtIoRead和qd41EvtIoWrite,实际的DMA配置在这两个驱动回调例程中实现, ReadFile和WriteFile 只是完成了数据从用户层到内核层的搬运。详细代码在第 340-378 行,不再赘述。

4 INF文件与如何调用Qt编写上位机软件调用底层驱动

image

image

image

image

我把C语言编写的应用程序每一个独立的控制命令编译成一个可执行文件,这样Qt可以在新的进程中调用 C 程序编写的命令字读写程序。使用Qt 封装好的进程类 QProcess, 使用其成员函数 start 即可调用一个外部程序。

标签:驱动程序,接口卡,代码,应用程序,地址,PCI,所示,WDF
From: https://www.cnblogs.com/swlingling/p/17844019.html

相关文章

  • 转载:基于WDF的PCI/PCIe接口卡Windows驱动程序(1)-WDF概述及开发环境搭建
    原文出处:http://www.cnblogs.com/jacklu/p/4619110.html本科毕业设计是这方面的工作,所以想开几篇博客来介绍使用WDF开发PCI/PCIe接口卡的驱动程序方法。这个系列的博客将首先用一个篇幅为不懂Windows 下PCI/PCIe驱动开发的介绍WDF和开发环境搭建,接下来几篇将直接讲述程序编写,......
  • 转载:基于WDF的PCI/PCIe接口卡Windows驱动程序(2)-开发者需要了解的WDF中的一些重要的概
    原文出处:http://www.cnblogs.com/jacklu/p/4646601.html本科毕业设计是这方面的工作,所以想开几篇博客来介绍使用WDF开发PCI/PCIe接口卡的驱动程序方法。在上一篇简要介绍了WDF和开发环境搭建后,本篇将讲述几个WDF中的概念,对开发者了解WDF非常有帮助,属于“内功部分”;本篇文章结......
  • Linux驱动开发 驱动程序的具体编写及出口入口函数解析,printk打印内核信息
    (文章目录)前言本文将带大家学习驱动程序的具体编写及出口入口函数解析。一、出口函数入口函数解析每一个驱动程序都是含有出口函数和入口函数的,装载驱动的时候先调用入口函数,卸载驱动的时候则调用出口函数。入口出口函数的形式如下:static__initinthello_dev_init(void)......
  • Linux读取文件的简单字符驱动程序
    Linux读取文件的简单字符驱动程序设备驱动程序作为沟通外部硬件与Linux内核的纽带,属于嵌入式开发中不可避免的一环。Linux内核程序的开发和用户空间中开发的不同之处在于两点,一是内核程序由内核进行调用,基本没有一个类似于用户空间程序中的main函数,二是内核代码无法调用很多我们......
  • ESXI内虚拟服务器安装VMware tools报Windows无法验证此驱动程序软件的发布者
    ESXI内虚拟服务器安装VMwaretools报Windows无法验证此驱动程序软件的发布者 安装VMwaretools时,报Windows无法验证此驱动程序软件的发布者提示无法安装驱动后,自动回滚,导致安装失败。后来发现是WIN2008R2虚拟服务器少了KB4474419补丁,下载这个补丁的镜像文件(下载链接:https......
  • Rockchip RK3399 - DRM HDMI驱动程序
    ----------------------------------------------------------------------------------------------------------------------------开发板:NanoPC-T4开发板eMMC:16GBLPDDR3:4GB显示屏:15.6英寸HDMI接口显示屏u-boot:2023.04linux:6.3----------------------------------......
  • 关于Windows打印机驱动相关问题-如何利用Java(或其他)调用打印机驱动程序完成原始文件翻
    前面这些都是问题描述,问题在偏下面场景:用户电脑上安装了PCL驱动,可通过驱动完成打印。需求:现在需要提供一种脱离PC端完成文件上传并打印的功能。让用户使用手机或pc未安装驱动时都能打印文件。目前思路:首先上传文件这一步没有任何问题,开发了相应的H5界面,通过浏览器进行原始文件......
  • 第一个驱动程序(在Ubuntu系统下运行)
    一、构造内核源码树#apt-cachesearchlinux-source#apt-getinstalllinux-source-4.4.0(下载的源码在目录/usr/src下)#解压内核源码tarxjf.....进入源码目录#makeoldconfig#make#makemodulesmakemodules_install12345678ref:http://blog.chinaunix.n......
  • Rockchip RK3399 - DRM eDP驱动程序
    在《RockchipRK3399-DRM驱动程序》》我们已经介绍过了,RK3399有两个VOP,均可以支持HDMI、eDP、DP、MIPIDSI0、MIPIDSI1显示接口,本节我们选择eDP作为分析的对象。一、设备树配置1.1edp设备节点设备节点vopb下的子节点vopb_out_edp通过edp_in_vopb(由remote-endpoint属性指定)......
  • Rockchip RK3399 - DRM eDP驱动程序
    在《RockchipRK3399-DRM驱动程序》》我们已经介绍过了,RK3399有两个VOP,均可以支持HDMI、eDP、DP、MIPIDSI0、MIPIDSI1显示接口,本节我们选择eDP作为分析的对象。一、设备树配置1.1edp设备节点设备节点vopb下的子节点vopb_out_edp通过edp_in_vopb(由remote-endpoint属性指定)......