公用缓冲区 DMA?不需要任何转换!
在公用缓冲区 DMA 中,驱动程序和设备共享相同的内存区域用于数据传输。如果驱动程序使用公用缓冲区 DMA,那么分配公用缓冲区只需调用 AllocateCommonBuffer。驱动程序不需要分配连续的内存或将物理地址转换成虚拟地址。事实上,不应该这样做。
用连续的内存分配一个缓冲区,然后将驱动程序用来访问缓冲区的虚拟地址转换成设备可以使用的物理地址,这看起来很合理。但是,直接的虚拟到物理的转换不能解决硬件体系结构之间的差异。例如,32 位编址的 PCI 总线不能寻址到 4Gb 以上的物理内存位置,所以这种技术不是在每种 Windows 平台上都有用。
使用 AllocateCommonBuffer 代替上面的做法,它提供一种简单而正确的方法来获取缓冲区以及独立于硬件的地址。
AllocateCommonBuffer 分配一片专用于 DMA 的内存区域。它返回区域的虚拟基址供驱动程序使用并返回逻辑基址供设备使用。逻辑地址空间独立于底层的硬件,因此可以保证逻辑地址在每种 Windows 平台上都可以使用。在内部实现上,HAL 执行各个机器上所需的地址转换;驱动程序不需要代表设备在逻辑地址和物理地址之间进行转换。
因为 DMA 操作(例如 AllocateCommonBuffer)依赖于底层的系统和总线体系结构,所以它们不能作为 Ntoskrnl.exe 导出的单个例程来实现。这些例程在 HAL 和合适的总线驱动程序中实现,通过由 IoGetDmaAdapter 返回的 DMA 适配器对象进行访问。通过 DMA_OPERATIONS 结构中提供的指针来调用 AllocateCommonBuffer,而 DMA_OPERATIONS 结构是由 IoGetDmaAdapter 返回的 DMA_ADAPTER 结构的一部分。
下列代码段显示驱动程序如何获得这个指针。
// Call IoGetDmaAdapter to get adapter object.DmaAdapterObject = IoGetDmaAdapter(PDO, &deviceDescription, &MapRegisters);
// Save pointers to the commonly used DMA functions in // FdoData, which was previously initialized to point to // the device extension.
FdoData->AllocateCommonBuffer = *DmaAdapterObject->DmaOperations->AllocateCommonBuffer; FdoData->FreeCommonBuffer = *DmaAdapterObject->DmaOperations->FreeCommonBuffer;
下面的例子显示驱动程序如何通过保存的指针来调用例程:
// Allocate a buffer for DMA and store the virtual and // logical addresses in FdoData.
FdoData->BaseVa = FdoData->AllocateCommonBuffer( DmaAdapterObject, FdoData->BufferLength, &FdoData->BaseLogicalAddr, FALSE ); if (FdoData->BaseVa == NULL) { DebugPrint(ERROR, DBG_INIT, "Failed to allocate DMA buffer\n"); status = STATUS_INSUFFICIENT_RESOURCES; break; }驱动程序传递适配器对象、请求的缓冲区的长度和布尔值 FALSE(指示分配的内存不应该被用作缓存)。AllocateCommonBuffer 以参数的形式返回逻辑基址,以函数返回值的形式返回缓冲区的虚拟基址。如果分配失败,那么函数返回 NULL;驱动程序检查返回值,如有必要,设置一个合适的错误状态。
您应该做什么?
| • | 调用 AllocateCommonBuffer 来分配内存并获取公用缓冲区 DMA 的虚拟地址和逻辑地址。 |
| • | 调用 FreeCommonBuffer 来释放 AllocateCommonBuffer 分配的内存和资源。 |
| • | 不要调用 MmAllocateContiguousMemory 来分配缓冲区,也不要调用 MmGetPhysicalAddress 来将虚拟地址转换成用来对 DMA 适配器编程的物理地址。这种方法不适用于所有硬件,设备和驱动程序将无法跨平台兼容。还有可能产生严重的错误。 |
| • | 一些设备类型具有设备类型特定的 DMA 例程。如果您的设备有这些例程,则使用它们。 |
| • | 使用 Driver Verifier 的 DMA 验证选项(在 Windows XP 和更高的发布版本中提供)以及调试器的 !dma 扩展来测试驱动程序的 DMA 实现。 |
更多信息:
Windows DDK:
使用公用缓冲区系统 DMA
使用公用缓冲区 Busmaster DMA
映射寄存器
AllocateCommonBuffer