next up previous
次へ: ドライバの作成 上へ: システムコールの実装(その4: dio_ioctl) 戻る: サンプルプログラム

dio_ioctl関数の作成

これから作成するカーネル空間側のioctlドライバメソッドの名前を dio_ioctlとする。

dio_ioctlの書式は、L.D.Dの「5.1 ioctl」に合わせて、つぎのように する。
	int dio_ioctl(struct inode *inode, struct file *filp,
	              unsigned int cmd, unsigned long arg)

ポインタであるinodefilpは、アプリケーションが送ったファイ ル記述子fdに対応する値で、openメソッドに対して渡されたパラメー タと同じものになる。引数cmdはユーザから渡されたものがそのまま渡さ れる。オプションの引数argは、ユーザから整数で渡されてもポインタ で渡されても、メソッドにはunsigned longとして渡される。プログラム が第3引数を渡さなかった場合には、ドライバば受け取るargの値には意味が なくなる。

サンプルプログラムのpassでは、沢山用意されていたコマンドcmd の中からTCGETAやTCSETAFなどを選んで利用したが、dio_ioctl関数では、 新しく次の三つのコマンドを作ることにする。

  1. 汎用出力データのリセット(IOCTL_DIO_RESET)
  2. 汎用データの入力(IOCTL_DIO_INPUT)
  3. 汎用データの出力(IOCTL_DIO_OUTPUT)

この三つのコマンドは具体的には、次のような処理を行なうことにする。

  1. IOCTL_DIO_RESET: 汎用出力データ(OUT1 〜 OUT32)のリセット(クリア)
  2. IOCTL_DIO_INPUT: 開始点とデータサイズを指定して、1点単位で のデータ入力(ランダム入力)
  3. IOCTL_DIO_OUTPUT: 開始点とデータサイズを指定して、1点単位で のデータ出力(ランダム出力)
本実験では、CHK-2101(スイッチ & LED テストボード)を使うので、抽象的 な「1ビット単位」でなく、具体的な接点やLEDの「1点単位」という呼び方にし た。

サンプルプログラムではtermio構造体を参照したが、 dio_ioctl関数では、_DIO_POINT構造体を定義して参照する。

この構造体と用意したコマンドを、次頁のようなヘッダファイルとしてまとめる。 このヘッダファイルはカーネル空間のデバイスドライバとユーザ空間のアプリケー ションプログラムの両方で参照するので、dio_ioctl.hという名前で独立 したファイルにする。このヘッダーファイルは、これをインクルードするドライ バやアプリケーションのソースコードがあるディレクトリに置く。
\begin{boxedminipage}{\textwidth}
\begin{verbatim}...

_DIO_POINT構造体のそれぞれのメンバは、start_pointが入出力 の開始点、data_countが入出力点数(データサイズ)、data[32]が 入出力データ配列を指定している。

ioctlのコマンドは、コマンド番号で区別される。

用意したコマンドの IOCTL_DIO_RESETIOCTL_DIO_INPUTIOCTL_DIO_OUTPUTは、システムの中で同じ番号にならない唯一の 番号(ユニークな番号)を割り当てなければならない。

そのためには、L.D.Dの「5.1.1 ioctlコマンドの選び方」で説明されてい る_IO(type, nr)_IOR(type, nr, dataitem)_IOW(type, nr, dataitem)
_IOWR(type, nr, dataitem)などのマクロが使われる。

typeはマジック番号であり、ここでは'k'を使っている。これによ りカーネル空間内でユニークな番号が生成される。

本体のdio_ioctl関数の基本的な仕事は、次のようにswitch文を使っ て、このユニークな番号に対応するコマンドを選択することである。(次ページ 参照)

\begin{boxedminipage}{\textwidth}
\begin{verbatim}int dio_ioctl(struct inode...
... default:
return -EINVAL;
}return ret;
}\end{verbatim}\end{boxedminipage}

switch文で選択されたコマンドの実質的な処理はサブ関数(subioctl)で行 われる。dio_ioctlの最終的な戻値は、各サブ関数からの戻り値をそのまま 返す。

選択されたIOCTL_DIO_RESETコマンドは、次に示すdio_subioctl_reset関数で処理される。

\begin{boxedminipage}{\textwidth}
\begin{verbatim}int dio_subioctl_reset(PDI...
...es->io_address[0]);
predata = 0;
return 0;
}\end{verbatim}\end{boxedminipage}

この関数は、リセットを行うためにOUT1〜OUT32(LED33〜LED64)へデータの0を出 力している。 データの出力にはoutl関数を使っている。outl関数は、I/Oポート へロングデータ(ダブルワード)を書き込むカーネル関数である。上記の例では0 は32ビットのロングデータであり、pdio_res->io_address[0]はI/Oポー トアドレスである。なお、この処理では引数データのargは使っていない。

選択されたIOCTL_DIO_INPUTコマンドは、 dio_subioctl_input関数で処理される。この関数の基本構造を次に示す。
	int dio_subioctl_input(PDIO_RESOURCE pdio_res, unsigned long arg)
	{
	   DIO_POINT point;

	   if (copy_from_user(&point, (void *)arg, sizeof(DIO_POINT)))
	      return -EFAULT;
	   .
	   .
	   .

	   if (copy_to_user((void *)arg, &point, sizeof(DIO_POINT)))
	       return -EFAULT;

	    return 0;
	}

最初にcopy_from_user関数を思い出すこと、この関数の引数を *to, *from, sizeとすると、次のような意味を持っていた。

unsigned long copy_from_user(
 void           *to     // コピー先バッファアドレス(カーネル空間)
 const          *from   // コピー元バッファアドレス(ユーザ空間)
 unsigned long  size    // コピーサイズ
)

ランダム入力を可能にするために、このcopy_from_user関数は、最初に 引数*fromに対応するargからデータ構造体(_DIO_POINT)の取 出しを行っている。この構造体のなかにランダムアクセスを行うときに必要な開始点 (start_point)や入力点数を指定した情報(data_count)が格納さ れている。

copy_to_user関数を思い出すこと、この関数の引数を *to, *from, sizeとすると、次のような意味を持っていた。

unsigned long copy_from_user(
 void           *to     // コピー先バッファアドレス(ユーザ空間)
 const          *from   // コピー元バッファアドレス(カーネル空間)
 unsigned long  size    // コピーサイズ
)
所定の処理を行なったあとは最後にcopy_to_user関数により、 _DIO_POINT構造体のデータ(data[32])を、引数*toに対応するユーザ空間を示すargへコピーして処理を終了する。

ユーザ空間とのデータのやりとりには、入力も出力も共通のバッファである argを使用する。ユーザ空間側からは、ioctlを実行する前に、 構造体(_DIO_POINT)に開始点(start_point)と入力点数 (data_count)を指定してから関数を実行すると、必要なデータが (data[32])に格納されて返ってくることになる。

このdio_subioctl_input関数の基本構造を確認したら、ランダム入力の 機能を実現するために、次のような機能仕様を加えることにする。

この仕様に基づいてdio_subioctl_input関数を作成すると次のようにな る。

\begin{boxedminipage}{\textwidth}
\begin{verbatim}int dio_subioctl_input(PDI...
...of(DIO_POINT)))
return -EFAULT;return 0;
}\end{verbatim}\end{boxedminipage}

inl(pdio_res->io_address[0])は、I/Oポートアドレスの pdio_res->io_address[0]からロングデータ(ダブルワード)のデータを読 み出す。

選択されたIOCTL_DIO_OUTPUTコマンドは、 dio_subioctl_output関数で処理される。この関数の基本構造を次に示す。
	int dio_subioctl_output(PDIO_RESOURCE pdio_res, 
	                                      unsigned long arg)
	{
	   DIO_POINT point;

	   if (copy_from_user(&point, (void *)arg, sizeof(DIO_POINT)))
	      return -EFAULT;
	   .
	   .
	   .

	   return 0;
	}

ランダム出力の関数では、ユーザ空間へのデータ転送はない。
argからデータ構造体の取出しを行ない、ランダムアクセスに必要な、 開始点、入力点数、出力データを取得している。

このdio_subioctl_output関数の基本構造を確認したら、ランダム出力の 機能を実現するために、次のような機能仕様を加えることにする。

この仕様に基づいてdio_subioctl_input関数を作成すると次のようにな る。

\begin{boxedminipage}{\textwidth}
\begin{verbatim}int dio_subioctl_output(PDI...
..._address[0]);
predata = uldata;return 0;
}\end{verbatim} \end{boxedminipage}


next up previous
次へ: ドライバの作成 上へ: システムコールの実装(その4: dio_ioctl) 戻る: サンプルプログラム
MANOME Yoichi 平成18年12月25日