Feb 28,1997 onl5v4: Solaris 2.5 cc ドライバのデバッグ. ブロック転送のデバッグ、その2 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ (http://www-online.kek.jp/~inoue/CAMAC/onl5v4-sol2.5/debug-step11-log.txt) (ftp://onl5v4.kek.jp/export/home/onl5v4/inoue/CAMAC/Log/step11.log) 高エネルギー加速器研究機構 素粒子原子核研究所 物理、オンライングループ 井上 栄二 (1). 現状確認。 o ここで、念のために cam2.f を/usr/local/camac/camac.sol2.debug/Solaris 1.x/vme/camac/fortran/ から /usr/local/camac/camac.sol2.debugに移して 再度、動作を確認しておく。 onl5v4[53]% pwd /usr/local/camac/camac.sol2.debug/Solaris1.x/vme/camac/fortran onl5v4[54]% cp cam2.f /usr/local/camac/camac.sol2.debug onl5v4[55]% cd /usr/local/camac/camac.sol2.debug /usr/local/camac/camac.sol2.debug onl5v4[56]% vi Makefile : CLIB = -I. -L. -lcamac FC = f77 FFLAGS = -fast -O3 -u OBJ = camlib.o forlib.o : #all : cc libcamac.a cam1 cam3 # by E.Inoue all : cc libcamac.a cam1 cam3 cam2 # : # by E.Inoue cam2 : cam2.f $(FC) $(FFLAGS) cam2.f -o cam2 $(CLIB) : clean: \rm -f cc *.o libcamac.a cam1 cam3 *~ core cam2 # \rm -f cc *.o libcamac.a cam1 cam3 *~ core onl5v4[57]% onl5v4[64]% make cam2 f77 -fast -O3 -u cam2.f -o cam2 -I. -L. -lcamac f77: Warning: -O3 overwrites previously set optimization level of -O2 cam2.f: MAIN: onl5v4[65]% cam2 Input transfer mode (1:word 2:long word) >1 Input loop >10 Input mode (0:QSTOP 1:QIGNORE 2:QREPEAT 3:QSCAN) >0 Input data counts >5 Input n a f >3 0 0 in: 10 1 34 0 0 count in 32bit=10 MODE=0 N= 3 A= 0 F= 0 len= 5 lenr= 0 error=**(Hex) Data( 1)= 0 0x 0(Hex) Data( 2)= 0 0x 0(Hex) Data( 3)= 0 0x 0(Hex) Data( 4)= 0 0x 0(Hex) Data( 5)= 0 0x 0(Hex) Data( 6)= 0 0x 0(Hex) Data( 7)= 0 0x 0(Hex) Data( 8)= 0 0x 0(Hex) Data( 9)= 0 0x 0(Hex) Data(10)= 0 0x 0(Hex) Input n a f >3 1 0 in: 10 1 34 0 0 count in 32bit=10 MODE=0 N= 3 A= 1 F= 0 len= 5 lenr= 0 error=**(Hex) Data( 1)= 21845 0x 5555(Hex) Data( 2)= 21845 0x 5555(Hex) Data( 3)= 21845 0x 5555(Hex) Data( 4)= 21845 0x 5555(Hex) Data( 5)= 21845 0x 5555(Hex) Data( 6)= 0 0x 0(Hex) Data( 7)= 0 0x 0(Hex) Data( 8)= 0 0x 0(Hex) Data( 9)= 0 0x 0(Hex) Data(10)= 0 0x 0(Hex) Input n a f >^C *** TERMINATING cam2 *** Received signal 2 (SIGINT) onl5v4[66]% o ブロック転送によるデータ読みだしはできているが、読みだしに時間が かかっている。 NAFを指定した後、go を始めてから5秒ぐらいたっ後、 データがコンソールに表示される。 そして、コンソール上には以下の メッセージが表示される。 WARNING: spurios VMEbus interrupt on level 4, vec 0 (2). ブロック転送テスト・プログラムおよび、ccドライバ・プログラムのチェック。 o /usr/local/camac/camac.sol2.debug/cam2.f のチェック ブロック転送を開始するために、cam2.f プログラム中から、"CDMAW"(あるい は"CDMAL") をコールすると、camlib.c プログラム中の CDMAWルーチン(ある いはCDMALルーチン)がコールされる。 以下、話しを簡単にするために、 "CDMAW"についてだけ考える。 onl5v4[73]% vi camlib.c : CDMAW(mode, naf, buf, len, retlen, err) int mode, naf, len, *retlen, *err; u_short *buf; { register int status; errno = 0; message.command = (u_short)CC_CMD_DOBLOCK; message.mode = (u_short)((mode << 3) | CC_BLOCK | CC_BIT16); message.naf = (u_short)naf; message.ptr_iosb = (struct cc_iosb *)&iosb; vec[0].iov_base = (caddr_t)&message; vec[0].iov_len = sizeof(struct cc_message); vec[1].iov_base = (caddr_t)buf; vec[1].iov_len = len * sizeof(u_short); if ((status = writev(cc_path, vec, 2)) != 0) return status; *retlen = iosb.ret_length; *err = iosb.status; check_file_pointer(*retlen); return 0; } CDMAL(mode, naf, buf, len, retlen, err) int mode, naf, len, *retlen, *err; int *buf; { register int status, i; message.command = (u_short)CC_CMD_DOBLOCK; message.mode = (u_short)((mode << 3) | CC_BLOCK | CC_BIT24); message.naf = (u_short)naf; message.ptr_iosb = (struct cc_iosb *)&iosb; vec[0].iov_base = (caddr_t)&message; vec[0].iov_len = sizeof(struct cc_message); vec[1].iov_base = (caddr_t)buf; vec[1].iov_len = len * sizeof(int); if ((status = writev(cc_path, vec, 2)) != 0) return status; *retlen = iosb.ret_length; *err = iosb.status; check_file_pointer(*retlen); return 0; } : onl5v4[74]% camlib.c プログラム中の CDMAWルーチンは、"writev(cc_path, vec, 2)" を コールする。 このコールが行なわれると、ccドライバ中の "cc_write()" ルーチンがアクセスされる。 onl5v4[39]% vi cc.c : static int cc_write(dev_t dev, struct uio *uio, cred_t *cred_p) { register struct cc_device *cc = &ccdevice[0]; : switch (message.command) { /************************* * CAMAC single action * *************************/ case CC_CMD_DOSINGLE: : /************************* * CAMAC block action * *************************/ case CC_CMD_DOBLOCK: iov++; uio->uio_iov++; uio->uio_iovcnt--; cc->ptr_udata = (u_short *)iov->iov_base; cc->len_udata = 0; cc->len_udata_t = len = iov->iov_len / sizeof(u_short); if ((mode & CC_BIT16) == 0) len /= 2; switch (naf & 0x0018) { case 0x0000: /* CAMAC read */ camac_b(cc, mode, naf, len, &retlen); break; case 0x0010: /* CAMAC write */ camac_b(cc, mode, naf, len, &retlen); break; default: /* NDT */ camac_b(cc, mode, naf, len, &retlen); break; } iosb.status = cc->status; iosb.ret_length = retlen; iosb.s_reg = cc->s_reg; iosb.devinfo = cc->camac_qx + (cc->camac_qx << 16); copyout((caddr_t)&iosb, (caddr_t)message.ptr_iosb, sizeof(struct cc_iosb)); break; : onl5v4[40]% CDMAWルーチンから cc_writeルーチンへは、 message.command = (u_short)CC_CMD_DOBLOCK; message.mode = (u_short)((mode << 3) | CC_BLOCK | CC_BIT16); のメッセージが渡されるので、"CAMAC block action" が選択される。 そして、camac_b()ルーチンがコールされる。 onl5v4[41]% vi cc.c : static int camac_b(struct cc_device *cc, u_short mode, u_short naf, int len, int *retlen) { register dev_t dev = cc->dev; register struct K_REG *k = cc->k; register struct uio *uio = cc->uio; cc->klist = CC_KLIST_NO; cc->mode = mode; cc->naf = naf; cc->len = len; /* set uio for DMA */ uio->uio_iov->iov_base = (caddr_t)cc->ptr_udata; uio->uio_iov->iov_len = (mode & CC_BIT16) ? len * 2: len * 4; uio->uio_iovcnt = 1; uio->uio_segflg = UIO_USERSPACE; uio->uio_offset = 0; uprintf(" in: %d %d %d %d %d\n", uio->uio_iov->iov_len, uio->uio_iovcnt, uio->uio_resid, uio->uio_segflg, uio->uio_offset); /* execute DMA transfer */ switch (cc->naf & 0x0018) { case 0x0000: /* CAMAC read */ physio(cc_strategy, NULL, dev, B_READ, cc_minphys, uio); break; case 0x0010: /* CAMAC write */ physio(cc_strategy, NULL, dev, B_WRITE, cc_minphys, uio); break; default: cc->status = CC_STA_BLOCK_INVFUNC; return 0; } k->donc = CC_INT_AUTO_CLEAR | intrpri; cc->camac_qx = k->csr; cc_sys_status = cc->bp->b_error; /* check timeout */ if (cc->interrupt & CC_INT_TIMEOUT ) { cc->interrupt &= ~CC_INT_TIMEOUT; cc->status = CC_STA_BLOCK_TIMEOUT; *retlen = 0; return cc->status; } uprintf("out: %d %d %d %d %d\n", uio->uio_iov->iov_len, uio->uio_iovcnt, uio->uio_resid, uio->uio_segflg, uio->uio_offset); /* Q-STOP mode or no error */ if ((mode & CC_QSCAN) == 0 || (k->csr & CC_ERR) == 0) { if ((mode & CC_BIT16) == 0) { cc->retlen = cc->len - k->mtc / 2; /* retlen=number of transfer */ if ((mode & CC_QSCAN) == 0 && (k->csr & CC_ERR) != 0) /* Q-STOP */ cc->retlen = cc->retlen - 2; /* delete the data of Q=0 */ cc->ptr_udata += cc->retlen * 2; /* ptr(2bytes),retlen(4bytes) */ cc->len_udata += cc->retlen * 2; } else { cc->retlen = cc->len - k->mtc; if ((mode & CC_QSCAN) == 0 && (k->csr & CC_ERR) != 0) /* Q-STOP */ cc->retlen--; /* delete the data of Q=0 */ cc->ptr_udata += cc->retlen; /* ptr(2bytes),retlen(2bytes) */ cc->len_udata += cc->retlen; } } /* return */ *retlen = cc->retlen; return cc->status; } : onl5v4[42]% camac_b()ルーチンでは、 physio(cc_strategy, NULL, dev, B_READ, cc_minphys, uio); をコールして、DMA転送のためのセットアップ作業を行なう。 physio()を コールする際に cc_strategy()ルーチンのアドレスを必要とする。 onl5v4[42]% vi cc.c : static void cc_minphys(struct buf *bp) { if (bp->b_bcount > CC_MINPHYS_SIZE) bp->b_bcount = CC_MINPHYS_SIZE; return; } /* setup DMA limit */ static ddi_dma_lim_t dma_lim = { 0, 0xffffffff, 0xffff, 0x2, 0x2, 1024 }; static int cc_strategy(struct buf *bp) { register struct cc_device *cc = &ccdevice[0]; register struct K_REG *k = cc->k; register u_long dma_addr; register mode = cc->mode; register naf = cc->naf; register int wc = (mode & CC_BIT16) ? cc->len : cc->len * 2; register u_int flags; /* check DMA mode and set flags */ switch (cc->naf & 0x0018) { case 0x0000: /* CAMAC read */ flags = DDI_DMA_READ; break; case 0x0010: /* CAMAC write */ flags = DDI_DMA_WRITE; break; default: cc->status = CC_STA_BLOCK_INVFUNC; return 0; } /* setup DMA bufer */ if (ddi_dma_buf_setup(cc->dip, bp, flags, DDI_DMA_SLEEP, NULL, &dma_lim, &cc->handle) != DDI_DMA_MAPPED) { bp->b_error |= EIO; bp->b_flags |= B_ERROR; return bp->b_flags; } /* get a virtual address to hand to our device */ if (ddi_dma_htoc(cc->handle, NULL, &cc->dma_cookie) != DDI_SUCCESS) { bp->b_error |= EIO; bp->b_flags |= B_ERROR; return bp->b_flags; } /* set up variables */ cc->bp = bp; dma_addr = cc->dma_cookie.dmac_address; /* count = cc->dma_cookie.dmac_size; */ uprintf("count in 32bit=%d\n", cc->dma_cookie.dmac_size); cc->retlen = 0; k->csr = CC_RST; /* K2917 Reset */ if (cc->klist == CC_KLIST_NO) { k->cma = CC_CMA_INIT; /* Initialize memory pointer */ k->cmr = mode | (cc->cur_crate << 8); k->cmr = naf; k->cmr = -(cc->len & 0xFFFF); /* Max len = 1MWord */ k->cmr = 0xFFFF; k->cmr = CC_HALT; k->cma = CC_CMA_INIT; /* Reset memory pointer */ k->maclo = dma_addr & 0xFFFF; /* Set DMA base address */ k->machi = dma_addr >> 16; k->amr = CC_AMR_INIT; /* Set VME AM code */ k->mtc = wc; k->cser = CC_DMA_RESET; /* DMA reset */ switch (cc->naf & 0x0018) { case 0x0000: /* CAMAC read */ k->docr = CC_DOCR_INIT | CC_DMA_READ; k->sccr = CC_DMA_START; k->csr |= CC_DMA; /* DMA mode */ k->csr &= ~CC_WRITE; break; case 0x0010: /* CAMAC write */ k->docr = CC_DOCR_INIT | CC_DMA_WRITE; k->sccr = CC_DMA_START; k->csr |= CC_DMA; /* DMA mode */ k->csr |= CC_WRITE; break; default: /* ERROR */ cc->status = CC_STA_BLOCK_INVFUNC; return 0; } } /* Kinetic list-processing */ else { k->cma = now_cma; /* Initialize memory pointer */ k->maclo = dma_addr & 0xFFFF; /* Set DMA base address */ k->machi = dma_addr >> 16; k->amr = CC_AMR_INIT; /* Set VME AM code */ k->mtc = now_wc; k->cser = CC_DMA_RESET; /* DMA reset */ k->docr = CC_DOCR_INIT | CC_DMA_READ; k->sccr = CC_DMA_START; k->csr |= CC_DMA; /* DMA mode */ k->csr &= ~CC_WRITE; } /* start DMA */ cc->executing_dma_flag = 1; cc->timeout_id = timeout(cc_timeout, NULL, CC_TIMEOUT_DMA * hz); k->csr |= CC_GO; /* Go! */ k->donc = CC_INT_AUTO_CLEAR | CC_INT_ENABLE | intrpri; /* wait at physio() */ return 0; } : onl5v4[43]% o strategy() strategy(9E)ルーチンはブロック・ドライバに起因するものである。 この ルーチンをコール理由は、ブロックデバイスに対するI/Oリクエストの効果的 なキューイングのための手段を実装できるようにするのが目的である。 キャラクタ向けのドライバでも、strategy(9E)ルーチンを使える。 キャラク タI/Oモデルでstrategy(9E)ルーチンを使う目的は、リクエストのキューを 保持するためではなくて、むしろ一時にひとつのリクエストをサービスする 目的で使われる。 キャラクタ向けのDMAデバイス用としてのstrategy(9E) ルーチンは同期データ転送のためのDMA資源を割り当てる、さらにデバイス・ レジスタをプログラミングすることでコマンドをスタートさせる。 ----------------------------------------------------------------------- 注意事項. strategy(9E)はパラメータとしてデバイス・ナンバ(dev_t)を受信しない。 そのかわりに、このパラメータは、strategy(9E)に渡されるbuf(9S)構造体 のb_edevフィールドから検索される。 ----------------------------------------------------------------------- ----------------------------------------------------------------------- 注意事項. strategy(9E)はintでリターンするように宣言されるけれども、リターンは つねにゼロでなければならない。 ----------------------------------------------------------------------- o minphys() xxminphysは、physio(9F)やaphysio(9F)によってコールされるファンクショ ンのためのポインタである。 ここで、physio(9F)あるいはaphysio(9F)は、 リクエストされた転送のサイズが、ドライバに課せられた限度を越えないこ とを保証するための、カーネル・サポート・ルーチンである。 もしユーザが 限度を越えた転送をリクエストすると、strategy(9E)は、一回で課せられた 限度を越えないようなリクエストで、繰り返し何度もコールされることにな る。 これは、DMAリソースが制限されるという理由で、重要である。 プリ ンタのような遅いデバイスは、長い時間に渡ってリソースとタイアップしな いように注意すべきである。 普通、ドライバは、カーネル・ファンクションminphys(9F)のアドレスを渡す が、ドライバは、それに代えて独自のxxminphys()ルーチンを定義することが できる。 xxminphys()の仕事は、buf(9S)構造体のb_bcountフィールドをドラ イバ・リミット以下に保持することである。 ドライバが欺くべきでない付加的 なシステム・リミットがあってもよい。 そこで、ドライバのxxminphys() ルーチンは、b_bcountフィールドをセットした後で、また、リターンする前 にシステム・コールminphys(9F)ルーチンをコールすべきである。 minphys(9F)ルーチンの書き方 #define XXMINVAL (124 << 10) static void xxminphys(struct buf *bp) { if(bp->b_bcount > XXMINVAL) bp->b_bcount = XXMINVAL; minphys(bp); } ccドライバ・プログラムでは上で示したように、 static void cc_minphys(struct buf *bp) { if (bp->b_bcount > CC_MINPHYS_SIZE) bp->b_bcount = CC_MINPHYS_SIZE; return; } となっており、cc_minphys()の中でminphys(bp)がコールされていない。 これではまずいのではないのかな。 チェック事項。 o 同期I/O 対 非同期I/O 転送のエントリー・ポイント・スケジュールがただちにリターンするか、あ るいはI/O完了まで待つかによって、データ転送は、同期あるいは非同期に 行なうことができる。 read(9E)およびwrite(9E)エントリー・ポイントは、同期エントリー・ポイン トである。 これらは、I/Oが完了するまでリターンすべきではない。 ルーチ ンからのリターンでプロセスは転送が成功したかどうかを知る。 aread(9E)およびawrite(9E)エントリー・ポイントは、非同期エントリー・ポ イントである。 それらは、I/Oをスケジュールすると、ただちにリターンす る。 リターンで、リクエストを発行しているプロセスは、I/Oがスケジュー ルされたことを知り、I/Oのステータスは、後で決定されなければならない ことを知る。 しばらくの間、プロセスは他のオペレーションをやっていても よい。 非同期I/Oリクエストがユーザ・プロセスによってカーネルに対して出される 時、I/O処理がされている間は、プロセスはウェイトする必要はない。 プロ セスはマルチプルI/Oリクエストを実行できる。 そして、カーネルにデータ 転送の細かな操作をさせることができる。 これは、並列プログラミング法が 非同期カーネルI/Oオペレーションで、パフォーマンスやレスポンス・タイム の向上の点で有利になる場所での、トランザクション処理のようなアプリ ケーションで有益である。 非同期I/Oを使うアプリケーションのパフォーマ ンスの押し上げは、どうしても、非常に複雑なプログラムになり費用がかか ることになる。 o DMA転送の終了でデバイスはインタラプトを発生して、インタラプト・ルーチ ンがコールされるようにする。 xxintr()は、インタラプトを発生したであろ うデバイスの state構造体のポインタを受け取る。 onl5v4[43]% vi cc.c : static u_int cc_intr() { register struct cc_device *cc = &ccdevice[0]; register struct K_REG *k = cc->k; mutex_enter(&cc->mutex); /* start MUTEX */ /* check K2917 register */ if ((k->csr & CC_LAM) != 0) cc->interrupt |= CC_INT_LAM; if ((k->csr & CC_DONE) != 0) cc->interrupt |= CC_INT_DONE; if ((k->empc & CC_INT_ENABLE) == 0) cc->interrupt |= CC_INT_EMPTY; if ((k->aboc & CC_INT_ENABLE) == 0) cc->interrupt |= CC_INT_ABORT; /* free DMA resources */ if (cc->executing_dma_flag != 0) { ddi_dma_free(cc->handle); biodone(cc->bp); cc->executing_dma_flag = 0; } if (cc->interrupt == 0) { /* reject interrupt, then return */ mutex_exit(&cc->mutex); /* end MUTEX */ return DDI_INTR_UNCLAIMED; } else { /* accept interrupt, then wake up waiting process, then return */ untimeout(cc->timeout_id); cv_signal(&cc->cv); mutex_exit(&cc->mutex); /* end MUTEX */ return DDI_INTR_CLAIMED; } } : onl5v4[44]% ドライバは、bioerror(9F)をコールすることでエラーを表示する。 ドライバ は転送が完了した時、あるいはbioerror(9F)でエラーの表示をした後で、 biodone(9F)をコールしなければならない。 ---- Section. ** キャラクタ・デバイスのドライバ (A). ドライバ構造概要 (B). キャラクタ・ドライバ のデバイス・アクセス (C). エントリ・ポイント (D). オート・コンフィギュレーション (E). デバイス・アクセスのコントロール (1). open() (2). close() (F). I/Oリクエスト処理 この章では、I/Oリクエスト処理の詳細を示す: アプリケーションからカーネル へ、そしてドライバ、デバイス、インタラプト・ハンドラへ、そしてユーザへ戻 るまでについて。 (1). ユーザアドレス ユーザ・スレッドがwrite(2)システム・コールを発行すると、ユーザ・スペー スのバッファのアドレスが渡される: char buffer[] = "python"; count = write(fd, buffer, strlen(buffer) + 1); write(2)に渡されるアドレスとして、iovec(9S)を割り当て、さらにiov_base フィールドセットすることによって、システムは、この転送を描写するuio構 造体を作る; 上の例で言うと、bufferがこれにあたる。 uio構造体はドライバ のwrite(9E)ルーチンに渡されるものである(uio(9S)構造体についてのもっと 詳しい情報は、以下の"ベクタI/O"の項を参照)。 問題は、このアドレスがカーネル・スペースではなくて、ユーザ・スペースに あることである、それで、現在メモリ上に存在することが保証されないことで ある。 それは有効なアドレスであるということさえも保証されない。 逆に言 えば、デバイス・ドライバやカーネルから直接ユーザ・アドレスをアクセスす ることは、システムをクラッシュさせてしまう。 それで、デバイス・ドライ バは、けっして直接ユーザ・アドレスをアクセスすべきではない。 代わりに カーネルへの、あるいはカーネルからのデータ転送には、普通、Solaris2.x DDI/DKI のデータ転送ルーチンを使う。 (使用可能ルーチンの概要については ページ391の"Copying Data"、およびページ447の"uio(9S) Handling"を参照) これらのルーチンは、適切なユーザ・ページに持っていって透過的にコピー を続けることで、あるいは無効なアクセスでエラーを返すことで、ページ faultの操作ができる。 普通に使われる2つのルーチンは、copyout(9F)とcopyin(9F)である。 copyout(9F)は、カーネル・スペースからユーザ・スペースにデータをコピー するもので、copyin(9F)は、ユーザ・スペースからカーネル・スペースにデー タをコピーするものである。 ddi_copyout(9F)およびddi_copyin(9F)も同じよ うに動作するが、これらはioctl(9E)ルーチンの中で使われる。 copyout(9F) とcopyin(9F)はそれぞれiovec(9S)構造体で描写されたバッファを使うことが できる。 uiomove(9F)は、ドライバ(あるいはデバイス)メモリの連続した領域 に全体の転送を行なうことができる。 (2). ベクタI/O キャラクタ・ドライバでは、転送はuio(9S)構造体によって記述される。 uio (9S)構造体は転送の向きやサイズに加えて、転送の片端(他端はデバイス)にお けるバッファのアレーについての情報を収めている。 以下はキャラクタ・ド ライバにとって重要なuio(9S)構造体メンバのリストである。 o uio() uio構造体は以下のメンバを収めている: iovec_t *uio_iov; /* base address of the iovec buffer */ /* description array */ int uio_iovcnt; /* the number of iovec structures */ off_t uio_offset; /* offset into device where data is */ /* transferred from or to */ offset_t uio_loffset; /* 64-bit offset into file where data */ /* is transferred from or to. */ int uio_resid; /* amount (in bytes) not transfrred on */ /* completion */ uio構造体はドライバのread(9E)およびwrite(9E)エントリ・ポイントに渡 される。 この構造体は、コールされる "gather-write" および "scatter- read" をサポートするために一般化される。 デバイスに write する時、 書き込まれるデータ・バッファは、アプリケーション・メモリの中に連続 的でなくてもよい。 同様に、デバイスからメモリへ読み込む時、データは 連続した連なりでデバイスからでるが、アプリケーション・メモリの不連 続した領域にはいる。 scatter/gathrer I/O のもっと詳しい情報は、 readv(2)、writev(2)、pread(2)、およびpwrite(2)を参照。 それぞれのバッファは、iovec(9S)構造体によって記述される。 この構造 体は、データ領域へのポインタおよび転送されるバイト数を収めている。 caddr_t iov_base; /* address of buffer */ int iov_len; /* amount to transfer */ uio構造体は、iovec(9S)構造体のアレーに対するポインタを収めている。 このアレーのベース・アドレスは、uio_iovの中に保持され、エレメントの 数は、uio_iovcntの中にストアされる。 uio_offsetフィールドは、アプリケーションが転送を始めようとするデバ イスの中で32-bitオフセットを持たせている。 uio_loffsetは64-bit file オフセット用に使われる。 デバイスがオフセット概念をサポートしていな いならば、これらのフィールド安全に無効にされる。 ドライバは、uio_ offset、あるいは、uio_loffset(しかし両方ではない)を、解読すべきであ る。 ドライバが、cb_ops(9S)の中でD_64BITフラッグをセットしている場 合には、uio_loffsetを使うべきである。 uio_residフィールドには最初は転送されるべきバイト数(uio_iovの中の、 iov_lenフィールドの全ての合計)が入れられる。 そして、リターンする前 には、転送されなかったバイト数が、ドライバによってセットされなけれ ばならない。 read(2)およびwrite(2)システム・コールは、転送が失敗(こ の時 -1 が返される)したかどうかを決定するのに、read(9E)およびwrite( 9E)エントリ・ポイントからのリターン値を使用する。 リターン値が正常 終了したことを示している場合には、システム・コールは、必要としたバ イト数からuio_residを引いたバイト数をリターンする。 もし、uio_resid がドライバによって変化されない場合には、たとえ全データが転送された としても、read(2)およびwrite(2)は0(エンド・オブ・ファイルを表してい る)をリターンする。 uiomove(9F)、physio(9F)、およびaphysio(9F)のサポート・ルーチンは、 uio(9S)構造体を直にアップ・デイトする。 これらが使われる場合には、 ドライバでの精算は必要ない。 (3). 同期I/O 対 非同期I/O 上記の "同期I/O 対 非同期I/O" の項を参照。 (4). データ転送の方法 データは、プログラムドI/OあるいはDMAを使って転送することができる。 こ れらのデータ転送の方法は、デバイスの能力に関係して、同期あるいは、 非同期エントリ・ポイントで使われる。 o プログラムドI/O転送 プログラムドI/O・デバイスはデータ転送を行なうのにCPUをあてにしてい る。 プログラムドI/O・データ転送は他のデバイス・レジスタ読みだしや 書き込み動作と同様である。 いろいろなデータ・アクセス・ルーチンは、 デバイス・メモリに値を書き込んだり、読み出したりするのに使われる。 もっと詳しい情報は、ページ55の"Data Access Function"を参照。 o uiomove() uiomove(9F)は、プログラムドI/O・デバイスに対するデータの転送 に使われる。 uiomove(9F)は、ユーザ・スペース(uio(9S)で定義さ れる)とカーネル・スペースの間でデータを転送する。 o uwritec()およびureadc() o DMA転送(同期) ほとんどのキャラクタ・ドライバは、read(9E)およびwrite(9E)のDMA転送 のセットアップ作業のほとんどを行なうのにphysio(9F)を使う。 physio(9F)はstrategy(9E)ルーチンのアドレスを提供するようなドライバ であることを要求する。 physio(9F)は、データ転送の期間中メモリ・ス ペースがロック・ダウン(ページアウトできない)されることを保証する。 DMA転送ではページfaults を処理できないので、これは必要である。 physio(9F)は、大きな転送をより扱い易い小さな並びに、自動的に分割す る方法も備えている。 もっと詳しい情報は、minphys()を参照。 physio(xxstrategy, NULL, dev, B_WRITE, xxminphys, *uiop); physio(9F)へコールする場合、xxstrategy はドライバstrategyルーチンへ のポインタである。 buf(9S)ストラクチャ・ポインタとしてNULLを渡すこ とは、buf(9S)ストラクチャを割り当てることをphysio(9F)に告げることで ある。 buf(9S)を使ったphysio(9F)を用意することがドライバにとって 必要ならば、buf(9S)を割り当てるのに、getrbuf(9F)が使われるべきであ る。 physio(9F)は、転送が正常に終了した場合にはゼロをリターンし、 異常終了した場合にはエラー・ナンバを返す。 strategy(9E)をコールした 後、転送が正常終了、あるいは異常終了するまでブロックしておくために physio(9F)は、biowait(9F)をコールする。 physio(9F)のリターン値は、 bioerror(9F)によってセットされるbuf(9S)構造体セットのエラー・フィー ルドで決められる。 o DMA転送(非同期) aread(9E)およびawrite(9E)をサポートしているキャラクタ・ドライバは、 physio(9F)に替えてaphysio(9F)を使う。 ----------------------------------------------------------------------- 注意事項. anocancel(9F)のアドレスは、aphysio(9F)の2番目のアーギュメントとして 現在渡すことができる唯一の値である。 ----------------------------------------------------------------------- aphysio(9F)は、ドライバがstrategy(9E)ルーチンのアドレスを渡すことを 要求する。 aphysio(9F)は、データ転送の期間中メモリ・スペースがロック ダウン(ページアウトされることがない)されることを保証する。 DMA転送で はページfaultsの処理ができないので、これは必要である。 aphysio(9F)は 大きな転送をもっと扱い易い小さな並びに自動的に分割する手段も備えてい る。 もっと詳しい情報は、minphys()の項を参照。 aphysio(9F)のコールで、xxstrategyはドライバstrategyルーチンに対する ポインタである。 buf(9S)構造体ポインタとしてNULLを渡すことは、buf(9 S)構造体を割り当てることをaphysio(9F)に知らせることである。 buf(9S) 構造体を用いたaphysio(9F)を準備することがドライバにとって必要である ならば、buf(9S)を割り当てるのに、getrbuf(9F)が使われるべきである。 aio_reqp は、aphysioへのパラメータとして渡される。 このパラメータは aio_req(9S)構造体へのポインタであり、aread(9E)とawrite(9F)へも渡され る。 aio_reqp(9S)はユーザ・スペースの中のデータがストアされるべき場 所について描写する。 I/Oリクエストが正常にスケジュールされる場合には aphysio(9F)はゼロをリターンし、異常の場合にはエラー数をリターンす る。 strategyをコールした後、aphysio(9F)はI/Oの終了や失敗を待つこと なしにリターンする。 o minphys() 上記の "minphys()" の項を参照。 o strategy() 上記の "strategy()" の項を参照。 (G). デバイス・メモリのマッピング フレーム・バッファのようなデバイスは、メモリ・マップの手段によって、ユー ザ・スレッドが直にアクセスできるようなメモリを持っている。 これらのデバ イス用のドライバは、通常、read(9E)およびwrite(9E)をサポートしない。 代わ りにこれらのドライバは、mmap(9E)エントリ・ポイントを用いてメモリ・マッピ ングをサポートする。 典型的な例として、ユーザ・スレッドにマップされたフ レーム・バッファを利用するためのmmap(9E)エントリを実装したフレーム・バッ ファがあげられる。 (1). segmap() (2). mmap() int xxmmap(dev_t dev, off_t off, int prot); mmap(2)システム・コールが行なわれると、このルーチンがコールされる。ま た、ページfaultが起こった場合にもこのルーチンがコールされる。 mmap(9E) は対応するページ・フレーム・ナンバにおいて、デバイス・オフセット off が加味されてコールされる。 dev は、デバイス・ナンバで、off は、デバイスのメモリに変換するためのオ フセットである。 prot は、PROT_READ や PROT_WRITE のようなリクエストさ れるアクセスの種類である。 read-only デバイス上では、prot として PROT_ WRITE の値を指定すると無効にされる。 (H). ファイル・デスクリプタを使ったマルチプレキシングI/O (1). state構造体 (2). chpoll() (I). 種々のI/Oコントロール (1). ioctl(9E) ---- o /usr/local/camac/camac.sol2.debug/cc.cのチェック