JavaScript is required to for searching.
ナビゲーションリンクをスキップ
印刷ビューの終了
デバイスドライバの記述     Oracle Solaris 10 8/11 Information Library (日本語)
search filter icon
search icon

ドキュメントの情報

はじめに

パート I Solaris プラットフォーム用デバイスドライバの設計

1.  Solaris デバイスドライバの概要

2.  Solaris カーネルとデバイスツリー

3.  マルチスレッド

4.  プロパティー

5.  イベントの管理とタスクのキュー

6.  ドライバの自動設定

7.  デバイスアクセス: プログラム式入出力

8.  割り込みハンドラ

9.  ダイレクトメモリーアクセス (DMA)

10.  デバイスメモリーおよびカーネルメモリーのマッピング

11.  デバイスコンテキスト管理

12.  電源管理

13.  Solaris ドライバの強化

14.  階層化ドライバインタフェース (LDI)

パート II 特定の種類のデバイスドライバの設計

15.  文字デバイスのドライバ

16.  ブロックデバイスのドライバ

ブロックドライバの構造の概要

ファイル入出力

ブロックデバイスの自動設定

デバイスアクセスの制御

open() エントリポイント (ブロックドライバ)

close() エントリポイント (ブロックドライバ)

strategy() エントリポイント

buf 構造体

bp_mapin 構造体

同期データ転送 (ブロックドライバ)

非同期データ転送 (ブロックドライバ)

無効な buf 要求のチェック

要求のキューへの入力

最初の転送の開始

割り込んでいるデバイスの処理

dump() エントリポイントと print() エントリポイント

dump() エントリポイント (ブロックドライバ)

print() エントリポイント (ブロックドライバ)

ディスク装置ドライバ

ディスクの ioctl

ディスクパフォーマンス

17.  SCSI ターゲットドライバ

18.  SCSI ホストバスアダプタドライバ

19.  ネットワークデバイスのドライバ

20.  USB ドライバ

パート III デバイスドライバの構築

21.  ドライバのコンパイル、ロード、パッケージ化、およびテスト

22.  デバイスドライバのデバッグ、テスト、およびチューニング

23.  推奨されるコーティング方法

パート IV 付録

A.  ハードウェアの概要

B.  Solaris DDI/DKI サービスの概要

C.  64 ビットデバイスドライバの準備

D.  コンソールフレームバッファードライバ

索引

非同期データ転送 (ブロックドライバ)

この節では、非同期入出力転送を実行するための方法を示します。ドライバは入出力要求をキューに入れたあと、呼び出し元に制御を戻します。ここでも、ハードウェアが、一度に 1 つの転送が可能な単純なディスク装置であることを前提にしています。デバイスは、データ転送が完了したときに割り込みます。また、エラーが発生した場合にも割り込みが実行されます。非同期データ転送を実行するための基本的な手順は次のとおりです。

  1. 無効な buf(9S) 要求をチェックします。

  2. 要求をキューに入れます。

  3. 最初の転送を開始します。

  4. 割り込んでいるデバイスを処理します。

無効な buf 要求のチェック

同期の場合と同様に、デバイスドライバは、strategy(9E) に渡された buf(9S) 構造体の有効性をチェックします。詳細については、「同期データ転送 (ブロックドライバ)」を参照してください。

要求のキューへの入力

同期データ転送とは異なり、ドライバは非同期要求の完了を待機しません。代わりに、ドライバはその要求をキューに追加します。キューの先頭は、現在の転送である場合があります。キューの先頭はまた、例 16-5 に示すように、アクティブな要求を保持するための状態構造体の別のフィールドである場合もあります。

キューが最初に空である場合は、ハードウェアがビジー状態ではないため、strategy(9E) は復帰する前に転送を開始します。それ以外の場合、空でないキューで転送が完了すると、割り込みルーチンは新しい転送を開始します。例 16-5 では、新しい転送を開始するかどうかの判定を便宜上、別のルーチンで行なっています。

ドライバは、buf(9S) 構造体の av_forw メンバーと av_back メンバーを使用して転送要求のリストを管理できます。1 つのポインタを使用して片方向リンクリストを管理することも、両方のポインタを一緒に使用して両方向リンクリストを構築することもできます。デバイスのハードウェア仕様によって、デバイスのパフォーマンスを最適化するために (ポリシーの挿入などの) どのタイプのリスト管理を使用するかが指定されます。転送リストはデバイスごとのリストであるため、このリストの先頭と最後尾は状態構造体に格納されます。

次の例は、転送リストなどのドライバの共有データにアクセスできる複数のスレッドを示しています。共有データを識別し、mutex を使用してそのデータを保護する必要があります。mutex のロックの詳細については、第 3 章マルチスレッドを参照してください。

例 16-5 ブロックドライバに対するデータ転送要求のキューへの入力

static int
xxstrategy(struct buf *bp)
{
    struct xxstate *xsp;
    minor_t instance;
    instance = getminor(bp->b_edev);
    xsp = ddi_get_soft_state(statep, instance);
    /* ... */
    /* validate transfer request */
    /* ... */
    /*
     * Add the request to the end of the queue. Depending on the device, a sorting
     * algorithm, such as disksort(9F) can be used if it improves the
     * performance of the device.
     */
    mutex_enter(&xsp->mu);
    bp->av_forw = NULL;
    if (xsp->list_head) {
       /* Non-empty transfer list */
       xsp->list_tail->av_forw = bp;
       xsp->list_tail = bp;
    } else {
       /* Empty Transfer list */
       xsp->list_head = bp;
       xsp->list_tail = bp;
    }
    mutex_exit(&xsp->mu);
    /* Start the transfer if possible */
    (void) xxstart((caddr_t)xsp);
    return (0);
}

最初の転送の開始

キューへの入力を実装しているデバイスドライバは通常、start() ルーチンを備えています。start() は次の要求をキューから取り出し、デバイスとの間のデータ転送を開始します。この例では、start() はデバイスの状態 (ビジー状態か空いているか) には関係なく、すべての要求を処理します。


注 - start() は、任意のコンテキストから呼び出されるように記述する必要があります。start() は、カーネルコンテキスト内の strategy ルーチンと、割り込みコンテキスト内の割り込みルーチンの両方から呼び出すことができます。


start() は、アイドルのデバイスを起動できるように strategy(9E) が要求をキューに入れるたびに strategy() から呼び出されます。デバイスがビジー状態の場合、start() はただちに復帰します。

start() はまた、取り込まれた割り込みから割り込みハンドラが復帰する前に、その割り込みハンドラからも呼び出されるため、空でないキューを処理できます。キューが空の場合、start() はただちに復帰します。

start() は非公開のドライバルーチンであるため、start() は任意の引数を取り、任意の型を返すことができます。次のコーディング例は、DMA コールバックとして使用するために記述されています (ただし、その部分は示されていません)。したがって、この例では caddr_t を引数として取り、int を返す必要があります。DMA コールバックルーチンの詳細については、「資源割り当てエラーの処理」を参照してください。

例 16-6 ブロックドライバに対する最初のデータ要求の開始

static int
xxstart(caddr_t arg)
{
    struct xxstate *xsp = (struct xxstate *)arg;
    struct buf *bp;

    mutex_enter(&xsp->mu);
    /*
     * If there is nothing more to do, or the device is
     * busy, return.
     */
    if (xsp->list_head == NULL || xsp->busy) {
       mutex_exit(&xsp->mu);
       return (0);
    }
    xsp->busy = 1;
    /* Get the first buffer off the transfer list */
    bp = xsp->list_head;
    /* Update the head and tail pointer */
    xsp->list_head = xsp->list_head->av_forw;
    if (xsp->list_head == NULL)
       xsp->list_tail = NULL;
    bp->av_forw = NULL;
    mutex_exit(&xsp->mu);
    /*
     * If the device has power manageable components,
     * mark the device busy with pm_busy_components(9F),
     * and then ensure that the device
     * is powered up by calling pm_raise_power(9F).
     *
     * Set up DMA resources with ddi_dma_alloc_handle(9F) and
     * ddi_dma_buf_bind_handle(9F).
     */
    xsp->bp = bp;
    ddi_put32(xsp->data_access_handle, &xsp->regp->dma_addr,
        cookie.dmac_address);
    ddi_put32(xsp->data_access_handle, &xsp->regp->dma_size,
         (uint32_t)cookie.dmac_size);
    ddi_put8(xsp->data_access_handle, &xsp->regp->csr,
         ENABLE_INTERRUPTS | START_TRANSFER);
    return (0);
}

割り込んでいるデバイスの処理

割り込みルーチンは非同期バージョンに似ていますが、start() の呼び出しが追加され、cv_signal(9F) の呼び出しが削除されています。

例 16-7 非同期割り込みのためのブロックドライバルーチン

static u_int
xxintr(caddr_t arg)
{
    struct xxstate *xsp = (struct xxstate *)arg;
    struct buf *bp;
    uint8_t status;
    mutex_enter(&xsp->mu);
    status = ddi_get8(xsp->data_access_handle, &xsp->regp->csr);
    if (!(status & INTERRUPTING)) {
        mutex_exit(&xsp->mu);
        return (DDI_INTR_UNCLAIMED);
    }
    /* Get the buf responsible for this interrupt */
    bp = xsp->bp;
    xsp->bp = NULL;
    /*
     * This example is for a simple device which either
     * succeeds or fails the data transfer, indicated in the
     * command/status register.
     */
    if (status & DEVICE_ERROR) {
        /* failure */
        bp->b_resid = bp->b_bcount;
        bioerror(bp, EIO);
    } else {
        /* success */
        bp->b_resid = 0;
    }
    ddi_put8(xsp->data_access_handle, &xsp->regp->csr,
        CLEAR_INTERRUPT);
    /* The transfer has finished, successfully or not */
    biodone(bp);
    /*
     * If the device has power manageable components that were
     * marked busy in strategy(9F), mark them idle now with
     * pm_idle_component(9F)
     * Release any resources used in the transfer, such as DMA
     * resources (ddi_dma_unbind_handle(9F) and
     * ddi_dma_free_handle(9F)).
     *
     * Let the next I/O thread have access to the device.
     */
    xsp->busy = 0;
    mutex_exit(&xsp->mu);
    (void) xxstart((caddr_t)xsp);
    return (DDI_INTR_CLAIMED);
}