码迷,mamicode.com
首页 > 其他好文 > 详细

WDF DMA 一些概念(一)

时间:2016-05-12 16:53:49      阅读:758      评论:0      收藏:0      [点我收藏+]

标签:

   
cache一致性

On machines or bus configurations inwhich the hardware does not ensure cache coherence for DMA operations—such ascertain Intel Itanium systems—the standard Windows DMA implementation does theprocess-specific work that is necessary to ensure such coherency when the driver calls WdfDmaTransactionExecute.

The framework flushes this cache when the driver callsWdfDmaTransactionDmaCompleted.

Map寄存器

在系统地址空间和物理地址空间 翻译地址。类似于页表项和页目录项用于物理地址和虚拟地址之间的转换一样。每个map寄存器可以转换高达4KB的地址。Map寄存器是一种共享资源。系统根据创建DMA enable 对象时的最大传输长度reserves map寄存器。

框架会在执行transaction事务前立即分配map registers,在transaction完成后释放。一旦DMA设备的驱动程序从或者向系统内存拷贝数据,框架会动态分配 以及编程map registers.

 

Number of Required Map Registers =(Transfer Size / PAGE_SIZE) + 1

加1是针对一次传输的起始地址不是页对齐地址。map registers被分配在一个连续的块中。Windows操作系统将一块map registers (基地址和长度)给框架。其值 KMDF驱动无法获知。

 举个例子:

PLX9x5x的驱动中在初始化设备扩展结构中有如下代码:

NTSTATUS
PLxInitializeDeviceExtension(
    IN PDEVICE_EXTENSION DevExt
    )
/*++
Routine Description:

    This routine is called by EvtDeviceAdd. Here the device context is
    initialized and all the software resources required by the device is
    allocated.

Arguments:

    DevExt     Pointer to the Device Extension

Return Value:

     NTSTATUS

--*/
{
    NTSTATUS    status;
    ULONG       dteCount;
    WDF_IO_QUEUE_CONFIG  queueConfig;

    PAGED_CODE();

    //
    // Set Maximum Transfer Length (which must be less than the SRAM size).
    //
    DevExt->MaximumTransferLength = PCI9656_MAXIMUM_TRANSFER_LENGTH;
    if(DevExt->MaximumTransferLength > PCI9656_SRAM_SIZE) {
        DevExt->MaximumTransferLength = PCI9656_SRAM_SIZE;
    }

	KdPrint((
                "MaximumTransferLength %d\n", DevExt->MaximumTransferLength));

    //
    // Calculate the number of DMA_TRANSFER_ELEMENTS + 1 needed to
    // support the MaximumTransferLength.
    //
    dteCount = BYTES_TO_PAGES((ULONG) ROUND_TO_PAGES(
        DevExt->MaximumTransferLength) + PAGE_SIZE);

    KdPrint((  "Number of DTEs %d\n", dteCount));

    //
    // Set the number of DMA_TRANSFER_ELEMENTs (DTE) to be available.
    //
    DevExt->WriteTransferElements = dteCount;
    DevExt->ReadTransferElements  = dteCount;
}


有用硬件实现的,也有用软件实现的。

什么时候用map registers? 满足下列任一项

1 设备不支持S/G

2 buffer已经超越了设备的寻址能力,如只支持32位传输,但是却要传到高4GB的物理地址空间,系统会使用map registers.

技术分享

SG的实现

在初始化过程中,如果设备不支持硬件SG,Windows DMA 使用map registers.

1 Windows DMA分配足够连续的map registers,即low-memory 缓冲区,包含整个传输的数据;

2 对一个写操作来说,从系统内存到设备,数据从原有的数据缓冲区到map registers缓冲区。

3 WIndows DMA 给框架提供map registers的物理内存地址 作为 缓冲区在设备总线地址空间的地址。

然后 框架吧sg list的地址传送给驱动程序,当调用驱动的EvtProgramDma 回调函数时。

BOOLEAN
PLxEvtProgramWriteDma(
    IN  WDFDMATRANSACTION       Transaction,
    IN  WDFDEVICE               Device,
    IN  PVOID                   Context,
    IN  WDF_DMA_DIRECTION       Direction,
    IN  PSCATTER_GATHER_LIST    SgList
    )
/*++

Routine Description:

Arguments:

Return Value:

--*/
{
    PDEVICE_EXTENSION        devExt;
    size_t                   offset;
    PDMA_TRANSFER_ELEMENT    dteVA;
    ULONG_PTR                dteLA;
    BOOLEAN                  errors;
    ULONG                    i;

    UNREFERENCED_PARAMETER( Context );
    UNREFERENCED_PARAMETER( Direction );

	KdPrint(("--> PLxEvtProgramWriteDma\n"))
                ;

    //
    // Initialize locals
    //
    devExt = PLxGetDeviceContext(Device);
    errors = FALSE;

    //
    // Get the number of bytes as the offset to the beginning of this
    // Dma operations transfer location in the buffer.
    //
    offset = WdfDmaTransactionGetBytesTransferred(Transaction);
	KdPrint(("offset is %d\n",offset));
    //
    // Setup the pointer to the next DMA_TRANSFER_ELEMENT
    // for both virtual and physical address references.
    //
    dteVA = (PDMA_TRANSFER_ELEMENT) devExt->WriteCommonBufferBase;
    dteLA = (devExt->WriteCommonBufferBaseLA.LowPart +
                        sizeof(DMA_TRANSFER_ELEMENT));

    //
    // Translate the System's SCATTER_GATHER_LIST elements
    // into the device's DMA_TRANSFER_ELEMENT elements.
    //
    for (i=0; i < SgList->NumberOfElements; i++) {

        //
        // Construct this DTE.
        //
        // NOTE: The LocalAddress is the offset into the SRAM from
        //       where this Write will start.
        //
        dteVA->PciAddressLow  = SgList->Elements[i].Address.LowPart;
        dteVA->PciAddressHigh = SgList->Elements[i].Address.HighPart;
        dteVA->TransferSize   = SgList->Elements[i].Length;

        dteVA->LocalAddress   = (ULONG) offset;

        dteVA->DescPtr.DescLocation  = DESC_PTR_DESC_LOCATION__PCI;
        dteVA->DescPtr.TermCountInt  = FALSE;
        dteVA->DescPtr.LastElement   = FALSE;
        dteVA->DescPtr.DirOfTransfer = DESC_PTR_DIRECTION__TO_DEVICE;
        dteVA->DescPtr.Address       = DESC_PTR_ADDR( dteLA );

        //
        // Increment the DmaTransaction length by this element length
        //
        offset += SgList->Elements[i].Length;

        //
        // If at end of SgList, then set LastElement bit in final NTE.
        //
        if (i == SgList->NumberOfElements - 1) {

            dteVA->DescPtr.LastElement = TRUE;
}
}


 因为 缓冲区是物理连续的,所以SG表只有一个元素,基地址和长度。

 驱动使用该长度和地址,其实不是真实的数据缓冲区片段的地址编程DMA。因为map register buffers 在低4GB 物理地址空间,所以该buffers可以被任何

总线主控DMA设备寻址到。

当驱动程序通知框架DMA传输已经完成,框架转而通知standard Windows DMA实现层,

map register可以使具有有限寻址能力的设备访问到任一内存位置。

 

 

 

WDF DMA 一些概念(一)

标签:

原文地址:http://blog.csdn.net/christne1225i/article/details/51360846

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!