ナビゲーションリンクをスキップ | |
印刷ビューの終了 | |
デバイスドライバの記述 Oracle Solaris 10 8/11 Information Library (日本語) |
パート I Solaris プラットフォーム用デバイスドライバの設計
DMA ソフトウェアコンポーネント: ハンドル、ウィンドウ、cookie
21. ドライバのコンパイル、ロード、パッケージ化、およびテスト
オブジェクトが DMA エンジンの制限内に収まらない大きさの場合は、その転送をより小さい一連の転送に分ける必要があります。ドライバは転送そのものを分割できます。別の方法として、ドライバはシステムがオブジェクトの一部にだけ資源を割り当てられるようにすることができます。その結果、一連の DMA ウィンドウが作成されます。システムが資源を割り当てられるようにすることが推奨される解決方法です。ドライバが資源を管理するよりも効果的に資源を管理できるからです。
DMA ウィンドウには 2 つの属性があります。offset 属性はオブジェクトの先頭から測定されます。length 属性は、割り当てられるメモリーのバイト数です。部分的な割り当てが行われたあと、offset で始まる length バイトの範囲だけに資源が割り当てられています。
DMA ウィンドウを要求するには、ddi_dma_buf_bind_handle(9F) または ddi_dma_addr_bind_handle(9F) のパラメータとして DDI_DMA_PARTIAL フラグを指定します。ウィンドウを作成できる場合は、どちらの関数も DDI_DMA_PARTIAL_MAP を返します。ただし、システムがオブジェクト全体に資源を割り当てることがあります。この場合は DDI_DMA_MAPPED が返されます。ドライバは、戻り値を調べて、DMA ウィンドウが使用されているかどうかを判断します。次の例を参照してください。
例 9-7 DMA ウィンドウの設定
static int xxstart (caddr_t arg) { struct xxstate *xsp = (struct xxstate *)arg; struct device_reg *regp = xsp->reg; ddi_dma_cookie_t cookie; int status; mutex_enter(&xsp->mu); if (xsp->busy) { /* transfer in progress */ mutex_exit(&xsp->mu); return (DDI_DMA_CALLBACK_RUNOUT); } xsp->busy = 1; mutex_exit(&xsp->mu); if ( /* transfer is a read */) { flags = DDI_DMA_READ; } else { flags = DDI_DMA_WRITE; } flags |= DDI_DMA_PARTIAL; status = ddi_dma_buf_bind_handle(xsp->handle, xsp->bp, flags, xxstart, (caddr_t)xsp, &cookie, &ccount); if (status != DDI_DMA_MAPPED && status != DDI_DMA_PARTIAL_MAP) return (DDI_DMA_CALLBACK_RUNOUT); if (status == DDI_DMA_PARTIAL_MAP) { ddi_dma_numwin(xsp->handle, &xsp->nwin); xsp->partial = 1; xsp->windex = 0; } else { xsp->partial = 0; } /* Program the DMA engine. */ return (DDI_DMA_CALLBACK_DONE); }
2 つの関数が DMA ウィンドウで動作します。最初の関数 ddi_dma_numwin(9F) は、特定の DMA オブジェクト用の DMA ウィンドウの数を返します。もう 1 つの関数 ddi_dma_getwin(9F) は、オブジェクト内での再配置、つまりシステム資源の再割り当てを許可します。ddi_dma_getwin() 関数は、現在のウィンドウをオブジェクト内の新しいウィンドウにシフトします。ddi_dma_getwin() はシステム資源を新しいウィンドウに再割り当てするので、以前のウィンドウは無効になります。
注意 - 現在のウィンドウへの転送が完了する前に、ddi_dma_getwin() の呼び出しを使って DMA ウィンドウを移動しないでください。現在のウィンドウへの転送が完了するまで待ってください。このタイミングで割り込みが発生します。次に、データの破壊を避けるために ddi_dma_getwin() を呼び出してください。 |
例 9-8 に示すように、ddi_dma_getwin() 関数は通常、割り込みルーチンから呼び出されます。最初の DMA 転送は、ドライバの呼び出しによって開始されます。それ以降の転送は、割り込みルーチンから開始されます。
割り込みルーチンは、デバイスの状態を調べて、デバイスが転送を正常に完了しているかどうかを判断します。完了していない場合は、エラー回復処理が行われます。転送が正常に行われた場合、割り込みルーチンは論理転送が完了しているかどうかを調べる必要があります。完全な転送には、buf(9S) 構造体で指定されたオブジェクト全体が含まれています。部分的な転送では、1 つの DMA ウィンドウだけが移動されます。部分的な転送では、割り込みルーチンは ddi_dma_getwin(9F) を使ってウィンドウを移動し、新しい cookie を取得して、別の DMA 転送を開始します。
論理要求が完了している場合、割り込みルーチンは保留中の要求がないかどうか確認します。必要があれば、割り込みルーチンは転送を開始します。必要がなければ、割り込みルーチンは別の DMA 転送を呼び出さずに復帰します。次の例は、通常のフロー制御を示しています。
例 9-8 DMA ウィンドウを使用した割り込みハンドラ
static uint_t xxintr(caddr_t arg) { struct xxstate *xsp = (struct xxstate *)arg; uint8_t status; volatile uint8_t temp; mutex_enter(&xsp->mu); /* read status */ status = ddi_get8(xsp->access_hdl, &xsp->regp->csr); if (!(status & INTERRUPTING)) { mutex_exit(&xsp->mu); return (DDI_INTR_UNCLAIMED); } ddi_put8(xsp->access_hdl,&xsp->regp->csr, CLEAR_INTERRUPT); /* for store buffers */ temp = ddi_get8(xsp->access_hdl, &xsp->regp->csr); if ( /* an error occurred during transfer */ ) { bioerror(xsp->bp, EIO); xsp->partial = 0; } else { xsp->bp->b_resid -= /* amount transferred */ ; } if (xsp->partial && (++xsp->windex < xsp->nwin)) { /* device still marked busy to protect state */ mutex_exit(&xsp->mu); (void) ddi_dma_getwin(xsp->handle, xsp->windex, &offset, &len, &cookie, &ccount); /* Program the DMA engine with the new cookie(s). */ return (DDI_INTR_CLAIMED); } ddi_dma_unbind_handle(xsp->handle); biodone(xsp->bp); xsp->busy = 0; xsp->partial = 0; mutex_exit(&xsp->mu); if ( /* pending transfers */ ) { (void) xxstart((caddr_t)xsp); } return (DDI_INTR_CLAIMED); }