LinuxデバイスドライバFAQ


DMAとは?

DMAはDirect Memory Accessの略で、CPUを介在させないバス転送 (データ転送)の方法です。一方CPUを介在させるデータ転送の 方法をPIO (Programmed I/O)と言い、対比されます。メモリと CPUは内部のバスで接続されています。またI/O機器を接続する バスもこの内部バスに接続されます。I/O機器にあるデータを メモリに読み込むことを考えてみます。PIOでこれを行なう 場合、データは一旦CPUに送られ、それからメモリに書き込まれます。 しかし、DMAの場合、データはI/O機器から直接CPUを通らずに メモリに書き込まれます。ですから一般にDMAはPIOより 高速な手法になります。

DMAとブロック転送は違うのか?

ブロック転送はDMAとは異なる概念です。ブロック転送は CPUが介在するかどうかは何も言っていません。ある まとまったデータの転送、程度の意味と理解した方が いいです。ブロック転送では読み込みにせよ書き込みせよ 誰がその起動を行なうかが重要なポイントです。 マスタ/スレーブという言葉で表現します。 マスタは起動を行なう主体で、スレーブはそのサービスを 受けるものです。

VMEブロック転送とは?

VMEブロック転送はVME規格に基づくブロック転送の プロトコルです。BLTと呼びます。 規格に従ってはっきりと定義されています。 かいつまんで言うと、データ転送において、 マスタが一旦アドレスストローブ(AS)をアサートすると、 複数回のデータストローブ(DS)をアサートできるもので、 スレーブのデータアクノレッジ(DTACK)もDSに呼応して 複数回、DSをアサートします。

VME64とは?

VME64は最近のVME規格です。以前の規格ではBLTは 定義されていたものの、MBLT(Multiple BLT)はVME64に なってからです。これはアドレスとデータラインを 使って64ビットのデータ転送を行なうものです。 さらに、VME64拡張が規格として決まりつつあります。 この規格の特徴は5列のコネクタを用い、 3V電源をサポートしていることです。

CAMACブロック転送とは?

CAMAC規格にEUR4100eがあります。 他にもブランチドライバとかシリアルドライバとか 補助コントローラの規格が ありますが、省きます。EUR4100eはCAMAC規格として よく使われているもので、CAMACのクレートのための 規格です。CAMACではブロック転送という概念が 使われています。Qレスポンスの扱いについて触れている ところで、アドレススキャンモード、リピートモード、 ストップモードを定義しています。例えば、 ストップモードでは、「データのブロックが転送中である間、 モジュールはリードコマンド動作あるいはライトコマンド動作に 対してQ=1を発生しなければならない。またブロック転送の 終了条件が成立した後では、上記のリードコマンド動作あるいは ライトコマンド動作に対して、モジュールはQ=0を 発生しなければならない。」と決められています。 ただ、注意しなければならないのはこのEUR4100eが ブロック転送という言葉を使って各モードを定義してはいるが モジュール自身がこれらのモードで操作されているのか そうでないのかを知る手段はないということです。 ですから、計算機側のCAMACインターフェースが上記のモードを サポートし、DMAを使ったブロック転送を行なえるように しています。

DMAとブロック転送の両方を使う例は?

1つの例が、Kinetic社のK2917, VME-CAMACインターフェースです。 これはこのインターフェースがVMEのマスタとして動作します。 一度DMAリードが起動しますと、このインターフェースはCAMACの リード操作を行ない、モジュールからデータを読み出します。 同時にそのデータはVMEバスを介して計算機上のメモリに CPUを介さずに送ります。インターフェースにはワードカウントが あらかじめセットされていますので、K2917にあるCAMACの ブロック転送のモードをセットするレジスタにQストップモードが 設定してあったら、Q=0になるか、ワードカウントが0になるまで データ転送を続けます。

デバイスドライバモジュールのコンパイルの仕方

通常は、/usr/src/linuxというディレクトリのもとでカーネルやドライバは コンパイルされますが、 下記の様にまずは自分のディレクトリにskelton.cを持ってきて、コンパイルし、 インストール、デバイスファイルの作成を行うのがいいでしょう。 デバイスファイルは一度作成するだけでいいです。
% gcc -D__KERNEL__ -D MODULE -Wall -c -I/usr/src/linux/include skelton.c
% su
Password:
# insmod skelton.o
# mknod -m 666 /dev/skelton c 32 0
# exit
再度、コンパイルが必要な場合は、下記のようにまたドライバを リムーブしてから、インストールしなおす必要があります。
% su
Password:
# rmmod skelton
# insmod skelton.o
# exit

デバイスドライバは2枚のPCIデバイスをどのように認識するか?

デバイスを見つけるためのカーネル関数はpci_find_deviceです。次のような引数になています。
pci_find_device(unsigned int vendor, unsigned int device, const struct pci_dev *from)
* @vendor: PCI vendor id to match, or %PCI_ANY_ID to match all vendor ids
* @device: PCI device id to match, or %PCI_ANY_ID to match all vendor ids
* @from: Previous PCI device found in search, or %NULL for new search.

Iterates through the list of known PCI devices. If a PCI device is found with a
matching @vendor and @device, a pointer to its device structure is returned.
Otherwise, %NULL is returned.
* A new search is initiated by passing %NULL to the @from argument.
* Otherwise if @from is not null, searches continue from that point. 
この説明からわかるように、この関数を複数回呼び出すことで、複数の PCIデバイスを見つけることができます。詳しくは PIOを用いたPCIデバイス ドライバを御覧ください。

select文はドライバでどのように処理されるか?

カーネルのバージョンによってこの周辺は変更になりました。2.4.xでは(それ以前から) 対応するドライバのメソッドはpollメソッドになりました。
pollメソッドの中で処理の終了を待つ方法の1つとして、poll_waitカーネルルーチンを 呼ぶ方法があります。このpoll_wait待ちを起こす方法はwake_upカーネルルーチンを 呼ぶことです。

リアルタイムクロックって何?

Linuxには2つの主要なクロック、ハードウエアクロックとシステムタイムがあります。

ハードウエアクロックはCPU上のいかなるプログラムに対し独立に走ります。 このクロックはリアルタイムクロック、RTC、BIOSクロック、CMOSクロックとも 呼ばれます。このクロックはhwclockコマンドに読みだしや設定ができます。
% hwclock -h
hwclock - query and set the hardware clock (RTC)

Usage: hwclock [function] [options...]

Functions:
  --help        show this help
  --show        read hardware clock and print result
  --set         set the rtc to the time given with --date
  --hctosys     set the system time from the hardware clock
  --systohc     set the hardware clock to the current system time
  --adjust      adjust the rtc to account for systematic drift since 
                the clock was last set or adjusted
  --getepoch    print out the kernel's hardware clock epoch value
  --setepoch    set the kernel's hardware clock epoch value to the 
                value given with --epoch
  --version     print out the version of hwclock to stdout

Options: 
  --utc         the hardware clock is kept in coordinated universal time
  --localtime   the hardware clock is kept in local time
  --directisa   access the ISA bus directly instead of /dev/rtc
  --badyear     ignore rtc's year because the bios is broken
  --date        specifies the time to which to set the hardware clock
  --epoch=year  specifies the year which is the beginning of the 
                hardware clock's epoch value

% hwclock --show 
Fri Mar 23 17:21:44 2001  -0.440942秒

一方、システムタイムはタイマー割り込みによって操作されるカーネル内部の クロックによって保持される時間です。システムタイムは1970年1月1日、 00:00:00からの秒数です。

ハードウエアクロックはLinuxが走っていない時、時間を保持する役目を 持ちます。Linuxが走り出す時システムタイムはハードウエアクロックの 時間に合わせてセットされます。そして再びハードウエアクロックが 使われることはありません。つまり、システムタイムとハードウエアクロックが 示す値はシステムスタート時は同じですが、その後独立して動作するものだと 思って下さい。 例えばつぎのようにハードウエアクロックとシステムタイムは値が異なることが 十分にあるのです。
% date
Sun Mar 25 17:32:29 JST 2001

% hwclock --show
Sun Mar 25 17:32:24 2001  -0.877934秒

デバイスを見ましょう。
% cat /proc/ioports
0000-001f : dma1
0020-003f : pic1
0040-005f : timer
0060-006f : keyboard
0070-007f : rtc
0080-008f : dma page reg
00a0-00bf : pic2
00c0-00df : dma2
00f0-00ff : fpu
02f8-02ff : serial(auto)
ここにはtimerとrtcがあります。 timerはintel8253タイマーチップというデバイスです。 rtcはMotorolaMC146818A(あるいはDallas DS12887)リアルタイムクロックチップ というデバイスです。

rtcはデバイス名が/dev/rtcでそのドライバコードが /usr/src/linux/drivers/char/rtc.cです。 また、/usr/src/linux/include/asm/mc146818rtc.hには
#define CMOS_READ(addr) ({ outb_p((addr),RTC_PORT(0)); inb_p(RTC_PORT(1)); })
#define CMOS_WRITE(val, addr) ({ outb_p((addr),RTC_PORT(0)); outb_p((val),RTC_PORT(1)); })
などが定義されていて、RTCポートから読みだしや書き込みが できるようになっています。
void __init time_init(void)
{
        extern int x86_udelay_tsc;
        
        xtime.tv_sec = get_cmos_time();
        xtime.tv_usec = 0;
		|
		|
}
xtime変数がこの中で初期化されています。get_cmos_time関数は CMOS_READやCMOS_WRITEマクロを使ってRTCレジスタから時間を読みだします。 この変数はgettimeofday/settimeofdayシステムコールの中で使われています。 (/usr/src/linux/arch/i386/kernel/time.c:do_gettimeofday/do_settimeofday) この変数は2038年にはオーバーフローしてマイナスの値になってしまいます。

timerはLinuxの時間管理とプロセススケジュールなどに利用されます。 HZマクロ変数は通常100と設定されてます。これは10ミリ秒に1回のタイマー 割り込みを発生させます。 1回のタイマー割り込みでjiffiesは1つ増加します。jiffiesはOSが起動されて からのクロックティック(clock tick)数です。unsigned long volatileとして 宣言されているので約1.3年(496日)で溢れます。 (/usr/src/linux/kernel/timer.c:unsigned long volatile jiffies;) このtimer.cはKernel internal timersやkernel timekeepingなどを行なう コードが入っています。 また、/usr/src/linux/arch/i386/kernel/time.c(PC-specific time handler) にはタイマー関連のコードが入っています。 この中のタイマー割り込みが発生するとtimer_interruptが呼ばれ、そこで do_timer_interruptが呼ばれ、さらにそこでdo_timerが呼ばれます。 do_timerは/usr/src/linux/kernel/timer.cにあります。do_timerの処理は 2つに分かれています。1段目の処理は割り込みハンドラとしての処理で、 ここではjiffiesの値に1を加えます。そしてカレントプロセスに対する 処理を行なってから、2段目の処理に入ります。これはボトムハーフ(BH)ハンドラ (timer_bhルーチン)としての処理で、カレンダーの更新を行ない、 xtime変数に設定します。