目次

キャラクタ型デバイスドライバの作成概略

 普通のソフトウェアのプログラムとデバイスドライバのプログラムの最大の違いは標準ライブラリが使用できないという点に帰着される。もちろん、C++やクラスライブラリも利用できない。

 実際、デバイスドライバのプログラムの宣言を見てみる。

1:「/usr/src/linux/drivers/」ディレクトリ内にドライバ群が保存されている。

[root@garoa ipusiron]# cd /usr/src/linux/drivers/
[root@garoa drivers]# ls
Makefile  block/  dio/   macintosh/  nubus/  s390/  sgi/    telephony/  zorro/
acorn/    cdrom/  fc4/   misc/       pci/    sbus/  sound/  usb/
ap1000/   char/   isdn/  net/        pnp/    scsi/  tc/     video/

2:CD-ROMドライブのデバイスドライバが気になる方はそれを見ればよい。ここではPCIのデバイスドライバに注目することにする。

[root@garoa drivers]# cd pci
[root@garoa pci]# ls
Makefile  compat.c  oldproc.c  pci.c  pcisyms.c  proc.c  quirks.c
[root@garoa pci]# more pci.c
/*
 *      $Id: pci.c,v 1.91 1999/01/21 13:34:01 davem Exp $
 *
 *      PCI Bus Services, see include/linux/pci.h for further explanation.
 *
 *      Copyright 1993 -- 1997 Drew Eckhardt, Frederic Potter,
 *      David Mosberger-Tang
 *
 *      Copyright 1997 -- 1999 Martin Mares <mj@atrey.karlin.mff.cuni.cz>
 */

#include <linux/config.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/pci.h>
#include <linux/string.h>
#include <linux/init.h>
#include <linux/malloc.h>

#include <asm/page.h>

#undef DEBUG

 「#inlcude<linux/*.h>」のところがデバイスドライバの宣言部分である。

キャラクタ型デバイスドライバのデバイス処理について

 デバイスに対する処理は次の5種類存在する。

  1. open
    • この処理で必ずやるべきことはMOD_INC_USE_COUNTというマクロを実行して、カーネルに対して自分の存在を知らせるということである。
    • この処理をすることによって、カーネルに対して、このモジュールの登録を取り消すことができなくなる。
    • その後に、必要な初期処理を行います。例えば、デバイスの設定、割り込みハンドラの処理化、必要なメモリの確保などが挙げられる。。
    • そして、「printk("start device");」と命令を発行することで、printkの出力をログに記録できる。これは浮動小数点が使えないという点以外は標準ライブラリのprintfとほぼ同じである。
  • close
    • この処理で必ずやるべきことはMOD_DEC_USE_COUNTというマクロを実行して、カーネルに対して自分の消滅を知らせることである。
    • この処理をすることによって、カーネルに対して、このモジュールの登録を取り消すことができる。
  • read
    • 基本的に、デバイス側の出データが存在するアドレスからデバイスファイルの使用できるアドレスに対して、データをコピーすることでread処理は完結する。
    • しかしながら、プロセス処理のポインタはユーザーエリアのメモリ空間であり、ハードウェア側のメモリ空間はカーネルエリアである。ただ、プロセス間のポインタが存在するメモリ空間がどこを指しているかはハードウェアによっても異なることがあるので、プログラムはそれを把握しておく必要がある。
    • 単純にmemcpyを使用するか、copy_to_userを使用するのかはその状況に依存する。
  • write
    • readとは逆に、デバイスファイルのデータが存在するアドレスからデバイス側アドレスにデータをコピーすることでwrite処理を完結する。
    • readと同じく、memcpyが使用するか、copy_from_userを使用するのかはその状況に依存する。
    • writeはカーネル側のメモリ空間を書き換えるので、バグが存在する場合、必ずカーネルパニックを起こしてしまう。よって、バックアップを取りながらデバッグする必要があるだろう。
  • ioctl
    • この命令はよほど複雑なことをしない限り必要ない。
    • 例えば、何らかの理由でシリアルポートの設定をデバイスドライバの中で変更しなければならないときなどに使用する。即ち、デバイスドライバの中から別のデバイスドライバを呼び出すときに使う。

[補講]普通のプログラミングでもタイミング合わせに無意味なループやタイム待ちループを入れることは好ましくないが、デバイスドライバ処理の中に書くとほぼ確実にハングアップしてしまう。そうなってしまうと、スケジューラを呼び出してから、自ら休眠させなくてはならない。しかしながら、タイムアウトまたはシグナル受信で復帰しない限り、休眠しっぱなしという困った自体に陥ってしまう。 ◇

Linuxのデバイスドライバ用のAPIやマクロ

スケジューリング

 schedule()はプロセスの再スケジューリングを行います。カーネルは現在の実行プロセスの中でより優先度の最も高いプロセスを選択して、そのプロセスを実行可能状態にする。この関数を選んだプロセス以外に制御が渡った場合、呼び出し元プロセスはスリープ状態にする。

 sleep_on()は自プロセスをスリープ状態にして、他のプロセスを実行可能状態にする。この関数は割り込み禁止状態で発行しなければならない。この状態になったプロセスはwake_up()が発行されるか、タイムアウトになるまで実行が止まる。

 ドライバsleep_on()を発行する必要がある場合の例として、モデムにATコマンドを発行してその返事を待つ時などが当てはまる。

 wake_up()はプロセスをスリープ状態から解放して、プロセスを実行状態にする。

ハードウェアへのアクセス

 /usr/src/linux-2.2.14/include/asm-i386/io.hにマクロが定義(#define)されている。

メモリの確保

 普通はmallocを使用しますが、デバイスドライバでは次を使用する。

void *vmalloc(size_t);
void vfree(const void *);

参考文献

  • 『Interface Feb,2002』
  • 『日経Linux 1999,12月号』