Go to main content
Oracle® Solaris 11.3 デバイスドライバの記述

印刷ビューの終了

更新: 2016 年 11 月
 
 

DMA ウィンドウ

オブジェクトが 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 ウィンドウが使用されているかどうかを判断します。次の例を参照してください。

使用例 35  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() はシステムリソースを新しいウィンドウに再割り当てするので、以前のウィンドウは無効になります。


Caution

注意  - 現在のウィンドウへの転送が完了する前に、ddi_dma_getwin() の呼び出しを使って DMA ウィンドウを移動しないでください。現在のウィンドウへの転送が完了するまで待ってください。このタイミングで割り込みが発生します。次に、データの破壊を避けるために ddi_dma_getwin() を呼び出してください。


Example 9–8() に示すように、使用例 36 関数は通常、割り込みルーチンから呼び出されます。最初の DMA 転送は、ドライバの呼び出しによって開始されます。それ以降の転送は、割り込みルーチンから開始されます。

割り込みルーチンは、デバイスのステータスを調べて、デバイスが転送を正常に完了しているかどうかを判断します。完了していない場合は、エラー回復処理が行われます。転送が正常に行われた場合、割り込みルーチンは論理転送が完了しているかどうかを調べる必要があります。完全な転送には、buf(9S) 構造体で指定されたオブジェクト全体が含まれています。部分的な転送では、1 つの DMA ウィンドウだけが移動されます。部分的な転送では、割り込みルーチンは ddi_dma_getwin(9F) を使ってウィンドウを移動し、新しい cookie を取得して、別の DMA 転送を開始します。

論理要求が完了している場合、割り込みルーチンは保留中の要求がないかどうか確認します。必要があれば、割り込みルーチンは転送を開始します。必要がなければ、割り込みルーチンは別の DMA 転送を呼び出さずに復帰します。次の例は、通常のフロー制御を示しています。

使用例 36  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);
}