失眠网,内容丰富有趣,生活中的好帮手!
失眠网 > windows驱动开发-基于WDF的Altera PCIe DMA驱动

windows驱动开发-基于WDF的Altera PCIe DMA驱动

时间:2019-09-12 08:41:04

相关推荐

windows驱动开发-基于WDF的Altera PCIe DMA驱动

作者

QQ群:852283276

微信:arm80x86

微信公众号:青儿创客基地

B站:主页/208826118

FPGA设计

参考我的博客:Altera FPGA PCIe Avalon-MM DMA设计

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个描述符的传输中间时刻和完成时候获得通知:

写入RD_DMA_LAST_PTR值63。写入RD_DMA_LAST_PTR值127。轮询第63个状态字。轮询第127个状态字。

Read DMA Descriptor Controller Registers

Write DMA Descriptor Controller Registers

和读一样,地址偏移在0x100。

Read DMA and Write DMA Descriptor Format

每个描述符32字节,Read/Write Status and Descriptor Base + 0x200处,

windows DMA编程

分配Descriptor内存,

NTSTATUS status = WdfCommonBufferCreate(engine->parentDevice->dmaEnabler, bufferSize,WDF_NO_OBJECT_ATTRIBUTES, &engine->descBuffer);if (!NT_SUCCESS(status)) {TraceError(DBG_INIT, "WdfCommonBufferCreate failed: %!STATUS!", status);return status;}PHYSICAL_ADDRESS descBufferLA = WdfCommonBufferGetAlignedLogicalAddress(engine->descBuffer);PUCHAR descBufferVA = (PUCHAR)WdfCommonBufferGetAlignedVirtualAddress(engine->descBuffer);RtlZeroMemory(descBufferVA, bufferSize);

申请MSI/MSI-X中断,

NTSTATUS status = WdfInterruptCreate(xdma->wdfDevice, &config, &attribs,&(xdma->channelInterrupts[index]));if (!NT_SUCCESS(status)) {TraceError(DBG_INIT, "WdfInterruptCreate failed: %!STATUS!", status);}

申请DMA,这里设置了ADMA_MAX_TRANSFER_SIZE,

WdfDeviceSetAlignmentRequirement(adma->wdfDevice, FILE_32_BYTE_ALIGNMENT); //add by zhuce WDF_DMA_ENABLER_CONFIG dmaConfig;WDF_DMA_ENABLER_CONFIG_INIT(&dmaConfig, WdfDmaProfileScatterGather64Duplex, ADMA_MAX_TRANSFER_SIZE);status = WdfDmaEnablerCreate(adma->wdfDevice, &dmaConfig, WDF_NO_OBJECT_ATTRIBUTES, &adma->dmaEnabler);if (!NT_SUCCESS(status)) {TraceError(DBG_INIT, " WdfDmaEnablerCreate() failed: %!STATUS!", status);return status;}

申请读/写队列,

WDF_IO_QUEUE_CONFIG_INIT(&config, WdfIoQueueDispatchSequential);config.EvtIoWrite = EvtIoWriteDma;//config.EvtIoRead = EvtIoReadDma;WDF_OBJECT_ATTRIBUTES_INIT(&attribs);attribs.SynchronizationScope = WdfSynchronizationScopeQueue;WDF_OBJECT_ATTRIBUTES_SET_CONTEXT_TYPE(&attribs, QUEUE_CONTEXT);status = WdfIoQueueCreate(device, &config, &attribs, queue);if (!NT_SUCCESS(status)) {TraceError(DBG_INIT, "WdfIoQueueCreate failed %d", status);return status;}

发起DMA,ADMA_EngineProgramDma作为回调函数,超过ADMA_MAX_TRANSFER_SIZE,传输会被分多次,WDF为我们做了很多工作,这些我们都是看不到的。

status = WdfDmaTransactionInitializeUsingRequest(queue->engine->dmaTransaction, Request,ADMA_EngineProgramDma,WdfDmaDirectionReadFromDevice);if (!NT_SUCCESS(status)) {TraceError(DBG_IO, "WdfDmaTransactionInitializeUsingRequest failed: %!STATUS!",status);goto ErrExit;}status = WdfDmaTransactionExecute(queue->engine->dmaTransaction, queue->engine);if (!NT_SUCCESS(status)) {TraceError(DBG_IO, "WdfDmaTransactionExecute failed: %!STATUS!", status);goto ErrExit;}

回调函数,填入描述符表。

for (ULONG i = 0; i < SgList->NumberOfElements; i++) {descriptor[i].control = XDMA_DESC_MAGIC;descriptor[i].numBytes = SgList->Elements[i].Length;ULONG hostAddrLo = SgList->Elements[i].Address.LowPart;LONG hostAddrHi = SgList->Elements[i].Address.HighPart;if (Direction == WdfDmaDirectionWriteToDevice) {// source is host memorydescriptor[i].srcAddrLo = hostAddrLo;descriptor[i].srcAddrHi = hostAddrHi;descriptor[i].dstAddrLo = LIMIT_TO_32(deviceOffset);descriptor[i].dstAddrHi = LIMIT_TO_32(deviceOffset >> 32);} else {// destination is host memorydescriptor[i].srcAddrLo = LIMIT_TO_32(deviceOffset);descriptor[i].srcAddrHi = LIMIT_TO_32(deviceOffset >> 32);descriptor[i].dstAddrLo = hostAddrLo;descriptor[i].dstAddrHi = hostAddrHi;}}

如果觉得《windows驱动开发-基于WDF的Altera PCIe DMA驱动》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。