前言

前一篇文章讲到了利用windriver来生成一个对应使用的板卡的驱动程序,并且有相对应的INF文件给板卡安装上。这个生成的驱动程序代码包含了基本的访问板卡的功能,甚至可以实现DMA传输等功能(需要你的板卡支持DMA操作),但是这个驱动程序是基于交互式的,将程序编译生成exe文件运行后,还需要操作者输入访问的寄存器偏移地址或者数据等,没法做到自动执行。所以,我们在得到这份驱动代码后,基于这个框架,需要自行修改部分内容,来满足自身的需求。

Windriver驱动程序结构

  1. 首先我们先来看下生成的驱动程序的结构,pcie_1206是我自己命名,主要是kp_pcie1206.c、pcie1206_lib.c和pcie1206_diag.c。其中kp.c文件是用来和内核态进行交互的,除非你需要使用到kernel PlugIn功能,不然这个文件可以不用修改,只需要注意下编译时不报错就行;lib.c文件主要包含了一些库,使用Windriver下的WDC API,让上层软件可以访问到板卡,同时也会将一些WDC API二次封装成pcie1206(你所命名下的)的API,可以自行选择是否使用;diag.c就是基于用户态下的应用程序,来访问板卡上的资源,驱动程序需要修改的部分主要集中在这个文件。

  2. 接着我们再来看下diag.c的main()

int main(void)
{WDC_DEVICE_HANDLE hDev = NULL;DWORD dwStatus;printf("\n");printf("PCIE1206 diagnostic utility.\n");printf("Application accesses hardware using " WD_PROD_NAME ".\n");/* Initialize the PCIE1206 library */dwStatus = PCIE1206_LibInit();if (WD_STATUS_SUCCESS != dwStatus){PCIE1206_ERR("pcie1206_diag: Failed to initialize the PCIE1206 library: %s",PCIE1206_GetLastErr());return dwStatus;}/* Find and open a PCIE1206 device (by default ID) */hDev = DeviceFindAndOpen(PCIE1206_DEFAULT_VENDOR_ID, PCIE1206_DEFAULT_DEVICE_ID);/* Display main diagnostics menu for communicating with the device *///MenuMain(&hDev);  //此处用来交互访问板卡资源,可以选择注释掉,自行编写需求功能//自行编写的定制化功能DWORD TransferSize = 1024 * 1024 * 128;ReservedMemoryDMA(hDev, TransferSize, 0);/* Perform necessary cleanup before exiting the program: *//* Close the device handle */if (hDev)DeviceClose(hDev, NULL);/* Uninitialize libraries */dwStatus = PCIE1206_LibUninit();if (WD_STATUS_SUCCESS != dwStatus){PCIE1206_ERR("pcie1206_diag: Failed to uninitialize the PCIE1206 library: %s",PCIE1206_GetLastErr());}system("pause>nul");return dwStatus;
}

main函数里首先将WDC API相关的库初始化;接着找到我们使用的PCI设备,打印相关信息,并返回设备的相关句柄;然后就可以自行编写定制化功能的代码了,这里我做了一个DMA传输相关的功能;最后就是关闭句柄,解除库等操作。

DMA传输

  1. windriver其实已经提供了一套DMA传输的流程操作,在DriverWizard的help里找到PCI User’s Manual手册,打开之后9.1节会讲到DMA传输。根据分配DMA Buffer方式的不同,DMA传输又可以分成Contiguous buffer和Scatter/Gather。
  2. Contiguous buffer的方式需要分配到一整块连续的物理内存,对于操作系统来讲,较大buffer的分配不是一件容易的事情,很多情况下是要看你的电脑内存够不够,而且够了也不一定能分配到连续的。而Scatter/Gather则是分散聚合的模式,可以将多块小的物理内存块聚合起来一起使用,只需要通过链表将这些内存块一一串联起来。当然具体用什么方式需要跟不同的需求来决定的。
  3. 这里以Contiguous buffer的方式说明一下DMA传输的流程。整个DMA传输过程全部放在了DMARoutine()函数里,需要输入的参数有hDev(获取到的设备句柄)、dwDMABUfSize(申请的内存buffer大小)、u32LocalAddr(自行确认有没有这个需求,我做的时候把这个参数去掉了)、dwOptions(申请buffer传递给WDC API的标志位,比较重要)、fPolling(轮询标志位,我最后采用了轮训方式,也可以选择不需要这个参数,自行设计)、fToDev(DMA传输的方向,Device->host还是host->Device, 关系到buffer的分配)。
BOOL DMARoutine(WDC_DEVICE_HANDLE hDev, DWORD dwDMABufSize,UINT32 u32LocalAddr, DWORD dwOptions, BOOL fPolling, BOOL fToDev)
{PVOID pBuf = NULL;WD_DMA *pDma = NULL;BOOL fRet = FALSE;/* Allocate a DMA buffer and open DMA for the selected channel */if (!DMAOpen(hDev, &pBuf, u32LocalAddr, dwDMABufSize, fToDev, &pDma))goto Exit;/* Enable DMA interrupts (if not polling) */if (!fPolling){if (!MyDMAInterruptEnable(hDev, MyDmaIntHandler, pDma))goto Exit; /* Failed enabling DMA interrupts */}/* Flush the CPU caches (see documentation of WDC_DMASyncCpu()) */WDC_DMASyncCpu(pDma);/* Start DMA - write to the device to initiate the DMA transfer */MyDMAStart(hDev, pDma);/* Wait for the DMA transfer to complete */MyDMAWaitForCompletion(hDev, pDma, fPolling);/* Flush the I/O caches (see documentation of WDC_DMASyncIo()) */WDC_DMASyncIo(pDma);fRet = TRUE;Exit:DMAClose(pDma, fPolling);return fRet;
}/* DMAOpen: Allocates and locks a contiguous DMA buffer */
BOOL DMAOpen(WDC_DEVICE_HANDLE hDev, PVOID *ppBuf, UINT32 u32LocalAddr,DWORD dwDMABufSize, BOOL fToDev, WD_DMA **ppDma)
{DWORD dwStatus;DWORD dwOptions = fToDev ? DMA_TO_DEVICE : DMA_FROM_DEVICE;/* Allocate and lock a contiguous DMA buffer */dwStatus = WDC_DMAContigBufLock(hDev, ppBuf, dwOptions, dwDMABufSize, ppDma);if (WD_STATUS_SUCCESS != dwStatus){printf("Failed locking a contiguous DMA buffer. Error 0x%lx - %s\n",dwStatus, Stat2Str(dwStatus));return FALSE;}/* Program the device's DMA registers for the physical DMA page */MyDMAProgram((*ppDma)->Page, (*ppDma)->dwPages, fToDev);return TRUE;
}/* DMAClose: Frees a previously allocated contiguous DMA buffer */
void DMAClose(WD_DMA *pDma, BOOL fPolling)
{/* Disable DMA interrupts (if not polling) */if (!fPolling)MyDMAInterruptDisable(hDev);/* Unlock and free the DMA buffer */WDC_DMABufUnlock(pDma);
}
  1. 核心函数是DMAOpen(),它分配了一个连续物理地址的buffer,并将其在内核态下锁定起来,这里使用到了WDC_DMA_ContigBufLock函数来和内核态下进行交互,并且返回一个WD_DMA的结构体,里面包含了DMA buffer相关的信息,例如分配的物理地址,对应的虚拟地址,分配buffer的长度等等。需要注意的是如果使用这种方式分配大于1M的buffer,最好在INF文件里提前进行预分配,不然很容易分配失败。
  2. WDC_DMASynCpu()和WDC_DMASyncIo()用来刷新缓存,防止出现缓存和内存上数据不一致。MyDMAProgram()用来设计板卡的DMA模块相关的寄存器;MyDMAStart()用来告知设备开始启动DMA传输;MyDMAInterruptEnable()用来设置DMA传输完成后发出的中断,也可以改为轮询方式;MyDMAWaitForCompletion()用来等待传输完成;其实这几个自定义的函数不用每一个都会使用到的,可以根据自己需求来进行相关的设计。
  3. 如果想分配大于1M的内存buffer,需要提前在INF文件里进行预分配,在INF文件的[UpdateRegistryDevice]进行如下配置。以下是单向的buffer预分配,分为Host-to-device和Device-to-host,我们只需要修改每一行最后的那个参数,buffer size和DMA flags,另外在使用WDC_DMA_ContigBufLock函数之前,dwOptions需要和这里的DMA flags保持一致才行。同时在执行分配函数的时候,如果事先打开了Debug Monitor软件,buffer分配成功了会打印出 using preallocated buffer字样。
; Host-to-device DMA buffer:
HKR,,"DmaToDeviceBytes",0x00010001,0x100000   ; Buffer size, in bytes
HKR,,"DmaToDeviceOptions",0x00010001,0x41     ; DMA flags (0x40=DMA_TO_DEVICE; + 0x1=DMA_KERNEL_BUFFER_ALLOC)
; Device-to-host DMA buffer:
HKR,,"DmaFromDeviceBytes",0x00010001,0x100000 ; Buffer size, in bytes
HKR,,"DmaFromDeviceOptions",0x00010001,0x21   ; DMA flags (0x20=DMA_FROM_DEVICE; + 0x1=DMA_KERNEL_BUFFER_ALLOC)
  1. windriver下实现DMA传输就是这样,当然如果想实现更复杂的功能,还需各位自行设计,User’s Manual手册基本上都讲到了,参照手册来就行了;之后可能再会讲一些windriver里的操作方法。

驱动程序(9) 利用Windriver修改代码实现DMA传输定制功能的驱动程序相关推荐

  1. STM32单片机实现DMA+ADC+UART功能

    突然想测试一下STM32单片机ADC采样速率问题,按照常规方法,可以通过ADC采样,然后将采样值打印出来.但是这种方法在处理和打印数据的时候会占用很多时间,导致处理数据的时间超过了ADC的采样时间.于 ...

  2. Windows驱动——利用WinDriver开发PCI设备驱动程序

    摘要 WinDriver是Jungo公司出版的一个设备驱动程序开发组件,它可以大大加速PCI设备驱动程序的开发.作者在实际的项目中采用了WinDriver来开发设备驱动程序,取得了相当好的运行效果.从 ...

  3. 驱动程序(10) Windriver实现DMA传输时分配超大内存Buffer的办法

    前言 利用windriver做DMA传输的时候,尤其是将数据从板卡传输到PC端时,往往需要分配内存buffer.windriver给了两种方法,Contiguous buffer模式(WDC_DNAC ...

  4. 利用Word域代码实现将形如“图一-1”的题注修改为“图1-1”

    利用Word域代码实现将形如"图一-1"的题注修改为"图1-1" 憧憬少 欢迎访问我的个人博客:yxchangingself.xyz 42 人赞同了该文章 目录 ...

  5. 银联在线支付---利用测试案例代码模拟支付应用(修改)

    一.工程搭建 新建一个Web工程,命名为PayOnLine,把你下载好的案例代码拷贝到你的工程下,我的代码目录如下: acp_sdk.properties配置文件需要放在类根路劲下,里面的参数配置信息 ...

  6. 修改代码150万行!Apache Flink 1.9.0做了这些重大修改!(附链接)

    来源:阿里技术 本文约4100字,建议阅读8分钟. 本文为你介绍 Flink 1.9.0 中非常值得关注的重要功能与特性. [ 导读 ] 8月22日,Apache Flink 1.9.0 正式发布.早 ...

  7. 修改代码的艺术----- 2.2 高层测试 2.3 测试覆盖

    2.2  高层测试 单元测试的确很棒,但高层测试也有其一席之地.所谓高层测试便是那些覆盖了某个应用中的场景和交互的测试.高层测试可以用来一下子就确定一组类的行为.能够这样做往往就意味着你可以更容易地为 ...

  8. 利用反射修改final数据域

    当final修饰一个数据域时,意义是声明该数据域是最终的,不可修改的.常见的使用场景就是eclipse自动生成的serialVersionUID一般都是final的. 另外还可以构造线程安全(thre ...

  9. git编辑器选哪个_[Git]Git创建和修改代码库

    有了上一篇的简易Git使用指南,接下来我们就可以创建自己的代码库了 创建代码库 1.把文件夹变成git文件夹 git init 这样子就这里面的内容就可以git了 2.把要git的文件放入暂存区 gi ...

最新文章

  1. 多快好省的宏基因组研究技巧
  2. Python进阶-----property用法(实现了get,set,delete三种方法)
  3. 页面间的跳转,打开,关闭小技巧.
  4. 令牌桶限流之redis-cell的安装,使用,详解
  5. 【中部武汉】理想离家并不遥远
  6. 使用CloudIDE搭建简单java环境
  7. 20200814:力扣201周周赛题解记录上
  8. 反思设计——从大师身上反思
  9. centOS6和centOS7网卡重启方法,以及关闭防火墙的方法
  10. 情侣天气推送升级简单版 项目上传github实现定时自动推送教程
  11. 机器学习 -- PCA(Ⅱ 梯度上升法解决主成分分析问题)
  12. Java身份证处理工具
  13. EXT combobox赋值
  14. Twitter API
  15. 数据仓库,数据集市,数据湖
  16. android手机(平板)下载文件后,在文件管理软件中可以看到,通过mtp模式连接电脑后,无法在电脑上看到
  17. 数据库设计(一) 需求分析
  18. 用莫比乌斯带巧解内接矩形问题:拓扑学的用处
  19. ZED2相机同步订阅rgb与depth话题用于室内场景分析
  20. vscode 插件-better comments-代码注释高亮

热门文章

  1. 模块DIY——基于DDS直接数字频率合成技术自制的可编程任意波形发生器模块(DDS原理、寄存器解读、原理图设计、驱动程序-适用于AD9833/AD9834/AD9838)
  2. 使用宝塔面板快速部署Django项目
  3. [数据分析师]数据分析看中国展览业的数字化应用发展
  4. 省级面板数据(2000-2019)九:城市发展(土地面积+交通+供水+基础设施)(stata版)
  5. MATLAB图像 空间频率,【QA in MRI】5.1.4 Spatial Frequencies 空间频率
  6. Proteus8仿真:51单片机IrLink红外发送加接受模块的使用
  7. 在python3中、下列输出变量a的正确写法是_大学生安全教育网课答案智慧树2020
  8. 绝路之后,人人网还有三条去路
  9. 「Cpolar」看我如何实现公网远程控制Mac OS【使用mac自带VNC】
  10. PyQt5 QCalendarWidget日历控件