データは、プログラム式入出力または 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); }
文字ドライバは一般に、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) 構造体内のエラーフィールドによって決定されます。
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);
aphysio(9F) では、ドライバは strategy (9E) ルーチンのアドレスを渡す必要があります。aphysio (9F) では、メモリー空間が確実にロックダウンされるため、データ転送の期間中、メモリー空間をページアウトすることはできません。DMA 転送ではページフォルトを処理できないため、DMA 転送にはこのロックダウンが必要です。 aphysio(9F) ではまた、大きな転送を、より管理しやすい一連の小さな転送に分割するための自動化された方法も提供されます。詳細については、minphys() Entry Pointを参照してください。
Example 15–5 と Example 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() エントリポイントは、 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(9E) ルーチンは、ブロックドライバが元になっています。strategy 関数の名前は、ブロックデバイスへの入出力要求を効率的にキューに入れるための方針を実装することから来ています。また、文字指向のデバイスのドライバも strategy(9E) ルーチンを使用できます。ここで提供されている文字入出力モデルの場合、 strategy(9E) はリクエストのキューを保持するのではなく、一度に 1 つのリクエストを処理します。
次の例では、文字指向の DMA デバイスの strategy (9E) ルーチンは、同期データ転送のための DMA リソースを割り当てます。strategy() は、デバイスレジスタをプログラミングすることによってコマンドを開始します。詳細については、Chapter 9, Direct Memory Access (DMA)を参照してください。
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); }
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) を呼び出す必要があります。