最近在修改一份基于DriverStudio的PCI视频采集卡驱动代码,遇到了关于DMA的使用上的疑惑。
其OnStartDevice初始化了相关资源,代码如下:
PCM_RESOURCE_LIST pResListRaw = I.AllocatedResources();
// Get the list of translated resources from the IRP
PCM_RESOURCE_LIST pResListTranslated = I.TranslatedResources();
// Initialize the device descriptor for the DMA object using the assigned resource
DEVICE_DESCRIPTION dd;
RtlZeroMemory(&dd, sizeof(dd));
dd.Version = DEVICE_DESCRIPTION_VERSION;
dd.Master = TRUE;
dd.ScatterGather = FALSE;
dd.DemandMode = TRUE;
dd.AutoInitialize = FALSE;
dd.Dma32BitAddresses = TRUE;
dd.IgnoreCount = FALSE;
dd.DmaChannel = 3;
dd.InterfaceType = PCIBus;
dd.DmaWidth = Width32Bits; // PCI default width
dd.DmaSpeed = MaximumDmaSpeed;
dd.MaximumLength = m_nDmaBufferSize;
// Initialize the DMA adapter object
m_Dma.Initialize(&dd, m_Lower.TopOfStack());
m_DmaBuffer.Initialize(&m_Dma,m_nDmaBufferSize,true);
// Create an instance of KPciConfiguration so we can map Base Address
// Register indicies to ordinals for memory or I/O port ranges.
KPciConfiguration PciConfig(m_Lower.TopOfStack());
// For each I/O port mapped region, initialize the I/O port range using
// the resources provided by NT. Once initialized, use member functions such as
// inb/outb, or the array element operator to access the ports range.
status = m_IoPortRange0.Initialize(
pResListTranslated,
pResListRaw,
PciConfig.BaseAddressIndexToOrdinal(0)
);
status = m_Irq.InitializeAndConnect(
pResListTranslated,
LinkTo(Isr_Irq),
this
);
if (!NT_SUCCESS(status))
{
Invalidate();
return status;
}
// Setup the DPC to be used for interrupt processing
m_DpcFor_Irq.Setup(LinkTo(DpcFor_Irq), this);
这里初始化了DMA,但是没有绑定PCI设备物理地址与驱动空间地址的相关代码。然而在IoControl中却有以下操作
//写
UCHAR* pDMA = (UCHAR*)m_DmaBuffer.pVirtualAddress() + pCapInfo->nCode*pCapInfo->nSize + pCapInfo->nOffset;
memcpy(pDMA,pCapInfo->pData,pCapInfo->nSize);
//读
UCHAR* pDMA = (UCHAR*)m_DmaBuffer.pVirtualAddress() + pCapInfo->nCode*pCapInfo->nSize + pCapInfo->nOffset;
memcpy(pCapInfo->pData,pDMA,pCapInfo->nSize);
从代码可以看出,直接将m_DmaBuffer当做PCI的内存进行了访问。简单阅读了DriverStudio的封装源代码,初始化过程实际上调用了IoGetDmaAdapter与AllocateCommonBuffer,也就是说m_DmaBuffer.pVirtualAddress()实际上是AllocateCommonBuffer分配的公用缓冲区,而实际上并没有任何地方将该地址与PCI实际的物理地址进行绑定。查阅相关资料,AllocateCommonBuffer一般用于ScatterGather模式,这里使用的是block DMA模式。难道是IoGetDmaAdapter或者AllocateCommonBuffer自动将pVirtualAddress()与PCI的物理地址空间进行了绑定?查询相关资料,也没有这种解释。那究竟为什么能直接通过m_DmaBuffer.pVirtualAddress()操作PCI的物理地址空间呢?
求各位大神解答。