デバイスドライバとは
デバイスドライバとは,パソコンに接続されている周辺装置をOSによって制御するために用意されたプログラムである。
OSからの要求を特定の周辺機器コントローラが理解できるよう翻訳することによって,OSとハードウェアを結ぶものである.
Linuxでは,デバイスをファイルとして扱える.よって,ユーザはファイル操作命令でデバイスを操作できる.
ファイル操作命令のような一般的な命令に対して各々のデバイスドライバは,その命令に対応した独自の関数で処理している.
デバイスドライバには以下の種類がある.
- ブロック型ドライバ
- キャラクタ型ドライバ
- ネットワークインターフェースデバイスドライバ
また,デバイスドライバはカーネルと一体となって動作するので,libcは使えない.
プログラムの説明
作成したNULLデバイスの説明をする.全体のソースはここから -->
hello.c
宣言部
以下の宣言は,modinfoというコマンドでそこに記述した情報が表示されるもので,
プログラムの説明である.MODULE_LICENSE を記述しないとinsmodのときに警告
が出る.
14 MODULE_LICENSE("GPL");
15 MODULE_AUTHOR(DRIVER_AUTHOR);
16 MODULE_DESCRIPTION(DRIVER_DESC);
file_operations構造体
25 struct file_operations hello_fops =
26 {
27 .owner = THIS_MODULE
28 };
ユーザからのファイル操作命令をデバイスドライバ独自の命令(関数)と結びつける役割がある.
つまり,関数へのポインタを集めたテーブルのようなものである.
例として,ユーザがデバイスファイルをopen命令で開こうとすると,
file_operations構造体に登録された .open = xxx というxxx関数が呼び出される.
今回はヌル・デバイスドライバなので,特に設定していない.
static int hello_init(void)
insmodされたときに呼び出される関数.ここで,メジャー番号,マイナー番号を割り当てて,
デバイスの登録を行う.つまり,モジュールの初期化を行っている.
31 static int hello_init(void)
32 {
・
・
KDEVでデバイス番号の作成を行っている.デバイス番号はメジャー番号12ビット +
マイナー番号20ビットの32ビットで表される.次にregister_chrdev_region関
数でデバイス番号の割当を行い,成功したら0を返す.もし,メジャー番号を0に
して動的割当にするなら,alloc_chrdev_region関数を使う.
36 dev_number=MKDEV(major,minor); /*デバイス番号の生成*/
37 if(MAJOR(dev_number)){
38 result = register_chrdev_region(dev_number, DEVICE_INSTANCE, devname);
39 } else
40 result = alloc_chrdev_region(&dev_number, 0, DEVICE_INSTANCE, devname);
cdev_alloc関数でcdev構造体のメモリ確保を行っている.cdev構造体はキャラ
クタデバイスを表す構造体で,これがカーネルに登録される.失敗することもあ
るのでそのときは,先ほど確保したデバイス番号を開放する.
opsはfile_operations 構造体をさすポインタで,これでデバイスドライバがファイル操
作命令を受け取ることができる.kobject_set_nameは名前の通り,kobjectとい
う基本的なデータ型に名前を付けている.また,cdevのownerフィールドも設定
しなければならない.これはアンロードの時のモジュールの保護に使われる.
49 device = cdev_alloc(); /* cdev構造体の領域の確保*/
・
/*cdev構造体の初期化*/
55 device->ops = &fops;
56 kobject_set_name(&device->kobj, "simple_cdev%d",dev_number);
57 device->owner=THIS_MODULE;
cdev_addでカーネルへデバイスの登録を行う.
59 result = cdev_add(device, dev_number, DEVICE_INSTANCE); /*デバイスの登録*/
・
65 return 0;
66 }
また,75行目にある
75 module_init(hello_init);
は,マクロでこれでinit_module(マクロを使わなければこの名前を使わなければならない)
に好きな名前が付けられる.
static int hello_exit(void)
ここは,rmmodが実行されたときに呼び出される関数である.
cdev_delでカーネルからデバイスドライバを取り除く.
unregister_chrdev_region()でデバイス番号を開放する.
71 cdev_del(device);
72 unregister_chrdev_region(dev_number, );
また,76行目にある
76 module_exit(hello_exit);
は,マクロでこれでclean_module(マクロを指定しなければこの名前にしないといけない)
に好きな名前が付けられる.
デバイスドライバの追加手順
まず,kernelのコンパイル時のconfigで,「Loadable module support」項目の
「Module unloading」を有効にしないと,rmmodできないのでこれを有効にしてkernelはコンパイルしていることが前提.
デバイスドライバプログラムがあるディレクリに以下のMakefileを作成する.
Makefile
ifneq ($(KERNELRELEASE),)
obj-m := hello.o
else
KERNELDIR ?= /lib/modules/$(shell unname -r)/build
PWD := S(shell pwd)
default:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
endif
あとは,そのディレクリでmakeをするとコンパイルできる.
%make
コンパイルするとhello.koというモジュールオブジェクトファイルが作成される.
そして,スーパーユーザになって,以下のコマンドを実行してモジュールをロードする.
%/sbin/insmod hello.ko
ロードしたモジュールは以下のコマンドで確認できる./proc/devices
はロードしているモジュール名とモジュール番号が確認できる.
%cat /proc/devices
Character devices:
・
・
1000 hello
・
ロードしたモジュールをアンロードするには以下のコマンドを実行する.すると,
アンロードしたことが確認できる.
%/sbin/rmmod hello.ko
%cat /proc/devices
Character devices:
・
・
ロードしたモジュールを利用するには,デバイスファイルを作成しなければならない.
デバイスファイルを消すには,普通にrmコマンドを使えば良い.コマンドは以下の通りである.
rmはモジュールがアンロードしてから行うと良い.
%/bin/mknod /dev/xxx c 1000 0
%ls -l /dev/hello
%rm /dev/hello
%ls -l /dev/hello
私たちのプログラムはprintkを用いて,文字列を出力していた.これは,以下のコ
マンドで確認できる.dmesgはリングバッファの表示ができる.
%dmesg | tail -3
hello, world!
Success register
good bye!bye!
ノード番号を指定するファイル
メジャー番号は,/usr/src/linux/inclue/linux/major.h にある.
ロードされているモジュールは,/proc/devices に記述されている.また,
/usr/src/linux/Documentation/devices.txt にはメジャー番号やマイナー番号
の割当が記述されている.
考察
カーネルのバージョン2.6からデバイスドライバの登録方法が新しくなっているが,
2.4でも使われていたregister_chrdev()でも登録できるようになっている.
また,sysfsという仮想ファイルシステムにはデバイスドライバ情報、ドライバクラス情報、
バス情報があり,sysfsをマウントするとその情報が参照できる.
起動時にマウントされていなければ,以下のようにしてマウントできる.
%mount -t sysfs sysfs 適当なディレクトリ
また,2.6ではメジャー番号が,今まで8ビットで割り当てていたものが12ビットとなり,
1~4095まで割り当てることができる.0は動的割当となる(今まで通り).
マイナー番号も20ビットと拡張されている.