MapTransferEx 例程會初始化一組先前配置的 DMA 資源,並啟動 DMA 傳輸。 此例程可在 DMA 作業介面的第 3 版中使用。 從 Windows 8 開始,支援此介面的版本 3。 如需 DMA 作業介面的詳細資訊,請參閱 DMA_OPERATIONS。
MapTransferEx 與 MapTransfer 的比較
MapTransferEx 是 MapTransfer 例程的改良版本。 從 Windows 2000 的第 1 版開始,MapTransfer 適用於所有版本的 DMA 作業介面。 一次呼叫MapTransfer 就能從 MDL 對應一個連續的實體記憶體區塊。 不過,複雜 DMA 傳輸的數據緩衝區可能會由 MDL 鏈結描述,而鏈結中的每個 MDL 可能會描述數個實體連續記憶體區塊。 若要使用 MapTransfer 來傳輸這類緩衝區,驅動程式必須多次呼叫 MapTransfer。 一般而言,這些呼叫是在一對巢狀循環內進行。 內部迴圈會從一個連續物理記憶體區塊逐一查看到每個 MDL 中的下一個區塊,而外部迴圈會從一個 MDL 逐一查看到 MDL 鏈結中的下一個區塊。
相反地, 對 MapTransferEx 的呼叫可以傳輸整個數據緩衝區,以進行複雜的 DMA 傳輸。 下列三個 MapTransferEx 參數描述要用於傳輸的緩衝區內存。
| 參數 | 說明 |
|---|---|
| Mdl | |
| 位移 | 緩衝區的位元組位移是指從 MDL 鏈結描述的記憶體開頭開始的偏移量。 |
| 長度 | 包含數據緩衝區長度之位置的指標,以位元組為單位。 |
在 MapTransferEx 呼叫開始時, MapTransferEx 例程會透過 MDL 鏈結前進,以尋找緩衝區的開頭。 緩衝區的開頭是由 Offset 參數指定。 接下來,從緩衝區開頭到結尾, MapTransferEx 會建構散佈/收集清單,其中清單中的每個緩衝區片段都是來自 MDL 鏈結的記憶體實體連續區塊。 若要建構此清單,MapTransferEx 會依次從一個實體連續的記憶體區塊移動到每個 MDL 內的下一個區塊,並從一個 MDL 移動到 MDL 鏈結中的下一個。 當散佈/收集清單所描述的緩衝區內存總數等於 *Length 輸入參數所指定的位元組數目時,清單建構就會完成。 產生的散佈/收集清單中的緩衝區片段順序符合 MDL 鏈結中實體連續區塊的順序。
多次呼叫 MapTransferEx
MapTransferEx 不一定能在一次呼叫中傳輸整個 DMA 數據緩衝區。 下列清單描述了一些可能需要多次呼叫 MapTransferEx 才能完成傳輸的條件:
- DMA 配接器需要地圖緩存器,而指派給配接器的對應緩存器數目不足以描述整個緩衝區。
- 驅動程式配置來包含散佈/收集清單的記憶體不夠大,無法包含整個緩衝區的散佈/收集清單。
- 傳輸會使用系統 DMA 控制器,限制可在硬體散佈/收集清單中指定之緩衝區片段的數目。
在上述所有情況下,MapTransferEx 會在一次呼叫中盡可能地對映數據緩衝區,並告知驅動程式此次呼叫對映了多少緩衝區。 上述清單不包含其他條件,例如平臺特定的快取行為,可能需要多次呼叫 MapTransferEx 才能完成傳輸。 未來的硬體平臺可能會對 DMA 傳輸長度施加額外的限制。 基於這些原因,驅動程式開發人員應該設計其驅動程式,以正確處理 MapTransferEx 無法在一次呼叫中對應整個 DMA 數據緩衝區的情況。
呼叫 MapTransferEx 之前,呼叫端會將 *Length 參數設定為 DMA 數據緩衝區中仍然需要對應的位元組數目。 在傳回之前, MapTransferEx 會將 *Length 設定為呼叫實際對應之緩衝區中的位元組數目。 當 MapTransferEx 呼叫無法對應整個緩衝區長度時,如 *Length 輸入值所指定,*Length 的輸出值會小於其輸入值。 如果 DMA 傳輸需要兩個以上的 MapTransferEx 呼叫,呼叫驅動程式必須先從一個呼叫取得 *Length 輸出值,才能指定下一個呼叫的 *Length 輸入值。
例如,如果 MapTransferEx 呼叫只能將 X 個字節傳送到緩衝區, 而 Offset = B 和 *Length = N (在輸入時),則傳回時為 *Length = X。在下一次呼叫 MapTransferEx 時,驅動程式應該設定 Offset = B + X 和 *Length = N - X。在這兩個呼叫中,都會使用相同的 MDL 鏈結而不修改。
如果呼叫端指定了 DmaCompletionRoutine,那麼 MapTransferEx 將在安排 DmaCompletionRoutine 執行之前寫入 *Length 輸出值。 此行為可確保更新的 *Length 值一律可在 DmaCompletionRoutine 執行之前使用。 例如,如果 DMA 傳輸需要兩個 MapTransferEx 呼叫,則第一次呼叫排程的 DmaCompletionRoutine 可以從第一次呼叫取得 *Length 輸出值。 然後,例程可以使用此值來計算第二次呼叫的 *Length 輸入值。 一般而言,Length 參數會指向提供給 DmaCompletionRoutine 做為參數的 *CompletionContext 值中的位置。