使用 Common-Buffer System DMA

使用系統 DMA 控制器自動初始化模式的驅動程式必須配置記憶體給可以執行 DMA 傳輸的緩衝區或來源。驅動程式會呼叫 AllocateCommonBuffer 來取得此緩衝區,通常是從 DispatchPnP 常式中處理 IRP_MN_START_DEVICE 要求。 下圖顯示驅動程式如何配置緩衝區,並將其虛擬位址範圍對應至系統實體記憶體。

說明驅動程式如何為系統 dma 配置通用緩衝區的圖表。

如上圖所示,驅動程式會採取下列步驟來配置系統 DMA 的緩衝區:

  1. 驅動程式會呼叫 AllocateCommonBuffer,並將指標傳遞至 IoGetDmaAdapter所傳回的配接器物件,以及為其緩衝區要求的位元組長度。 若要以經濟方式使用記憶體,緩衝區的輸入 Length 值應該小於或等於PAGE_SIZE,或應該是PAGE_SIZE的整數倍數。

  2. 如果 AllocateCommonBuffer 傳回 Null 指標,驅動程式應該釋放它已宣告的任何系統資源,並傳回STATUS_INSUFFICIENT_RESOURCES以回應 IRP_MN_START_DEVICE 要求。

    否則, AllocateCommonBuffer 會在系統虛擬位址空間中配置要求的記憶體數量,並將兩種不同類型的指標傳回給該緩衝區:

    • 上圖中緩衝區的 LogicalAddress (BufferLogicalAddress) ,驅動程式必須提供儲存體,但之後應該忽略

    • 上圖中緩衝區 (BufferVirtualAddress 的虛擬位址) ,驅動程式也必須儲存此位址,以便建置描述 DMA 作業緩衝區的 MDL

    驅動程式應該將這些指標儲存在裝置擴充功能或其他驅動程式配置的駐留記憶體中。

  3. 驅動程式會呼叫 IoAllocateMdl ,為緩衝區配置 MDL。 驅動程式會傳遞AllocateCommonBuffer所傳回之緩衝區的VirtualAddress,以及其緩衝區的Length來配置 MDL。

  4. 驅動程式會使用IoAllocateMdlMdl傳回的指標呼叫MmBuildMdlForNonPagedPool,以將其駐留緩衝區的虛擬位址範圍對應至系統實體記憶體。

配置通用緩衝區並對應其虛擬位址範圍之後,從屬裝置的驅動程式就可以開始處理要求 DMA 傳輸的 IRP。 若要這樣做,驅動程式會呼叫下列一般支援常式:

  1. 在驅動程式寫入器的判斷下, RtlMoveMemory 將資料從鎖定的使用者緩衝區複製到驅動程式配置的通用緩衝區,以傳送至裝置

  2. 當驅動程式準備好為其裝置進行 DMA 程式設計,且需要系統 DMA 控制器時,AllocateAdapterChannel

  3. MapTransfer,其中包含描述驅動程式配置的通用緩衝區的 MDL,以設定傳輸作業的系統 DMA 控制器

    請注意,驅動程式只會呼叫 MapTransfer 一次,以設定系統 DMA 控制器以使用其通用緩衝區。 在傳輸期間,驅動程式可以呼叫 ReadDmaCounter 來判斷要傳輸的位元組數目,如有必要,請呼叫 RtlMoveMemory ,以將資料複製到使用者緩衝區或從使用者緩衝區複製更多資料。

  4. 當驅動程式完成從屬裝置往返的 DMA 傳輸時,FlushAdapterBuffers

  5. 只要傳輸所有要求的資料,或驅動程式因為裝置 I/O 錯誤而必須失敗 IRP,FreeAdapterChannel

IoGetDmaAdapter傳回的配接器物件指標是每個支援常式的必要參數,但RtlMoveMemory除外。

個別驅動程式會根據每個驅動程式如何實作服務其裝置,在不同的時間點呼叫此支援常式序列。 例如,一個驅動程式的 StartIo 常式可能會呼叫 AllocateAdapterChannel,另一個驅動程式可能會從從驅動程式建立的相互鎖定佇列中移除 IRP 的常式進行此呼叫,而另一個驅動程式可能會在其次級 DMA 裝置指出已準備好傳輸資料時進行此呼叫。