vmedrv

- VME device driver for Linux 2.x & 3.x -

English

vmedrv は,Linux 上で SBS Technologies (Bit3) 社製 PCI-VME アダプタ (Model 616/617/618/620) を使用するためのデバイスドライバです. 完全なカーネルモードで動作するローダブルモジュールで,プログラムI/O,メモリマップドアクセス(mmap()),VME 割り込み,DMA 転送など,VME アクセスに使われる全ての機能が使用可能です. 64bit 環境でも動作確認しています.

作成には充分注意していますが,間違い等が含まれている可能性があります. 動作の保証はできませんので,御了承下さい. なお,ライセンスは "LGPL Version 2.1" とします. LGPL2.1 の詳細については,配布パッケージに含まれている COPYING-LGPL2.1 ファイル を参照してください.

vmedrv は,分散型汎用オンライン環境の構築を目指す KiNOKO プロジェクトの一部として作成されました. KiNOKO プロジェクトの詳細については, KiNOKO ホームページ を御覧下さい.


動作環境

現在実装されている機能と Linux のバージョンは以下のとおりです.

OS PIO mmap() 割り込み DMA Non-Block DMApoll()利用可能モデル
Linux 2.0.x read()のみ read()のみ - - SBS 617
Linux 2.2.x - - SBS 617/618/620/620-3
Linux 2.4.x - - SBS 616/617/618/620/620-3
Linux 2.6.x
Linux 3.x
SBS 616/617/618/620/620-3

動作を確認した Linux ディストリビューションは,以下のとおりです.なお,パッケージのアップデートでカーネルのバージョンが変わったときは,ドライバの再コンパイルが必要になります.

kernel 3.x

Ubuntu 11.10, 12.04 (32bit / 64bit)
そのままコンパイルできます.Linux 2.6.x 用の vmedrv をそのまま使用してください.
サンプルプログラムのコンパイルに,g++ パッケージが必要です.

Fedora 16 (32bit / 64bit)
Linux 2.6.x 用の vmedrv をそのまま使用してください.
gcc-c++ のパッケージが必要です
使用しているカーネルに対応する kernel-devel パッケージが必要です. 以下の例を参照してください。
% sudo yum install kernel-devel-`uname -r` (クオートはバッククオート)

kernel 2.6

Debian 6.0 Squeeze, X86_64 (64bit)
そのままコンパイルできます.

Ubuntu 10.04 LTS (32bit / 64bit)
そのままコンパイルできます.

Fedora 15 (32bit / 64bit)
使用しているカーネルに対応する kernel-devel パッケージが必要です. 以下の例を参照してください。
% sudo yum install kernel-devel-`uname -r` (クオートはバッククオート)
Fedora 8 / 10 / 12
そのままコンパイルできます.
Ubuntu 10.04 LTS
そのままコンパイルできます.

Fedora-Core 5 / 6
ドライバをコンパイルする前に,使用しているカーネルに対応する kernel-devel パッケージをインストールしてください.
Fedora-Core 4
そのままコンパイルできます.
Fedora-Core 3
ドライバの Makefile 中の SYSTEMFC3 に設定してください.
Fedora-Core 2
ドライバの Makefile 中の SYSTEMFC2 に設定してください.

kernel 2.4

Fedora-Core 1
ドライバをコンパイルする前に,kernel-source パッケージをインストールしてください.
ドライバの Makefile 中の KERNEL_INCLUDE_DIR/usr/src/linux-2.4/include に設定してください.
Red Had Linux 8.0
ドライバをコンパイルする前に,kernel-source パッケージをインストールしてください.
ドライバの Makefile 中の KERNEL_INCLUDE_DIR/usr/src/linux-2.4/include に設定してください.
ドライバの Makefile 中の SYSTEMREDHAT に設定してください.
Vine Linux 2.5
ドライバの Makefile 中の KERNEL_INCLUDE_DIR/usr/include に設定してください.
Red Had Linux 7.1J
ドライバをコンパイルする前に,kernel-source パッケージをインストールしてください.
ドライバの Makefile 中の KERNEL_INCLUDE_DIR/usr/src/linux-2.4/include に設定してください.

kernel 2.2

Red Had Linux 7.0J
ドライバをコンパイルする前に,kernel-source パッケージをインストールしてください.
ドライバの Makefile 中の KERNEL_INCLUDE_DIR/usr/src/linux/include に設定してください.
Red Had Linux 6.2 / 6.2J
そのままコンパイルできます.
Kondara MNU/Linux 2000
そのままコンパイルできます.
Vine Linux 2.1.5
そのままコンパイルできます.
Vine Linux 2.0
そのままコンパイルできます.
Debian GNU/Linux (woody)
そのままコンパイルできます.
もしかしたら,kernel-headers パッケージをインストールする必要があるかもしれません.
Debian GNU/Linux (potato)
そのままコンパイルできます.
もしかしたら,kernel-headers パッケージをインストールする必要があるかもしれません.

ダウンロードとフィードバック

質問などは,なるべく掲示板のほうにおねがいします(匿名でかまいません). 同じ内容の質問が多くなっていますので,御協力をおねがいします.

また,トラブルに関する質問の際は,使用している Linux ディストリビューションの種類・バージョンやデバイス構成・型番,エラーメッセージや dmesg コマンドの出力など,なるべく詳細な情報を記述してください.

ハードウェア設定

SBS Model-617/618/620 の工場出荷時の設定は以下のようになっています.

つまり,他にコントローラがあって,割り込みの取得を行なわないなら,そのまま使えるということです.

アダプタを VME のコントローラとして使うためには,SYS ブロックにある以下のジャンパを設定する必要があります.

アダプタをコントローラとして使う場合は,アダプタをバスアービタとして機能させるために,BGI-BGO ブロックの横にある以下のジャンパも設定しなければなりません.

vmedrv で VME の割り込みを取得したい場合,VME の割り込みを PCI に伝達させるため,T-INT ブロックにある以下のジャンパを接続します.

以下にこれらのジャンパの位置を示します.基板上にジャンパ番号のシルク印刷があります.モデルにより部品の配置が若干異なりますが,ジャンパの位置は基本的に同じです.詳細は製品マニュアルを参照してください.

SBS618 Jumper Setting

アダプタをコントローラとして使う場合,アダプタはクレートのいちばん左のスロットで使わなければなりません. また,一部の高級なクレートを除いて,モジュールのささっていない全てのスロットに対し,バックプレーンのジャンパ (BG[0-3]-IN/BG[0-3]-OUT, IACK-IN/IACK-OUT) を接続しなければならないことに注意してください. 逆に,割り込みを発行するモジュールの裏の IACK-IN/IACK-OUT のジャンパをはずすことを忘れないように特に注意してください.

インストール方法

  1. ドライバのインストールの前に,VME アダプタを PC に接続し,VME の電源を入れておいてください (Linux kernel 2.6 用の vmedrv では,インストール時に電源を入れておく必要はなくなりました).電源を入れると,アダプタカードの LED がひとつ点灯するはずです.しない場合は,アダプタカードの挿入が不十分であることが多いので,確認してください.

  2. パッケージを展開し,ドライバソースのあるディレクトリに移動します.
    % gunzip vmedrv-1.0.0.tar.gz
    % tar xvf vmedrv-1.0.0.tar
    % cd vmedrv/Linux2.6_Bit3_617
    
    バージョン番号の部分は使用している vmedrv のバージョン,Linux のバージョンに置き換えてください.Model 616/618/620 でも 617 と同じコードを使用します.

  3. 2.4.x 以前のバージョンの Linux カーネルを使用している場合は,環境に合わせて,Makefile を編集する必要があります.Makefile の先頭付近にある以下の変数を設定してください.開発者がテストした環境での設定が上記の動作環境のリストに書いてありますので,参考にしてください.

    KERNEL_INCLUDE_DIR
    「カーネルヘッダファイル」のあるディレクトリです.動作中のカーネルがコンパイルされたときに使用されたヘッダファイルと厳密に同じものでなければなりません.

    一部のディストリビューションでは,kernel-headers パッケージに入っているカーネルヘッダが実際にカーネルのコンパイルに使用されたものと違う場合があります.このような場合は kernel-source パッケージをインストールし,その中のヘッダファイルを指定してください(カーネルソースのコンパイルは必要ありません).

    自分でカーネルを再構築した場合は,その際に使用したソースのヘッダファイルのディレクトリを指定してください.

    USE_MODVERSIONS
    動作中のカーネルが MODVERSIONS を有効にしてコンパイルされたものなら 1 を,そうでない場合は 0 を指定してください.最近のほとんどのディストリビューションでは,MODVERSIONS が使われているようです("1" を指定する).

    自分でカーネルの再構築を行なった場合は,そのときの設定に従ってください.

    現在のカーネルが MODVERSIONS を使っているかは,/proc/ksyms を cat してみて,表示されたカーネル関数にバージョン情報が付加されているかを見れば分かります.
    % cat /proc/ksyms | grep kmalloc
    c012e880 kmalloc_R93d4cfe6            <-- MODVERSION が使われている
    
    % cat /proc/ksyms | grep kmalloc
    c012e880 kmalloc                      <-- MODVERSION が使われていない
    

    SYSTEM (Linux 2.4 版のみ)
    RedHat Linux を使用している場合は SYSTEM=REDHAT,そうでない場合は OTHERS を指定してください.これは,RedHat が独自に行なったカーネルの変更に対応するためのものです.特に RedHat 9 で必要です.Fedora-Core では必要ありません.

    RedHat を使用している場合でも,カーネルソースを RedHat 以外から持って来て,それをコンパイルして使用している場合は OTHERS となります.

  4. ドライバをコンパイルします.
    % make
    

  5. root になって,ドライバをシステムに登録します.dmesg で成功したかを確認できます.
    % su
    # make install
    # dmesg
           いろいろなメッセージ
    vmedrv: Bit3 617 Bus Adapter was detected at ioport 0xdcc0 on irq 10.
      I/O Mapped Node at 0xdcc0.
      Memory Mapped Node at 0xfe010000.
      Mapping Register at 0xfe000000.
      Remote Memory at 0xf8000000.
    vmedrv: successfully installed at 0xdcc0 on irq 10 (major = 127).
    #
    

  6. Linuxをリブートしたときは,もう一度ドライバを登録しなければなりません.
    # make install
    
    リブート時に,自動でドライバを組み込む方法については,KiNOKO-DAQ Technical Tips を参照してください.Linux kernel 2.4 以前用の vmrdrv では,ドライバインストール時に VME クレートの電源が入っている必要があることに注意してください.

  7. システムからドライバを取り除きたい場合,以下のようにします.
    # make uninstall
    # dmesg
           いろいろなメッセージ
    vmedrv: removed.
    #
    

作者が動作確認をしたバージョンよりも新しいバージョンの Linux を使用した場合,コンパイルやインストールに失敗するかもしれません. その場合は,可能な限り対処しますので,上記のフィードバックのリンクより開発者に連絡をください.

動作テスト

vmedrv には,いくつかのテストプログラムが用意されています. これをコンパイルして,動作テストをおこないます.ただし,使用しているモジュールにより, 若干の書き換えが必要です.
% cd ..
% 少し書き換え
% make
もしかしたら古いシステムでは lseek64() がコンパイルエラーになるかもしれません.その場合は lseek64() を lseek() に置き換えてコンパイルしなおしてみてください.

以下は,用意されているテストプログラムの一覧です.引数なしで実行すると簡単な使用方法を表示します.

vmeread.c / vmewrite.c Programmed I/O のテストプログラム
vmemap.c メモリマップトアクセスのテストプログラム
vmedmaread.c / vmedmawrite.c DMA転送のテストプログラム
vmeint.c / vmeintwait.c / vmeintcheck.c 割り込みのテストプログラム
vmepoll.c ポーリング (poll()/select() システムコール) のテストプログラム

vmeread.cvmewrite.cvmemap.cvmedmaread.cvmedmawrite.c の実行には, VMEのメモリボード(またはそのようにアクセスできるモジュール)が必要です. また,これらのプログラムのディフォルトの転送モードはアドレス 32bit,データ 32bit です.転送モードを変更する場合には,プログラムの先頭付近にある以下の部分を変更して,再コンパイルしてください.

#define DEV_FILE "/dev/vmedrv32d32"

vmeint.c, vmeintwait.c, vmeintcheck.c, vmepoll.c を実行するには,VME割り込みを発行できるモジュールが必要です.また,それらのモジュールのレジスタを適切に設定して割り込みを発生させるコードも書かなければなりません. テストプログラム中では,"test-interrupter-XXX.c" を #include し,その中で定義されている以下の関数を使用しています.テストプログラムでは林栄精器の RPV-130 を使用していますが,それ以外のモジュールを使用する場合はこれらの関数を置き換えてコンパイルしなおす必要があります.

test_interrupter_enable()
test_interrupter_disable()
test_interrupter_clear()

使用方法

vmedrv は,汎用データ収集システム KiNOKO から利用すると便利です. 詳細は,KiNOKO ホームページ を参照してください.

vmedrv は,KiNOKO から利用すると便利ですが,ドライバに直接アクセスして使うこともできます.上記の動作確認に用いたプログラムは,ドライバに直接アクセスする方法を示すサンプルとなっています (サンプルとして読みやすいように書いたつもりです).

配布パッケージに含まれている vmeslib は,KEK vmelib と類似のインターフェースを実装したものです.vmelib を使っていた場合には,比較的容易に移行できます.ライブラリファイルとテストプログラムは配布パッケージの中の vmeslib にあります.

従来の vmelib と完全互換なライブラリも用意しましたが,DMA や割り込み処理での複数のデバイスのオープンに問題があるため,新しいライブラリの使用を推奨しています(新しいライブラリも KEK との共同開発です).従来の vmelib は,配布パッケージ中の KEK-vmelib にあります.

以下は,ドライバに直接アクセスして使用するためのインターフェースです.

ドライバのオープン
open()システムコールにより行ないます.
int fd = open("/dev/vmedrv32d32", O_RDWR);
vmedrv をインストールすると,以下のエントリが /devに作成されます. これらは全て同じものですが,デフォルトの転送モードが違います. 転送モードは,オープンした後でも,ioctl()システムコールによりいつでも変更できます.

エントリファイル名 デフォルト転送モード
/dev/vmedrv なし
/dev/vmedrv16d16 アドレス 16bit,データ 16bit,PIO 転送
/dev/vmedrv16d32 アドレス 16bit,データ 32bit,PIO 転送
/dev/vmedrv24d16 アドレス 24bit,データ 16bit,PIO 転送
/dev/vmedrv24d32 アドレス 24bit,データ 32bit,PIO 転送
/dev/vmedrv32d16 アドレス 32bit,データ 16bit,PIO 転送
/dev/vmedrv32d32 アドレス 32bit,データ 32bit,PIO 転送
/dev/vmedrv24d16dma アドレス 24bit,データ 16bit,DMA 転送
/dev/vmedrv24d32dma アドレス 24bit,データ 32bit,DMA 転送
/dev/vmedrv32d16dma アドレス 32bit,データ 16bit,DMA 転送
/dev/vmedrv32d32dma アドレス 32bit,データ 32bit,DMA 転送
/dev/vmedrv24d16nbdma アドレス 24bit,データ 16bit,Non-block DMA 転送
/dev/vmedrv24d32nbdma アドレス 24bit,データ 32bit,Non-block DMA 転送
/dev/vmedrv32d16nbdma アドレス 32bit,データ 16bit,Non-block DMA 転送
/dev/vmedrv32d32nbdma アドレス 32bit,データ 32bit,Non-block DMA 転送

DMA転送モードでも,メモリマップトアクセスは,PIO転送になります.

ここでのデータ幅指定は read() および write() でのみ使用されます.メモリマップトアクセスでは,ここでの指定にかかわらず,アクセスするポインタの型によって,D08/D16/D32 の全ての転送幅を混在して使用できます.

戻り値は,ドライバのファイルディスクリプタです. オープンに失敗した場合は,負の値が返され,大域変数 errno が設定されます. 詳しくは,マニュアルページ open(2) を参照してください.

シーク
lseek() または lseek64() システムコールにより行ないます.
lseek(fd, offset, SEEK_SET);
lseek64(fd, offset, SEEK_SET);
ここで,offset は VME 上のアドレスです.第3引数(whence) に指定できるのは,SEEK_SET および SEEK_CUR のみです.VME のアドレスで 0x80000000 以上にアクセスする場合は lseek64() を使用する必要があります(0x80000000 以下ではどちらも同じです.lseek64() は,比較的最近導入されたので,環境依存性があるかもしれません). 戻り値は,シーク後の新しいオフセットです. シークに失敗した場合は,負の値が返され,大域変数 errno が設定されます. 詳しくは,マニュアルページ lseek(2) または lseek64(3) を参照してください.

PIO 読み出し
read()システムコールにより行ないます.
int size = read(fd, buffer, max_size);
戻り値は,読み出しに成功したサイズです. 読み出しに失敗した場合は,負の値が返され,大域変数 errno が設定されます. 詳しくは,マニュアルページ read(2) を参照してください.

PIO 書き込み
write()システムコールにより行ないます.
int size = write(fd, buffer, size);
戻り値は,書き込みに成功したサイズです. 書き込みに失敗した場合は,負の値が返され,大域変数 errno が設定されます. 詳しくは,マニュアルページ write(2) を参照してください.

メモリマップ
mmap()システムコールにより行ないます.
void* start = mmap(0, length, prot, flags, fd, offset);
ここで,offset は VME のアドレス,length は マッピングを行う領域のサイズです.offset は,MMU のページサイズの整数倍でなければなりません.半端なアドレスからマップしたい場合は,先頭アドレスをページサイズで切り捨てて,返されたアドレスに切り捨てたぶんを足して使用してください.この処理を行うプログラムの例を以下に示します.
#include <asm/page.h>

map_offset = vme_address & PAGE_MASK;   /* 中途半端な部分を切り捨てる */
page_offset = vme_address - map_offset;     /* 切り捨てたサイズ */
map_length = length + page_offset;          /* 切り捨てた分余分にマップする */

start = mmap(0, map_length, PROT_WRITE, MAP_SHARED, fd, map_offset);
if (start == MAP_FAILED) {
    perror("ERROR: mmap()");
    exit(EXIT_FAILURE);
}
mapped_address = start + page_offset;       /* 切り捨てた分を足す */

386 以降の x86 シリーズ CPU では,MMU ページサイズは 4kB になっています.それ以外の CPU については,$(KERNEL_INCLUDE_DIR)/asm/page.h 中の PAGE_SIZEの定義(たぶんマクロ)を参照してください.vmedrv には,PAGE_SIZE を画面に出力するサンプルプログラム pagesize.c が含まれています.

メモリマップトアクセスでは,open() 時のデータ幅指定にかかわらず,アクセスするポインタの型によって,D08/D16/D32 の全ての転送幅を混在して使用できます.

戻り値は,マッピングされた領域の先頭アドレスです. マッピングに失敗した場合は,負の値が返され,大域変数 errno が設定されます. 詳しくは,マニュアルページ mmap(2) を参照してください.

割り込みの設定・取得
ioctl()システムコールを使って IRQ とベクタをシグナル番号とともに登録することにより,VME の割り込みをシグナルに変換できます.
struct vmedrv_interrupt_property_t interrupt_property;    
interrupt_property.irq = IRQ;
interrupt_property.vector = VECTOR;
interrupt_property.signal_id = SIGNAL_ID;

/* 割り込みの登録 */
ioctl(fd, VMEDRV_IOC_REGISTER_INTERRUPT, &interrupt_property);

/* 割り込みベクタ幅の指定(8bitベクタを使用する場合のみ) */
interrupt_property.vector_mask = 0x00ff;
ioctl(fd, VMEDRV_IOC_SET_VECTOR_MASK, &interrupt_property);

/* 割り込み取得後,自動で割り込み禁止を設定する(必要な場合のみ) */
/* ioctl(fd, VMEDRV_IOC_SET_INTERRUPT_AUTODISABLE, &interrupt_property); */

/* 割り込み許可 */
ioctl(fd, VMEDRV_IOC_ENABLE_INTERRUPT);

/* ここで割り込み処理を行なう */
/* VME 割り込みで,登録してある SignalID のシグナルが発生する */

/* 割り込み禁止 */
ioctl(fd, VMEDRV_IOC_DISNABLE_INTERRUPT);

/* 割り込みの削除 */
ioctl(fd, VMEDRV_IOC_UNREGISTER_INTERRUPT, &interrupt_property);
デフォルトの割り込みベクタは幅 16 bit です. 8 bit のベクタ返すモジュールを使用する場合は,上記の例のように,SET_VECTOR_MASK でベクタの有効ビットマスクを指定してください.16 bit のベクタを使用する場合は不要です.

割り込みの許可により,その時点で登録してある全ての割り込みに対して,ハンドリングが開始されます.登録されている割り込みが発生した場合,割り込みプロパティで指定したシグナルがプロセスに投げられます.

VMEDRV_IOC_SET_INTERRUPT_AUTODISABLE を設定した場合,ドライバにより割り込みが取得されると,割り込みハンドラを出る前に自動で VMEDRV_IOC_DISABLE_INTERRUPT が行われます.ベクタを取得しても IRQ をクリアしないようなモジュールを使っている場合,割り込みの繰り返し取得でシステムが固まらないようにするため,このオプションを指定してください.なお,この場合,割り込み取得後に次の割り込みを取得するためには,毎回 VMEDRV_IOC_ENABLE_INTERRUPT を行う必要があります.

割り込み待ち
VME 割り込み登録の際に指定するシグナル番号を 0 にしておくと,ioctl() で割り込み待ちを行うことができます.この際のコマンドには VMEDRV_IOC_WAIT_FOR_INTERRUPT を指定します.
struct vmedrv_interrupt_property_t interrupt_property;    
interrupt_property.irq = IRQ;
interrupt_property.vector = VECTOR;
interrupt_property.signal_id = 0;

/* 割り込みの登録 */
ioctl(fd, VMEDRV_IOC_REGISTER_INTERRUPT, &interrupt_property);

/* 割り込みベクタ幅の指定(8bitベクタを使用する場合のみ) */
interrupt_property.vector_mask = 0x00ff;
ioctl(fd, VMEDRV_IOC_SET_VECTOR_MASK, &interrupt_property);

/* 割り込み取得後,自動で割り込み禁止を設定する(必要な場合のみ) */
/* ioctl(fd, VMEDRV_IOC_SET_INTERRUPT_AUTODISABLE, &interrupt_property); */

/* 割り込み許可 */
ioctl(fd, VMEDRV_IOC_ENABLE_INTERRUPT);

/* 割り込み待ち */
interrupt_property.timeout = TIMEOUT_SEC;
result = ioctl(fd, VMEDRV_IOC_WAIT_FOR_INTERRUPT, &interrupt_property);
if (result > 0) {
    /* VME 割り込みを取得 */
}
else if (errno == ETIMEDOUT) {
    /* タイムアウト */
}
else if (errno == EINTR) {
    /* シグナル等他のものに割り込まれた */
}
else {
    /* エラー */
}

/* 割り込み禁止 */
ioctl(fd, VMEDRV_IOC_DISNABLE_INTERRUPT);

/* 割り込みの削除 */
ioctl(fd, VMEDRV_IOC_UNREGISTER_INTERRUPT, &interrupt_property);
vmedrv が使用する割り込みベクタは 16 bit です. 8 bit のベクタ返すモジュールを使用する場合は,上記の例のように,SET_VECTOR_MASK でベクタの有効ビットマスクを指定してください.16 bit のベクタを使用する場合は不要です.

VMEDRV_IOC_SET_INTERRUPT_AUTODISABLE を設定した場合,ドライバにより割り込みが取得されると,割り込みハンドラを出る前に自動で VMEDRV_IOC_DISABLE_INTERRUPT が行われます.ベクタを取得しても IRQ をクリアしないようなモジュールを使っている場合,割り込みの繰り返し取得でシステムが固まらないようにするため,このオプションを指定してください.なお,この場合,割り込み取得後に次の割り込みを取得するためには,毎回 VMEDRV_IOC_ENABLE_INTERRUPT を行う必要があります.

割り込みのチェックとクリア
割り込み待ちを行う設定(シグナル番号を0にする)で,ioctl() に VMEDRV_IOC_CHECK_INTERRUPT または VMEDRV_IOC_CLEAR_INTERRUPT を指定することにより,VME 割り込みが来ているかのチェックと,割り込みのクリアが行えます.
/* ここまでで,ioctl(fd, VMEDRV_IOC_WAIT_FOR_INTERRUPT) が使えるようにしておく */

int result = ioctl(fd, VMEDRV_IOC_CHECK_INTERRUPT, &interrupt_property);
if (result == 0) {
   /* 割り込みがなかった */
}
else if (result > 0) {
   /* 割り込みがあった */
   /* 必要な処理を行う */

   /* 割り込みのクリア */
   ioctl(fd, VMEDRV_IOC_CLEAR_INTERRUPT, &interrupt_property);
   test_interrupter_clear();
}
else /*(result < 0)*/ {
    /* エラー */
}

多重割り込みの処理
VME 割り込みを行うモジュールごとに vmedrv を open() しておけば,poll() または select() システムコールにより,複数の割り込みソースからの割り込み待ちを同時に行えます.キーボードやネットワークからの入力待ちを同時に行うこともできます.VME 割り込みの登録は,ioctl()による割り込み待ちのときと同じ様に,シグナル番号に 0 を指定しておきます.
/* ここまでで,ioctl(fd, VMEDRV_IOC_WAIT_FOR_INTERRUPT) が使えるようにしておく */

struct pollfd fd_set[fd_set_size];
fd_set[0].fd = fd_my_module_1; fd_set[0].event = POLLIN;
fd_set[1].fd = fd_my_module_2; fd_set[1].event = POLLIN;
fd_set[2]... /* その他の入出力デバイスを登録 */

int status = poll(fd_set, fd_set_size, timeout_sec);
if (status < 0) {
    if (errno == EINTR) {
        /* poll() 中にシグナル割り込みが発生した */
    }
    else {
        perror("ERROR: poll()");
    }
}
else if (fd_set[0].revent & POLLIN) {
   /* my_module_1 が割り込みを出している */
}
else ...

VME 割り込みが発生すると,vmedrv は POLLIN イベントを発生させます.エラーが発生すると,poll() は -1 を返し,大域変数 errno を設定します.詳しくは,マニュアルページ poll(2) を参照してください.

DMA 読み出し
ioctl() によって転送法を DMA に設定し,あとは PIO と同様に read()システムコールにより行ないます.
/* 転送法を DMA にする.デフォルトがDMAのエントリをオープンした場合には必要ない.*/
int transfer_method = VMEDRV_DMA;
ioctl(fd, VMEDRV_IOC_SET_TRANSFER_METHOD, &transfer_method);

int read_size = read(fd, buffer, max_size);
戻り値は,読み出しに成功したサイズです.

DMA 転送中は,VME へのアクセスはしないようにしてください.また,全ての VME 割り込み処理は DMA 転送が終了するまで延期されます.

Non-block DMA を行う場合は,transfer_method に VMEDRV_NBDMA を指定してください.デフォルトが Non-Block DMA のエントリをオープンした場合には,これは必要ありません.なお,Non-Block DMA では,転送速度は DMA の半分程度かそれ以下になります(手持ちのメモリカードと PC での測定で,DMA が 15MB/sec, Non-Block DMA が 9MB/sec. ただし,これはメモリカードの性能に制限されている可能性もある).

読み出しに失敗した場合は,負の値が返され,大域変数 errno が設定されます. 詳しくは,マニュアルページ read(2) を参照してください.

DMA 書き込み
ioctl() によって転送法を DMA に設定し,あとは PIO と同様に write()システムコールにより行ないます.
/* 転送法を DMA にする.デフォルトがDMAのエントリをオープンした場合には必要ない.*/
int transfer_method = VMEDRV_DMA;
ioctl(fd, VMEDRV_IOC_SET_TRANSFER_METHOD, &transfer_method);

int written_size = write(fd, buffer, size);
戻り値は,書き込みに成功したサイズです.

DMA 転送中は,VME へのアクセスはしないようにしてください.また,全ての VME 割り込み処理は DMA 転送が終了するまで延期されます.

Non-block DMA を行う場合は,transfer_method に VMEDRV_NBDMA を指定してください.デフォルトが Non-Block DMA のエントリをオープンした場合には,これは必要ありません.なお,Non-Block DMA では,転送速度は DMA の半分程度かそれ以下になります.

書き込みに失敗した場合は,負の値が返され,大域変数 errno が設定されます. 詳しくは,マニュアルページ write(2) を参照してください.

VMEアクセスのエラーチェック
ioctl()VMEDRV_IOC_PROBE を渡すことにより,VME 読み出しアクセスのエラーチェックを行うことができます.特に,指定したアドレスにモジュールが存在しているか,正しく動作しているかなどを調べるのに有用です.
vmedrv_word_access_t word_access;
word_access.address = address;
if (ioctl(fd, VMEDRV_IOC_PROBE, &word_access) == -1) {
    /* エラー:データが読み出せない */
}
else {
    /* 成功:読み出した値は word_access.data に記録されている */
    /* もしかしたらバイトオーダが違うかもしれない */
}
ioctl(fd, VMEDRV_IOC_PROBE, &word_access) は,読み出しに成功した場合 word_access.data に読み出した値を格納し,0 を返します.ただし,読み出した値はバイトオーダが違うかもしれないので,直接使用しないでください.失敗した場合は -1 を返し,大域変数 errno を設定します.

ドライバのクローズ
close()システムコールにより行ないます.mmap() してある場合は, close() の前に munmap() を呼んで下さい. 詳しくは,マニュアルページ munmap(2),close(2) を参照してください.
munmap(start, map_length);
close(fd);
ここで,munmap() に渡す address 引数は,mmap() で返された値,map_length 引数は,mmap()で実際に渡された値です.ページ境界の処理をした場合には,混乱しないように注意してください.

既知の不具合

更新履歴

2012年11月5日
最近のカーネル変更に対応 (2.6.36 以降,3.x 系列含む)
Version 1.2.1 リリース.
2010年12月2日
64bit 対応 (AMD64 / x86_64)
Version 1.2.0 リリース.
2010年7月21日
vmeslib で 0x80000000 以上のアドレスにアクセスした場合にエラーになる不具合を修正
(細見さん(東北大)による修正です.ありがとうございました)
Version 1.1.8 リリース.
2010年7月15日
サンプルプログラムで 0x80000000 以上のアドレスにアクセスした場合にエラーになる不具合を修正
(細見さん(東北大)の指摘による修正です.ありがとうございました)
Version 1.1.7 リリース.
2010年3月2日
Linux 2.2/2.4 用の vmedrv.h でコンパイルエラーが出る問題を修正
(岩井さん(KEK)の指摘による修正です.ありがとうございました)
Version 1.1.6 リリース.
2010年2月27日
割り込み取得サイクルの際にドライバが自動で割り込み禁止を設定するオプションを追加
(細見さん(東北大)の提案・試験・バグフィクスによります.ありがとうございました)
Linux 2.0/2.2/2.4 用の vmedrv.h で宣言が不完全だった問題を修正
(岩井さん(KEK)の指摘による修正です.ありがとうございました)
Version 1.1.5 リリース.
2008年12月21日
probe が常に成功を返す不具合を修正
(鈴木さん(KEK)の指摘による修正です.ありがとうございました)
Version 1.1.4 リリース.
2008年9月7日
古い 2.6 カーネル再対応 (おそらく 2.6.18 以前,Fedora Core 4 あたり)
Version 1.1.3 リリース.
2008年8月5日
新しいカーネルで割り込み時にクラッシュするかもしれない不具合を修正
Version 1.1.2 リリース.
2008年7月4日
新しいカーネル対応 (Fedora 8/9 くらい)
VMEカードがない状態でインストールを繰り返すとクラッシュする不具合を修正
Version 1.1.1 リリース.
2007年2月1日
ドライバのインストール,アンインストールを繰り返すとクラッシュする不具合を修正
(武多さんのバグレポートとテスト協力によります.ありがとうございました)
read()/write() の際に,VME エラーのチェックをするように変更
(アクセス先にモジュールが存在しなければタイムアウトエラーになる)
DMA バッファの扱いを新形式 (dma_alloc_coherent) に変更
PCI インターフェースを新形式(pci_register_driver)に変更
Fedora Core 6 対応
(葛西さんのバグレポートとテスト協力によります.ありがとうございました)
Version 1.1.0 リリース.
2006年1月28日
何度か open()/close() を繰り返すと動作しなくなる不具合を修正
IRQ が競合したときに,システムクラッシュを引き起こす不具合を修正
Version 1.0.2 リリース.
2005年11月2日
Fedora Core 3 でコンパイルしたときに生じる不具合を修正
1024byte を越えるデータを PIO で write() したときに生じる不具合を修正
(葛西さんによるバグレポートと修正によります.ありがとうございました)
ドライバインターフェースライブラリ vmeslib を試験実装
Version 1.0.1 リリース.
2005年10月20日
ポーリングインターフェース (poll() / select()) を追加
Non-Block DMA 転送をサポート
(武多さん(宇宙線研)の協力によります.ありがとうございました)
アクセスエラーを検出する Probe の機能を追加
ブロックせず割り込み状況をチェックする機能を追加
インストール時に VME の電源が入っている必要をなくした
KEK vmelib と互換のライブラリを用意
Fedora-Core 2, Fedora-Core 3 に対応.たぶん他のものも
(日下さん(東大)の修正を含んでいます.ありがとうございました)
安定してきたので正式リリースへ
Version 1.0.0 リリース.
2005年4月20日
Linux 2.6.x に対応
Version 0.5.1 リリース.
2003年7月8日
RedHat 8.0 で発生する不具合を修正.
(岩井さん(新潟大)の修正です.ありがとうございました)
Version 0.4.3 リリース.
2002年9月3日
IRQ 共有の不具合を修正
Version 0.4.2 リリース.
2002年8月28日
WAIT_FOR_INTERRUPT 中にシグナルを受けたら -EINTR を返すようにした.
(ioctl() は errno に EINTR をセットして負の値を返す.)
小さいサイズのデータをDMAしたときに固まる不具合を修正.
IRQ の共有をサポート.
(松本さん(筑波大)の協力によりテストしました.ありがとうございました)
Version 0.4.1 リリース.
2001年1月18日
Linux 2.4 カーネルに対応.
Version 0.4.0 リリース.
2000年11月11日
カーネルヘッダのパスを指定できるようにした.
MODVERSION の使用を指定できるようにした.
Version 0.3.1 リリース.
2000年9月17日
PIO Write と DMA Write を追加.
Model 618/620 対応.
ioctl() に WAIT_FOR_INTERRUPT を追加.
サンプルを大幅に変更.
Version 0.3.0 リリース.
2000年5月6日
最近の insmod で生じる不具合を修正.
ドライバのメジャー番号を設定できるようにした.
Linux 起動時にドライバを自動で組み込む方法の記述を追加.
Version 0.2.2 リリース.
2000年4月21日
Debian でコンパイルする際に生じる Makefile の不具合を修正.
(小曾根さん(ICEPP)の指摘による修正です.ありがとうございました)
Version 0.2.1 リリース.
2000年1月28日
Linux 2.2.x に対応
Version 0.2.0 リリース.
2000年1月14日
Version 0.1.0 リリース.
PIO転送,DMA転送,mmap,VME割り込みが使用可能.
ただし,PIO/DMA での write() はサポートしていない.

Edited by: Enomoto Sanshiro