Oracle® Solaris 11.2 デバイスドライバの記述

印刷ビューの終了

更新: 2014 年 9 月
 
 

データ転送方法

データは、プログラム式入出力または DMA のどちらかを使用して転送できます。これらのデータ転送方法は、デバイスの機能に応じて、同期エントリポイントまたは非同期エントリポイントのどちらかで使用できます。

プログラム式入出力転送

プログラム式入出力デバイスは、データ転送を実行する CPU に依存します。プログラム式入出力データ転送は、デバイスレジスタに対するほかの読み取りおよび書き込み操作と同一です。デバイスメモリーの値の読み取りまたは格納のために、さまざまなデータアクセスルーチンが使用されます。

uiomove (9F) を使用すると、一部のプログラム式入出力デバイスにデータを転送できます。uiomove (9F) は、uio(9S) 構造体で定義されたユーザー空間とカーネルの間でデータを転送します。uiomove() はページフォルトを処理できるため、データの転送先のメモリーをロックダウンする必要はありません。uiomove() はまた、uio(9S) 構造体内の uio_resid フィールドも更新します。次の例は、RAM ディスクの read(9E) ルーチンを記述するための 1 つの方法を示しています。このルーチンは同期入出力を使用し、RAM ディスクの状態構造体内に次のフィールドが存在することを前提にしています。

caddr_t    ram;        /* base address of ramdisk */
int        ramsize;    /* size of the ramdisk */
使用例 15-3   uiomove(9F) を使用した RAM ディスクの read (9E) ルーチン
static int
rd_read(dev_t dev, struct uio *uiop, cred_t *credp)
{
     rd_devstate_t     *rsp;

     rsp = ddi_get_soft_state(rd_statep, getminor(dev));
     if (rsp == NULL)
       return (ENXIO);
     if (uiop->uio_offset >= rsp->ramsize)
       return (EINVAL);
     /*
      * uiomove takes the offset into the kernel buffer,
      * the data transfer count (minimum of the requested and
      * the remaining data), the UIO_READ flag, and a pointer
      * to the uio structure.
      */
     return (uiomove(rsp->ram + uiop->uio_offset,
         min(uiop->uio_resid, rsp->ramsize - uiop->uio_offset),
         UIO_READ, uiop));
}

プログラム式入出力の別の例として、デバイスのメモリーに直接データを一度に 1 バイト書き込むドライバがあります。各バイトは、uwritec(9F) を使用して uio(9S) 構造体から取得されます。次に、そのバイトがデバイスに送信されます。read(9E) は、ureadc (9F) を使用して、デバイスから uio(9S) 構造体で記述された領域に 1 バイトを転送できます。

使用例 15-4  uwritec(9F) を使用したプログラム式入出力の write(9E) ルーチン
static int
xxwrite(dev_t dev, struct uio *uiop, cred_t *credp)
{
    int    value;
    struct xxstate     *xsp;

    xsp = ddi_get_soft_state(statep, getminor(dev));
    if (xsp == NULL)
        return (ENXIO);
    /* if the device implements a power manageable component, do this: */
    pm_busy_component(xsp->dip, 0);
    if (xsp->pm_suspended)
        pm_raise_power(xsp->dip, normal power);

    while (uiop->uio_resid > 0) {
        /*
         * do the programmed I/O access
         */
        value = uwritec(uiop);
        if (value == -1)
               return (EFAULT);
        ddi_put8(xsp->data_access_handle, &xsp->regp->data,
            (uint8_t)value);
        ddi_put8(xsp->data_access_handle, &xsp->regp->csr,
            START_TRANSFER);
        /*
         * this device requires a ten microsecond delay
         * between writes
         */
        drv_usecwait(10);
    }
    pm_idle_component(xsp->dip, 0);
    return (0);
}

DMA 転送 (同期)

文字ドライバは一般に、Example 15–5 に示すように、physio(9F) を使用して read(9E)write(9E) で DMA 転送のための設定作業を行います。

int physio(int (*strat)(struct buf *), struct buf *bp,
     dev_t dev, int rw, void (*mincnt)(struct buf *),
     struct uio *uio);

physio(9F) では、ドライバは strategy (9E) ルーチンのアドレスを指定する必要があります。physio (9F) では、メモリー空間が確実にロックダウンされるため、データ転送の期間中、メモリーをページアウトすることはできません。DMA 転送ではページフォルトを処理できないため、DMA 転送にはこのロックダウンが必要です。 physio(9F) ではまた、大きな転送を、より管理しやすい一連の小さな転送に分割するための自動化された方法も提供されます。詳細については、minphys() Entry Pointを参照してください。

使用例 15-5   physio(9F) を使用した read (9E) ルーチンと write (9E) ルーチン
static int
xxread(dev_t dev, struct uio *uiop, cred_t *credp)
{
     struct xxstate *xsp;
     int ret;

     xsp = ddi_get_soft_state(statep, getminor(dev));
     if (xsp == NULL)
        return (ENXIO);
     ret = physio(xxstrategy, NULL, dev, B_READ, xxminphys, uiop);
     return (ret);
}    

static int
xxwrite(dev_t dev, struct uio *uiop, cred_t *credp)
{     
     struct xxstate *xsp;
     int ret;

     xsp = ddi_get_soft_state(statep, getminor(dev));
     if (xsp == NULL)
        return (ENXIO);
     ret = physio(xxstrategy, NULL, dev, B_WRITE, xxminphys, uiop);
     return (ret);
}

physio(9F) の呼び出しで、xxstrategy はドライバの strategy() ルーチンへのポインタです。buf(9S) 構造体ポインタとして NULL を渡すと、physio (9F) は、buf (9S) 構造体を割り当てるよう指示されます。ドライバが physio(9F)buf(9S) 構造体を提供する必要がある場合は、getrbuf (9F) を使用してこの構造体を割り当てるべきです。転送が正常に完了した場合、 physio(9F) は 0 を返し、失敗した場合は、エラー番号を返します。 strategy(9E) を呼び出したあと、physio (9F) は、転送の完了または失敗までブロックするために biowait (9F) を呼び出します。physio (9F) の戻り値は、bioerror(9F) によって設定される buf(9S) 構造体内のエラーフィールドによって決定されます。

DMA 転送 (非同期)

aread (9E)awrite (9E) をサポートする文字ドライバは、physio(9F) の代わりに aphysio(9F) を使用します。

int aphysio(int (*strat)(struct buf *), int (*cancel)(struct buf *),
     dev_t dev, int rw, void (*mincnt)(struct buf *),
     struct aio_req *aio_reqp);

注 -  anocancel(9F) のアドレスは、現在 aphysio (9F) への 2 番目の引数として渡すことのできる唯一の値です。

aphysio(9F) では、ドライバは strategy (9E) ルーチンのアドレスを渡す必要があります。aphysio (9F) では、メモリー空間が確実にロックダウンされるため、データ転送の期間中、メモリー空間をページアウトすることはできません。DMA 転送ではページフォルトを処理できないため、DMA 転送にはこのロックダウンが必要です。 aphysio(9F) ではまた、大きな転送を、より管理しやすい一連の小さな転送に分割するための自動化された方法も提供されます。詳細については、minphys() Entry Pointを参照してください。

Example 15–5Example 15–6 は、 aread(9E) エントリポイントと awrite (9E) エントリポイントが、 read(9E) エントリポイントと write (9E) エントリポイントと比べてほんのわずかしか違わないことを示しています。その違いは主に、physio(9F) の代わりに aphysio(9F) を使用している点にあります。

使用例 15-6   aphysio(9F) を使用した aread (9E) ルーチンと awrite (9E) ルーチン
static int
xxaread(dev_t dev, struct aio_req *aiop, cred_t *cred_p)
{
     struct xxstate *xsp;

     xsp = ddi_get_soft_state(statep, getminor(dev));
     if (xsp == NULL)
         return (ENXIO);
     return (aphysio(xxstrategy, anocancel, dev, B_READ,
     xxminphys, aiop));
}

static int
xxawrite(dev_t dev, struct aio_req *aiop, cred_t *cred_p)
{
     struct xxstate *xsp;

     xsp = ddi_get_soft_state(statep, getminor(dev));
     if (xsp == NULL)
        return (ENXIO);
     return (aphysio(xxstrategy, anocancel, dev, B_WRITE,
     xxminphys,aiop));  
}

aphysio(9F) の呼び出しで、xxstrategy() はドライバの strategy ルーチンへのポインタです。aiop は、aio_req (9S) 構造体へのポインタです。aiop は、 aread(9E)awrite (9E) に渡されます。aio_req (9S) には、データをユーザー空間内のどこに格納するかが記述されます。入出力リクエストが正常にスケジュールされた場合、aphysio (9F) は 0 を返し、失敗した場合は、エラー番号を返します。strategy(9E) を呼び出したあと、aphysio (9F) は、入出力の完了または失敗を待機することなく復帰します。

minphys() エントリポイント

minphys() エントリポイントは、 physio(9F) または aphysio (9F) から呼び出される関数へのポインタです。xxminphys の目的は、リクエストされた転送のサイズがドライバで決められた制限を超えていないことを確認することです。ユーザーがより大きな転送を要求した場合は、strategy(9E) が繰り返し呼び出され、一度に転送するサイズを決められた制限以下にするよう要求されます。DMA リソースが制限されているため、このアプローチは重要です。プリンタなどの低速なデバイスのドライバは、リソースを長時間占有しないように注意するべきです。

通常、ドライバはカーネル関数 minphys (9F) のアドレスを渡しますが、ドライバは代わりに独自の xxminphys() ルーチンを定義できます。xxminphys() の役割は、buf(9S) 構造体の b_bcount フィールドをドライバの上限以下に維持することです。ドライバは、ほかのシステム制限にも従うべきです。たとえば、ドライバの xxminphys() ルーチンは b_bcount フィールドを設定したあと戻る前に、システムの minphys(9F) ルーチンを呼び出します。

使用例 15-7  minphys (9F) ルーチン
#define XXMINVAL (512 << 10)    /* 512 KB */
static void
xxminphys(struct buf *bp)
{
    if (bp->b_bcount > XXMINVAL)
        bp->b_bcount = XXMINVAL
    minphys(bp);
}

strategy() エントリポイント

strategy(9E) ルーチンは、ブロックドライバが元になっています。strategy 関数の名前は、ブロックデバイスへの入出力要求を効率的にキューに入れるための方針を実装することから来ています。また、文字指向のデバイスのドライバも strategy(9E) ルーチンを使用できます。ここで提供されている文字入出力モデルの場合、 strategy(9E) はリクエストのキューを保持するのではなく、一度に 1 つのリクエストを処理します。

次の例では、文字指向の DMA デバイスの strategy (9E) ルーチンは、同期データ転送のための DMA リソースを割り当てます。strategy() は、デバイスレジスタをプログラミングすることによってコマンドを開始します。詳細については、Chapter 9, Direct Memory Access (DMA)を参照してください。


注 - strategy(9E) は、パラメータとしてデバイス番号 (dev_t) を受け取りません。代わりに、デバイス番号は、strategy(9E) に渡された buf(9S) 構造体の b_edev フィールドから取得されます。
使用例 15-8  strategy (9E) ルーチン
static int
xxstrategy(struct buf *bp)
{
     minor_t            instance;
     struct xxstate     *xsp;
     ddi_dma_cookie_t   cookie;

     instance = getminor(bp->b_edev);
     xsp = ddi_get_soft_state(statep, instance);
     /* ... */
      * 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; /* remember bp */
     /* Program DMA engine and start command */
     return (0);
}

注 - strategy()int を返すように宣言されますが、strategy() は常に 0 を返す必要があります。

DMA 転送が完了すると、デバイスは割り込みを生成して、割り込みルーチンが呼び出されるようにします。次の例では、xxintr() は、この割り込みを生成した可能性のあるデバイスの状態構造体へのポインタを受け取ります。

使用例 15-9  割り込みルーチン
static u_int
xxintr(caddr_t arg)
{
     struct xxstate *xsp = (struct xxstate *)arg;
     if ( /* device did not interrupt */ ) {
        return (DDI_INTR_UNCLAIMED);
     }
     if ( /* error */ ) {
        /* error handling */
     }
     /* Release any resources used in the transfer, such as DMA resources.
      * ddi_dma_unbind_handle(9F) and ddi_dma_free_handle(9F)
      * Notify threads that the transfer is complete.
      */
     biodone(xsp->bp);
     return (DDI_INTR_CLAIMED);
}

このドライバは、bioerror (9F) を呼び出すことによってエラーを示します。ドライバは、転送が完了したときか、または bioerror (9F) でエラーを示したあとに biodone (9F) を呼び出す必要があります。