赞
踩
前面描述了DMA技术中适配器相关的部分以及DMA的分类,接下来看一下系统具体在支持两种DMA时候的操作的细微差别。
此处解释一下Scatter/Gather,这个也翻译为散点/收集,是指指示设备能够读取或写入内存中的任何区域,而不仅仅是特定范围。在早期,DMA读写的区域必须是一个整页面,但是这种要求其实对于内核来说,不是每时每刻都可以满足的,故和MDL类似,系统也支持将不在同一个页面上N个数据块映射为一整个页面。
使用基于数据包的 DMA 的从属设备的驱动程序在处理请求 DMA 传输的 IRP 时调用以下常规支持例程序列:
IoGetDmaAdapter 返回的适配器对象指针是每个例程的必需参数,KeFlushIoBuffers 和 MmGetMdlVirtualAddress 除外,后者需要指向在 Irp->MdlAddress 传递的 MDL 的指针。
各个驱动程序在不同点调用此支持例程序列,具体取决于实现每个驱动程序以为其设备提供服务的方式。 例如,一个驱动程序的 StartIo 例程可能会调用 AllocateAdapterChannel,另一个驱动程序可能会从从驱动程序创建的互锁队列中删除 IRP 的例程发出此调用,另一个驱动程序可能在其从属 DMA 设备指示它已准备好传输数据时发出此调用。
为了准备基于数据包的系统 DMA,驱动程序在收到IRP_MJ_READ或IRP_MJ_WRITE请求后调用 KeFlushIoBuffers 和 AllocateAdapterChannel。
在驱动程序调用这些例程之前,其 DispatchRead 或 DispatchWrite 例程 (或其他处理 DMA 传输) 的调度例程应已检查 IRP 参数的有效性。 调度例程还可能已将 IRP 排到另一个驱动程序例程进行进一步处理。
调用 AllocateAdapterChannel 的 驱动程序例程必须在 IRQL=DISPATCH_LEVEL执行。 除了指向 IoGetDmaAdapter 返回的适配器对象的指针之外,驱动程序还必须在调用 AllocateAdapterChannel 时提供以下内容:
AllocateAdapterChannel 将驱动程序的 AdapterControl 例程排队,该例程在系统 DMA 控制器分配给此驱动程序时运行,并且已为驱动程序的 DMA 操作分配了一组 映射寄存器。
输入时,AdapterControl 例程接收在对 AllocateAdapterChannel 的调用中传递的 DeviceObject 和 Context 指针,以及分配的映射寄存器 (MapRegisterBase) 句柄。
如果驱动程序具有 StartIo 例程,AdapterControl 例程还会收到指向 DeviceObject->CurrentIrp 的指针。 如果驱动程序管理自己的 IRP 队列,而不是使用StartIo 例程,则驱动程序应包含指向当前 IRP 的指针,作为它在调用 AllocateAdapterChannel 时传递的上下文的一部分。
AdapterControl 例程通常执行以下操作:
每个 AdapterControl 例程都必须返回 IO_ALLOCATION_ACTION类型的系统定义值。 对于使用系统 DMA 的驱动程序, AdapterControl 例程必须返回值 KeepObject。 这允许驱动程序保留系统 DMA 控制器和分配的映射寄存器的所有权,直到传输了所有请求的数据。
由于 AdapterControl 例程无法等待从属设备执行 DMA 操作,因此每个 AdapterControl 例程至少必须执行以下操作:
另一个驱动程序例程 (可能是 DpcForIsr 例程) 必须在每个 DMA 传输操作完成时调用 FlushAdapterBuffers 。 如果需要多次设置 DMA 控制器以满足当前 IRP 的传输请求,此例程还必须再次调用 MapTransfer 和 FlushAdapterBuffers 。
当驱动程序满足当前 IRP 的请求时,它必须调用 FreeAdapterChannel。 此支持例程应在上次调用当前 IRP 的 FlushAdapterBuffers 之后立即调用,以便系统 DMA 控制器可供任何驱动程序 迅速满足其他传输请求。
具有Scatter/Gather功能的从属设备的驱动程序还应从其 AdapterControl 例程返回 KeepObject。 当驱动程序必须拆分给定 DMA 请求时,当系统 DMA 控制器在 DMA 操作之间重新编程时,设备必须能够等待。 在某些 Windows 平台上,此类设备每次 DMA 操作最多可以传输一页数据,因为 HAL 只能将单个映射寄存器分配给该设备的驱动程序。
当 AllocateAdapterChannel 将控制权转移到驱动程序的 AdapterControl 例程时,驱动程序将“拥有”系统 DMA 控制器和一组映射寄存器。 然后,驱动程序必须为传输操作设置 DMA 控制器,如下图所示:
如果驱动程序具有 StartIo 例程,则 AllocateAdapterChannel 会将 PIrp 参数中指向 DeviceObject-CurrentIrp> 的指针传递到 AdapterControl 例程。 但是,如果驱动程序管理自己的 IRP 队列,则驱动程序应包含指向当前 IRP 的指针,作为它传递给 AdapterControl 的上下文的一部分。
如上图所示,驱动程序的 AdapterControl 例程设置 DMA 传输,如下所示:
1. AdapterControl 例程获取开始传输的地址。 对于满足 IRP 所需的初始传输, AdapterControl 例程调用 MmGetMdlVirtualAddress,将指针传递到 Irp->MdlAddress 处的 MDL,该指针描述此 DMA 传输的缓冲区;MmGetMdlVirtualAddress 返回一个虚拟地址,驱动程序可以使用该地址作为应开始传输的系统物理地址的索引;如果 IRP 需要多个传输操作,驱动程序将计算更新的起始地址,如后面所述。
2. AdapterControl 例程保存 MmGetMdlVirtualAddress 返回或在步骤 1 中计算的地址。 此地址是 MapTransfer (CurrentVa) 的必需参数。
3. AdapterControl 例程调用 MapTransfer 来设置系统 DMA 控制器,并提供以下参数:
4. MapTransfer 返回逻辑地址,使用系统 DMA 的驱动程序必须忽略此值;
5. AdapterControl 例程为 DMA 操作设置设备;
6. AdapterControl 例程返回 KeepObject;
当设备指示其当前 DMA 操作已完成时,驱动程序应调用 FlushAdapterBuffers,通常是从驱动程序的 DpcForIsr 例程调用。
完成 DMA 操作的 DpcForIsr 例程或其他驱动程序例程调用 FlushAdapterBuffers,以确保将系统 DMA 控制器中缓存的任何数据读入系统内存或写出到设备。 如果需要重新编程系统 DMA 控制器以便为当前 IRP 传输更多数据,则同一例程还必须再次调用 MapTransfer ;同样它必须在每次传输操作后再次调用 FlushAdapterBuffers 。
如果驱动程序必须多次为当前 IRP 调用 MapTransfer ,它将在每次调用中提供相同的适配器对象指针、 Mdl 指针、 MapRegisterBase 句柄和传输方向。 但是,驱动程序必须先更新 CurrentVa 和 Length 参数,然后才能对 MapTransfer 进行第二次和任何后续调用。 若要计算其中每个参数的更新值,请使用以下公式:
每个驱动程序应维护的有关其 DMA 传输的上下文信息取决于其特定设备的需求。 典型上下文可能包括 MDL (CurrentVa) 中的当前虚拟地址、到目前为止传输的字节数、要传输的剩余字节数、可能指向当前 IRP 的指针,以及驱动程序编写者认为有用的任何其他信息。
当请求的传输完成时,或者如果驱动程序必须返回 IRP 的错误状态,则驱动程序应立即调用 FreeAdapterChannel ,以释放系统 DMA 控制器以供其他驱动程序和此驱动程序使用。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。