第 9 章 実行に当たっての問題
9.1 DMA の実行
9.1.2 Contiguous Buffer (連続バッファ) DMA の実装
次のサンプル ルーチンは WinDriver の WDC API を使用して連続 DMA バッファを割り当て、バスマスタ DMA 転送を実行します。
ハードウェア独自の連続 DMA の詳細な例は、以下の拡張サポートを提供しているチップセット [第 8 章 ] のサンプル ライブラリ ファイルを参照してください:
PLX – WinDriver\plx\lib\plx_lib.c および
WinDriver\plx\diag_lib\plx_diag_lib.c (plx_lib.c DMA API を使用)
Xilinx Bus Master DMA (BMD) デザイン - WinDriver\xilinx\bmd_design\bmd_lib.c Contiguous Buffer (連続バッファ) DMA 実装のサンプル
BOOL DMARoutine(WDC_DEVICE_HANDLE hDev, DWORD dwDMABufSize,
UINT32 u32LocalAddr, DWORD dwOptions, BOOL fPolling, BOOL fToDev) {
PVOID pBuf = NULL;
WD_DMA *pDma = NULL;
BOOL fRet = FALSE;
/* Allocate a DMA buffer and open DMA for the selected channel */
if (!DMAOpen(hDev, &pBuf, u32LocalAddr, dwDMABufSize, fToDev, &pDma)) goto Exit;
/* Enable DMA interrupts (if not polling) */
if (!fPolling) {
if (!MyDMAInterruptEnable(hDev, MyDmaIntHandler, pDma)) goto Exit; /* Failed enabling DMA interrupts */
}
/* Flush the CPU caches (see documentation of WDC_DMASyncCpu()) */
WDC_DMASyncCpu(pDma);
/* Start DMA - write to the device to initiate the DMA transfer */
MyDMAStart(hDev, pDma);
/* Wait for the DMA transfer to complete */
MyDMAWaitForCompletion(hDev, pDma, fPolling);
/* Flush the I/O caches (see documentation of WDC_DMASyncIo()) */
WDC_DMASyncIo(pDma);
fRet = TRUE;
Exit:
DMAClose(pDma, fPolling);
return fRet;
}
/* DMAOpen: Allocates and locks a Contiguous DMA buffer */
BOOL DMAOpen(WDC_DEVICE_HANDLE hDev, PVOID *ppBuf, UINT32 u32LocalAddr, DWORD dwDMABufSize, BOOL fToDev, WD_DMA **ppDma)
{
DWORD dwStatus;
DWORD dwOptions = fToDev ? DMA_TO_DEVICE : DMA_FROM_DEVICE;
/* Allocate and lock a Contiguous DMA buffer */
dwStatus = WDC_DMAContigBufLock(hDev, ppBuf, dwOptions, dwDMABufSize, ppDma);
if (WD_STATUS_SUCCESS != dwStatus) {
printf("Failed locking a Contiguous DMA buffer. Error 0x%lx - %s\n",
dwStatus, Stat2Str(dwStatus));
return FALSE;
}
/* Program the device's DMA registers for the physical DMA page */
MyDMAProgram((*ppDma)->Page, (*ppDma)->dwPages, fToDev);
return TRUE;
}
/* DMAClose: Frees a previously allocated Contiguous DMA buffer */
void DMAClose(WD_DMA *pDma, BOOL fPolling) {
/* Disable DMA interrupts (if not polling) */
if (!fPolling)
MyDMAInterruptDisable(hDev);
/* Unlock and free the DMA buffer */
WDC_DMABufUnlock(pDma);
}
必要な実装
上記のサンプル コードで、対象のデバイスの仕様に応じて、以下の MyDMAxxx() ルーチンを実装します。
MyDMAProgram(): デバイスの DMA レジスタをプログラムします。
詳細は、デバイスのデータシートを参照してください。
MyDMAStart(): デバイスへ書き込みをして、DMA 転送を開始します。
MyDMAInterruptEnable() と MyDMAInterruptDisable(): WDC_IntEnable() と WDC_IntDisable() を使用して、ソフトウェアの割り込みを有効または無効にし、デバイスの関 連するレジスタを書き込みまたは読み込みをして、物理的にハードウェアの DMA 割り込みを有効 または無効にします。(WinDriver での割り込み処理に関する詳細はセクション 9.2 を参照してくだ さい) 。
MyDMAWaitForComplete(): 転送の完了をデバイスにポーリングするか、”DMA DONE”
(DMA の完了) 割り込みを待機します。
9.1.2.1 Windows で連続 DMA バッファの事前割り当て
WinDriver では、DMA API を使用して割り当て可能な DMA バッファのサイズに制限はありま せん。ただし、DMA の割り当て処理は、割り当て時の利用可能なシステム リソースの量に依存し
ます。そのため、バッファをより速く割り当てれば、より良い割り当てができます。
WinDriver for Windows では、対象のデバイスの INF ファイルを編集して、起動時に連続 DMA バッ ファを再割り当てすることができます。これによって、割り当てに成功する確度を上げます。
注意: host-to-device のバッファおよび / または device-to-host へのバッファの 2 方向のバッファのいずれれ の最大値、または 2 方向のバッファに事前割り当てができます。
Windows で連続 DMA バッファを事前に割り当てるには、以下の手順で行います:
1. 以下のように、対象のデバイス INF ファイルの [UpdateRegistryDevice] レジストリ キー以下に必 要な設定を追加します。
注意:
以下、二つのバッファの事前割り当ての設定の例です (一方向まはた二方向のいずれか 一方、ただし、もちろん一つのバッファのみ (またはまったくなし) を事前割り当てすること もできます。
DriverWizard で生成したデバイスの INF ファイルおよび WinDriver のサンプルに含まれ
るデバイスの INF ファイルには、既に一方向のバッファの設定行が含まれているので、各 行の最初のコメント (‘;’) を削除し、二方向のバッファの場合には、DMA 方向のフラグを 変更し、タイトルのコメントを編集する必要があります。
一方向のバッファを事前割り当てするには、以下の行を追加します:
; Host-to-device DMA buffer:
;HKR,,"DmaToDeviceBytes",0x00010001,0x100000 ; Buffer size, in bytes
;HKR,,"DmaToDeviceOptions",0x00010001,0x41 ; DMA flags (0x40=DMA_TO_DEVICE
; +
0x1=DMA_KERNEL_BUFFER_ALLOC)
; Device-to-host DMA buffer:
;HKR,,"DmaFromDeviceBytes",0x00010001,0x100000 ; Buffer size, in bytes
;HKR,,"DmaFromDeviceOptions",0x00010001,0x21 ; DMA flags
; (0x20=DMA_FROM_DEVICE + ;
0x1=DMA_KERNEL_BUFFER_ALLOC)
二方向のバッファを事前割り当てするには、以下の行を追加します:
; Bidirectional DMA buffer:
;HKR,,"DmaToDeviceBytes",0x00010001,0x100000 ; Buffer size, in bytes
;HKR,,"DmaToDeviceOptions",0x00010001,0x61 ; DMA flags
; (0x60=DMA_TO_FROM_DEVICE
; +
0x1=DMA_KERNEL_BUFFER_ALLOC)
; Second bidirectional DMA buffer:
;HKR,,"DmaFromDeviceBytes",0x00010001,0x100000 ; Buffer size, in bytes
;HKR,,"DmaFromDeviceOptions",0x00010001,0x61 ; DMA flags
; (0x60=DMA_TO_FROM_DEVICE
; +
0x1=DMA_KERNEL_BUFFER_ALLOC)
注意: 二方向のバッファの INF ファイルの設定では、一方向のバッファと同じレジストリ キーを 使用します (DmaToDeviceXXX と DmaFromDeviceXXX)、ただし、DMA オプション キーの 値(DmaToDeviceOptions / DmaFromDeviceOptions) は DMA_TO_FROM_DEVICE フ ラグ(0x06) を設定してください (DMA_TO_DEVICE (0x04) または DMA_FROM_DEVICE (0x20) フラグではなく)。
2. 必要に応じて、INF ファイルのバッファ サイズを編集し、オプション マスクへフラグを追加します。ただ し、上記の手順 1 の説明の通り、方向のフラグと DMA_KERNEL_BUFFER_ALLOC フラグを設定す る必要があるので、注意してください。
3. 注意: WinDriver でサポートする DMA フラグに関しては、別紙の WD_DMA 構造体の dwOptions フィールドの説明を参照してください。INF ファイルに設定する関連するフラグの値を探すには、
WinDriver\include\windrvr.h フ ァ イ ル の フ ラ グ の 定 義 を 参 照 し て く だ さ い; (た と え ば 、 DMA_KERNEL_BUFFER_ALLOC フラグを含む enum を探すなど)。
4. コードでは、連続 DMA ロック関数 (WDC_DMAContigBufLock()) への最初の呼び出しまたは最初 の二つの呼び出しでは、INF ファイルのバッファ設定に一致するパラメータの値を設定してください。
device-to-host のバッファまたは最初の二方向のバッファの割り当ての場合、DMA オプション
マスクのパラメータ (dwOptions) には、DmaFromDeviceOptions レジストリ キーの値に同 じ フ ラグ 設 定 を 設 定 す る必 要 が あ り 、 バ ッ フ ァ サイ ズ の パ ラメ ー タ (dwDMABufSize) を DmaFromDeviceBytes レジストリ キーの値に設定する必要があります。
host-to-device のバッファまたは二番目の二方向のバッファの割り当ての場合、DMA オプ
ション マスクのパラメータ (dwOptions) には、DmaToDeviceOptions レジストリ キーの値に 同じフラグ設定を設定する必要があり、バッ ファ サイズのパラメータ (dwDMABufSize) を DmaToDeviceBytes レジストリ キーの値に設定する必要があります。
注意:
WDC_DMAContigBufLock() を使用する場合、DMA_KERNEL_BUFFER_ALLOC フラグ
(INF ファイルの設定では設定する必要がある) を明示的に設定する必要はありません (関数 が自動的にこのフラグを設定するので)。
低 レ ベ ル な WinDriver の WD_DMALock() 関 数 を 使 用 す る 場 合 、 関 数 の
pDma->dwOptions パラメータに DMA オプションを設定し (DMA_KERNEL_BUFFER_ALLOC フ
ラグも設定する必要があります)、pDma->dwBytes パラメータにバッファ サイズを設定する必 要があります。
十分なリソースがないために事前割り当てに失敗する場合、非ページ プールのサイズを増 やす必要があります (メモリの割り当てから)。