[转]windows驱动开发-基于WDF的Altera PCIe DMA驱动
版权声明:本文为CSDN博主「黑客三遍猪」的原创文章,遵循CC 4.0 by-sa版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/Zhu_Zhu_2009/article/details/80790252
Altera pcie-avmm dma IP寄存器
DMA Descriptor Controller Registers
DMA控制器读写均支持最多128个描述符,读写操作是以FPGA视角来看,读操作是从PCIe地址空间到FPGA Avalon-MM地址空间,写操作是从FPGA Avalon-MM地址空间到PCIe地址空间。
在DMA控制器寄存器里设置描述符表位于在PCIe地址空间里的地址和大小,DMA控制器用Read Data Mover首先将描述符复制到自己内部的FIFO中,然后在根据描述符来开始DMA传输。描述符在RC内的地址必须是32字节对齐的。
DMA控制器有寄存器指示读写描述符的完成状态,读和写分别有自己的状态寄存器表,每个表有128个连续的DWORD项,对应128个描述符。状态字占用512字节,位置在RC Read Status and Descriptor Base指定的地址偏移0处,而实际的描述符在0x200偏移处,DMA控制器项状态字的done位写1表示传输成功,DMA控制器在完成最后一个描述符后会发送一个MSI中断,在接收到中断之后,主机host软件可以轮询done位来判断描述符状态,但是DMA控制器不会设置done位或者发送MSI在每一个描述符完成的时候,它根据RD_DMA_LAST PTR和WR_DMA_LAST_PTR寄存器存储的描述符ID来操作,由于描述符支持PCIe完成包的乱序传输,所以done位置位的时候,描述符可能还没有传输完成。例如想在128个描述符的传输中间时刻和完成时候获得通知:
1、写入RD_DMA_LAST_PTR值63。
2、写入RD_DMA_LAST_PTR值127。
3、轮询第63个状态字。
4、轮询第127个状态字。
Read DMA Descriptor Controller Registers
地址偏移 | 寄存器 | 访问权限 | 描述 |
0x00 | RC Read Status and Descriptor Base (Low) | RW | 低32位,在设置高32位之后设置,地址必须32位对齐,在传输完成的时候才能改变这个寄存器 |
0x04 | RC Read Status and Descriptor Base (High) | RW | 高32位 |
0x08 | EP Read Descriptor FIFO Base (Low) | RW | 低32位,指定存储描述符的FIFO地址,在设置高32位之后设置 |
0x0C | EP Read Descriptor FIFO Base (High) | RW | 高32位 |
0x10 | RD_DMA_LAST_PTR | RW | 读返回上次操作的描述符ID,如果没有DMA操作则返回0xFF,指定最后一个操作的描述符ID发起DMA,比如,读返回4,为了传输5个描述符,软件应该写入9 |
0x14 | RD_TABLE_SIZE | RW | 设置读描述符表的大小,值为描述符数量减1,默认为127 |
0x18 | RD_CONTROL | RW | 高31位保留,第0位设置上报每一个描述符的done位,但MSI都不会每次上报,否则根据RD_DMA_LAST_PTR来上报 |
Write DMA Descriptor Controller Registers
和读一样,地址偏移在0x100。
Read DMA and Write DMA Descriptor Format
每个描述符32字节,Read/Write Status and Descriptor Base + 0x200处。
地址偏移 | 寄存器 | 描述 |
0x00 | RD_LOW_SRC_ADDR | 低32位,DMA源地址,PCIe地址空间 |
0x04 | RD_HIGH_SRC_ADDR | 高32位 |
0x08 | RD_CTRL_LOW_DEST_ADDR | 低32位,Avalon-MM地址空间 |
0x0C | RD_CTRL_HIGH_DEST_ADDR | 高32位 |
0x10 | CONTROL |
[31:25] Reserved,必须为0 |
0x14~0x1C | Reserved | N/A |
windows DMA编程
分配Descriptor内存,
1 NTSTATUS status = WdfCommonBufferCreate(engine->parentDevice->dmaEnabler, bufferSize, 2 WDF_NO_OBJECT_ATTRIBUTES, &engine->descBuffer); 3 if (!NT_SUCCESS(status)) { 4 TraceError(DBG_INIT, "WdfCommonBufferCreate failed: %!STATUS!", status); 5 return status; 6 } 7 8 PHYSICAL_ADDRESS descBufferLA = WdfCommonBufferGetAlignedLogicalAddress(engine->descBuffer); 9 PUCHAR descBufferVA = (PUCHAR)WdfCommonBufferGetAlignedVirtualAddress(engine->descBuffer); 10 RtlZeroMemory(descBufferVA, bufferSize);
申请MSI/MSI-X中断,
1 NTSTATUS status = WdfInterruptCreate(xdma->wdfDevice, &config, &attribs, 2 &(xdma->channelInterrupts[index])); 3 if (!NT_SUCCESS(status)) { 4 TraceError(DBG_INIT, "WdfInterruptCreate failed: %!STATUS!", status); 5 }
申请DMA,这里设置了ADMA_MAX_TRANSFER_SIZE,
1 WdfDeviceSetAlignmentRequirement(adma->wdfDevice, FILE_32_BYTE_ALIGNMENT); //add by zhuce 2 WDF_DMA_ENABLER_CONFIG dmaConfig; 3 WDF_DMA_ENABLER_CONFIG_INIT(&dmaConfig, WdfDmaProfileScatterGather64Duplex, ADMA_MAX_TRANSFER_SIZE); 4 status = WdfDmaEnablerCreate(adma->wdfDevice, &dmaConfig, WDF_NO_OBJECT_ATTRIBUTES, &adma->dmaEnabler); 5 if (!NT_SUCCESS(status)) { 6 TraceError(DBG_INIT, " WdfDmaEnablerCreate() failed: %!STATUS!", status); 7 return status; 8 }
申请读/写队列,
1 WDF_IO_QUEUE_CONFIG_INIT(&config, WdfIoQueueDispatchSequential); 2 config.EvtIoWrite = EvtIoWriteDma; 3 //config.EvtIoRead = EvtIoReadDma; 4 WDF_OBJECT_ATTRIBUTES_INIT(&attribs); 5 attribs.SynchronizationScope = WdfSynchronizationScopeQueue; 6 WDF_OBJECT_ATTRIBUTES_SET_CONTEXT_TYPE(&attribs, QUEUE_CONTEXT); 7 status = WdfIoQueueCreate(device, &config, &attribs, queue); 8 if (!NT_SUCCESS(status)) { 9 TraceError(DBG_INIT, "WdfIoQueueCreate failed %d", status); 10 return status; 11 }
发起DMA,ADMA_EngineProgramDma作为回调函数,超过ADMA_MAX_TRANSFER_SIZE,传输会被分多次,WDF为我们做了很多工作,这些我们都是看不到的。
1 status = WdfDmaTransactionInitializeUsingRequest(queue->engine->dmaTransaction, Request, 2 ADMA_EngineProgramDma, 3 WdfDmaDirectionReadFromDevice); 4 if (!NT_SUCCESS(status)) { 5 TraceError(DBG_IO, "WdfDmaTransactionInitializeUsingRequest failed: %!STATUS!", 6 status); 7 goto ErrExit; 8 } 9 status = WdfDmaTransactionExecute(queue->engine->dmaTransaction, queue->engine); 10 if (!NT_SUCCESS(status)) { 11 TraceError(DBG_IO, "WdfDmaTransactionExecute failed: %!STATUS!", status); 12 goto ErrExit; 13 }
回调函数,填入描述符表。
1 for (ULONG i = 0; i < SgList->NumberOfElements; i++) { 2 descriptor[i].control = XDMA_DESC_MAGIC; 3 descriptor[i].numBytes = SgList->Elements[i].Length; 4 ULONG hostAddrLo = SgList->Elements[i].Address.LowPart; 5 LONG hostAddrHi = SgList->Elements[i].Address.HighPart; 6 if (Direction == WdfDmaDirectionWriteToDevice) { 7 // source is host memory 8 descriptor[i].srcAddrLo = hostAddrLo; 9 descriptor[i].srcAddrHi = hostAddrHi; 10 descriptor[i].dstAddrLo = LIMIT_TO_32(deviceOffset); 11 descriptor[i].dstAddrHi = LIMIT_TO_32(deviceOffset >> 32); 12 } else { 13 // destination is host memory 14 descriptor[i].srcAddrLo = LIMIT_TO_32(deviceOffset); 15 descriptor[i].srcAddrHi = LIMIT_TO_32(deviceOffset >> 32); 16 descriptor[i].dstAddrLo = hostAddrLo; 17 descriptor[i].dstAddrHi = hostAddrHi; 18 } 19 }
转载于:https://www.cnblogs.com/tubujia/p/11430213.html
[转]windows驱动开发-基于WDF的Altera PCIe DMA驱动相关推荐
- windows驱动开发-基于WDF的Altera PCIe DMA驱动
作者 QQ群:852283276 微信:arm80x86 微信公众号:青儿创客基地 B站:主页 https://space.bilibili.com/208826118 FPGA设计 参考我的博客:A ...
- STM32MP157驱动开发——Linux自带的LED灯驱动
STM32MP157驱动开发--Linux自带的LED灯驱动 0.前言 一.Linux 内核自带 LED 驱动使能 二.驱动简介 1.LED灯驱动框架分析 2.module_platform_driv ...
- linux驱动开发篇(三)—— 总线设备驱动模型
linux系列目录: linux基础篇(一)--GCC和Makefile编译过程 linux基础篇(二)--静态和动态链接 ARM裸机篇(一)--i.MX6ULL介绍 ARM裸机篇(二)--i.MX6 ...
- 基于WDF的PCI/PCIe接口卡Windows驱动程序(3)- 驱动程序代码(头文件)
原文出处:http://www.cnblogs.com/jacklu/p/4679304.html 如果你觉得这篇博客对你的项目有用,请引用以下论文: Meng Shengwei, Lu Jianji ...
- linux驱动开发音频设备驱动,linux驱动开发—基于Device tree机制的驱动编写
摘要:媒介 Device Tree是一种用去描绘硬件的数据布局,类似板级描绘说话,发源于OpenFirmware(OF).正在现在遍及应用的kernel 2.6.x版本中,对分歧仄台.分歧硬件,往] ...
- OpenHarmony HDF LED驱动开发 基于小熊派Micro
文章目录 一.效果展示 二.led控制程序 2.1.led驱动程序 2.1.1.驱动程序 2.1.2.驱动配置 2.2.C应用程序 2.3.JS应用 2.3.1.JS代码 2.3.2.C++接口 一. ...
- Linux字符型驱动开发—基于友善之臂2416开发板
驱动程序(Device Driver)是一种可以使计算机和设备通信的特殊程序,相当于内核和硬件之间的接口,操作系统只能通过这个接口,才能控制硬件设备的工作.驱动程序接受上层软件(应用程序.内核)的请求 ...
- [linux驱动开发] 基于gpiod API的platform总线多个led驱动开发
gpiod API对platform-led进行驱动开发 修改设备树源码 如何在驱动中获取设备树节点信息 计算设备子节点数量 给私有属性分配内存 对子节点进行遍历 gpiod的获取 根据设备树字节给的 ...
- Linux 下wifi 驱动开发(三)—— SDIO接口WiFi驱动浅析
SDIO-Wifi模块是基于SDIO接口的符合wifi无线网络标准的嵌入式模块,内置无线网络协议IEEE802.11协议栈以及TCP/IP协议栈,能够实现用户主平台数据通过SDIO口到无线网络之间的转 ...
最新文章
- char* p = 123,字符串在内存中的哪个位置?
- Java中的构造函数和重载
- 百练OJ:2965:玛雅历
- pandas算加权平均值_4000 字详解TCP超时与重传,看完没收获算我输
- 布隆过滤器(Bloom Filter)的原理和实现
- 2020/5/13号单词
- CentOS 7.2.1511 x64下载地址
- mariadb数据库增删改查
- 第九篇:Spring Boot整合Spring Data JPA_入门试炼04
- 计算机设计大赛国奖作品_4. 界面设计
- 语义信息增强的激光雷达SLAM
- 小勇机器人充电头坏了_崇安区管道机器人CCTV-管道爬行机器人,武汉天仪ty333,优质效率高...
- xmind电脑版免费_有哪些免费好用的电脑版剪辑软件?
- 耐福-NTP8849音频功放芯片有哪些功能?
- java护眼的颜色_爱护眼睛,从IDEA开始,护眼色设置走起-护眼设置
- 使用企业微信做微信消息通知
- 今天是星期五,上班已经三个礼拜了
- 沉默的大多数(王小波)
- 简单教学管理系统画E-R关系图
- controller 之@...
热门文章
- 物联网国赛LORA模块开发基础教程(通用库)—输出(LED)
- SUN 和Oracle公司
- 信息系统工程监理单位资质管理办法
- Eureka 集群启动报错
- oracle exp lrm00109,oracle11g ORA-01078 LRM-00109错误
- c 语言cad 二次开发,cad编程语言(AUTO CAD 得二次开发语言及工具)
- 5天学会Linux(实操练手+最全教程) Day1 环境搭建
- 打印机驱动程序无法使用怎么办?
- 数据分析师不能不知道的5种数据分析方法,解决90%分析难题!
- 济宁中考计算机考试试题,济宁市初中信息技术考试模拟题1.pdf