zoukankan      html  css  js  c++  java
  • [转]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
    [24:18] ID,描述符ID,0-127
    [17:00] SIZE,传输大小,以DWORD单位,最大传输大小是1MB-4bytes,超过最大传输大小,按最大值传输,否则传输设置值,这个字段最大值为0x40000-0x1

    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     }
  • 相关阅读:
    从锅炉工到AI专家(1)
    【Offer】[47] 【礼物的最大价值】
    【Offer】[46] 【把数字翻译成字符串】
    【Offer】[45]【把数组排成最小的数】
    【Offer】[44] 【数字序列中某一位的数字】
    【Offer】[43] 【1~n整数中1出现的次数】
    【Offer】[42] 【连续子数组的最大和】
    【Offer】[41] 【数据流中的中位数】
    【Offer】[40] 【最小的K个数】
    【Offer】[39] 【数组中出现次数超过一半的数字】
  • 原文地址:https://www.cnblogs.com/tubujia/p/11430213.html
Copyright © 2011-2022 走看看