第 9 章 実行に当たっての問題
9.1 DMA の実行
このセクションでは、バスマスタとして実行されるデバイスのためのバスマスタ ダイレクト メモリ アクセス (DMA) を実装する WinDriver の使用方法を説明します。
DMA とは、接続されたデバイスからホストのメモリへ直接データを転送可能な PCI、PCMCIA、および
CardBus を含んだコンピュータのバス構造によって提供される機能です。CPU はデータ転送に関与しない
ため、ホスト側のパフォーマンスの向上につながります。
DMA バッファを次の 2 つの方法で割り当てることができます。
z
Contiguous Buffer (連続バッファ): 連続メモリブロックを割り当てます。z Scatter/Gather: 割り当てられたバッファは物理メモリ内では断片的で、連続して割り当てる必要
はありません。割り当てられた物理メモリブロックは呼び出し処理の仮想アドレス空間で連続バッ ファへマップされています。そのため割り当てられた物理メモリブロックへ容易にアクセスすること ができます。
デバイスの DMA コントローラのプログラミングはハードウェアにより異なります。通常、ローカル アドレス (デ バイス上)、ホスト アドレス (PC の物理メモリ アドレス)、および転送カウント (転送するメモリ ブロック サイズ) を使用してデバイスをプログラムし、次に転送を開始するレジスタを設定します。
WinDriver は Contiguous Buffer DMA および Scatter/Gather DMA (ハードウェアがサポートしている場合) を
実装する API を提供します (WDC_DMAContigBufLock()、WDC_DMASGBufLock()、および
WDC_DMABufUnlock() の詳細を参照してください)。低水準 WD_DMAxxx API は WinDriver PCI 低水
準 API リファレンスで説明されていますが、代わりにラッパー WDC_xxx API を使用することを推奨します。
このセクションでは Scatter/Gather および Contiguous Buffer DMA を実装する WinDriver の使用方法を実 演するサンプル コードを紹介します。
注意:
z このサンプル ルーチンは、割り込みまたはポーリングを使用して DMA の完了を測定するデモで す。
z このサンプルルーチンは DMA バッファを割り当て、DMA 割り込みを有効にします (ポーリングが 使用されていない場合)。次にバッファを解放し、各 DMA 転送への割り込みを無効にします (有 効の場合)。しかし、実際の DMA コードを実行する場合、アプリケーションの初めに一度 DMA バッファを割り当てることができ、DMA の割り込みを有効にすることができます (ポーリングが使用 されたいない場合)。次に、同じバッファを使用して DMA 転送を繰り返し実行し、割り込みを無効 にします (有効の場合) 。アプリケーションが DMA を実行する必要がなくなった場合のみバッファ を解放します。
9.1.1 Scatter/Gather DMA
DMA 実装のサンプル
次のサンプルルーチンは WinDriver の WDC API を使用して Scatter/Gather DMA バッファを割り当て、バ スマスタ DMA 転送を実行します。
PLX チップセット [第 8 章 ] 用の拡張サポートの詳細な例は WinDriver/plx/lib/plx_lib.c ライブ ラリファイルおよび WinDriver/plx/diag_lib/plx_diag_lib.c 診断ライブラリファイル
(plx_lib.c DMA API を使用) に保存されています。
Altera PCI 開発キットボード用 Scatter/Gather DMA を実装する WD_DMAxxx API を使用したサンプルは WinDriver/altera/pci_dev_kit/lib/altera_lib.c ライブラリ ファイルに保存されています。
9.1.1.1 Scatter/Gather DMA 実装のサンプル
BOOL DMARoutine(WDC_DEVICE_HANDLE hDev, DWORD dwBufSize,
UINT32 u32LocalAddr, DWORD dwOptions, BOOL fPolling, BOOL fToDev) {
PVOID pBuf;
WD_DMA *pDma = NULL;
BOOL fRet = FALSE;
/* Allocate a user-mode buffer for Scatter/Gather DMA */
pBuf = malloc(dwBufSize);
if (!pBuf)
return FALSE;
/* Lock the DMA buffer and program the DMA controller */
if (!DMAOpen(hDev, pBuf, u32LocalAddr, dwBufSize, 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);
WDC_DMASyncIo(pDma);
fRet = TRUE;
Exit:
DMAClose(pDma, fPolling);
free(pBuf);
return fRet;
}
/* DMAOpen: Locks a Scatter/Gather DMA buffer */
BOOL DMAOpen(WDC_DEVICE_HANDLE hDev, PVOID pBuf, UINT32 u32LocalAddr, DWORD dwDMABufSize, BOOL fToDev, WD_DMA **ppDma)
{
DWORD dwStatus, i;
DWORD dwOptions = fToDev ? DMA_TO_DEVICE : DMA_FROM_DEVICE;
/* Lock a Scatter/Gather DMA buffer */
dwStatus = WDC_DMASGBufLock(hDev, pBuf, dwOptions, dwDMABufSize, ppDma);
if (WD_STATUS_SUCCESS != dwStatus) {
printf("Failed locking a Scatter/Gather DMA buffer. Error 0x%lx - %s\n",
dwStatus, Stat2Str(dwStatus));
return FALSE;
}
/* Program the device's DMA registers for each physical page */
MyDMAProgram((*ppDma)->Page, (*ppDma)->dwPages, fToDev);
return TRUE;
}
/* DMAClose: Unlocks a previously locked Scatter/Gather 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);
}
9.1.1.2 実行しなければならないものは
上記のサンプルコードで、MyDMAxxx() ルーチン以下の実装はデバイスの使用により異なります。
MyDMAProgram(): デバイスの DMA レジスタをプログラムします。
詳細は、デバイスのデータシートを参照してください。
z MyDMAStart(): DMA 転送を開始するデバイスへ書き込みます。
z MyDMAInterruptEnable() および MyDMAInterruptDisable(): WDC_IntEnable() および WDC_IntDisable() を使用して、ソフトウェアの割り込みを有効/無効にし、物理的に ハードウェア DMA 割り込みを有効/無効にするためにデバイスの関連したレジスタを書き込み/読 み取ります。(WinDriver での割り込み処理に関する詳細はセクション [9.2] を参照してください)。
z MyDMAWaitForComplete (): 転送の完了をデバイスにポーリングするか、”DMA DONE”
(DMA の完了) 割り込みを待機します。
注意: WD_xxx API (WinDriver PCI 低水準 API リファレンスを参照) を使用して、1MB より大きい
Scatter/Gather DMA バッファを割り当てる場合、FAQ
(http://www.xlsoft.com/jp/products/windriver/support/faq.html#dma1) で説明されている通り、
WD_DMALock() で DMA_LARGE_BUFFER フラグを設定し、追加のメモリページ用のメモリを割り当てる
必要があります。しかし、WDC_DMASGBufLock() を使用して DMA バッファを割り当てる場合、関数が処 理するため大きいバッファを割り当てる特別な実装は必要ありません。
9.1.2 Contiguous Buffer (連続バッファ) DMA
次のサンプルルーチンは WinDriver の WDC API を使用して Contiguous DMA バッファを割り当て、バス
マスタ DMA 転送を実行します。
PLX チップセット [第 8 章 ] 用の拡張サポートの詳細な例は WinDriver/plx/lib/plx_lib.c ライブ ラリ ファイルおよび WinDriver/plx/diag_lib/plx_diag_lib.c 診断ライブラリ ファイル
(plx_lib.c DMA API を使用) に保存されています。
AMCC 5933 用 Contiguous Buffer DMA を実装する WD_DMAxxx API を使用したサンプルは
WinDriver/amcc/lib/amcclib.c ライブラリ ファイルに保存されています (WD_DMAxxx API につ
いては、WinDriver PCI 低水準 API リファレンスを参照してください)。
9.1.2.1 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);
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);
}
9.1.2.2 実行しなければならないものは
上記のサンプル コードで、MyDMAxxx() ルーチン以下の実装はデバイスの使用により異なります。
z MyDMAProgram(): デバイスの DMA レジスタをプログラムします。
詳細は、デバイスのデータシートを参照してください。
z MyDMAStart(): DMA 転送を開始するデバイスへ書き込みます。
z MyDMAInterruptEnable() および MyDMAInterruptDisable(): WDC_IntEnable()
および WDC_IntDisable() を使用して、ソフトウェアの割り込みを有効/無効にし、物理的に
ハードウェア DMA 割り込みを有効/無効にするためにデバイスの関連したレジスタを書き込み/読 み取ります。(WinDriver での割り込み処理に関する詳細はセクション [9.2] を参照してください) 。 z MyDMAWaitForComplete (): 転送の完了をデバイスにポーリングするか、”DMA DONE”
(DMA の完了) 割り込みを待機します。
9.1.3 SPARC での DMA の実行
Solaris の SPARC では、DVMA (Direct Virtual Memory Access) をサポートします。DVMA をサポートする プラットフォームでは、物理アドレスではなく仮想アドレスを持つデバイスを提供することによって、転送を実 行します。このメモリ アクセスの方法で、提供された仮想アドレスへのデバイス アクセスを MMU (Memory Management Unit) を使用する適切な物理アドレスへ移します。デバイスは dis-contiguous 物理ページへ マップされる連続仮想イメージへ / から転送します。これらのプラットフォームで操作するデバイスは、
Scatter/Gather DAM 機能を必要としません。