[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
/Documentation/kernel/pseudo/index.list
伊藤誠@札幌市北区です。
以前、訳してみて、寝かせておいた
/Documentation/kernel/pseudo/index.list
です。
そもそも C の本を読んでいない人なので、用語の変換がわからないとか、
中身も理解できていなかった(現在に至る..だけど)ので、
頓珍漢かもしれません。
寝せた甲斐があって、投稿にあたって多少直せましたが、
依然、要ツッコミです。よろしく。
---- Documentation/kernel/pseudo/index.list ----
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML>
<HEAD>
<!-- Copyright (c) 2000
The NetBSD Foundation, Inc. ALL RIGHTS RESERVED. -->
<!--
== By Brett Lymn
-->
<link rev="made" href="mailto:www@JP.NetBSD.ORG">
<TITLE>Writing a pseudo device</TITLE>
</HEAD>
<BODY bgcolor="#FFFFFF" text="#000000">
<HEADING>Writing a pseudo device
<LIST>
<SECTION> 擬似デバイスの書き方
<ENTRY>intro 序説
このドキュメントはカーネルドライバーを書いてみようという方へのガイドです。
ここではシンプルな擬似デバイスドライバーの書き方を扱います。
カーネルの構築、makefile 関連や新しいカーネルのインストールに関する他の奥義
はこれらはこのドキュメントには含まれてなく(別に)必要です
また、カーネルプログラミングそれ自体も含まれていなく
- これは、ユーザーレベルプログラミングとは多くの面でとても違います。
一通り書いたので、このドキュメントであなたのコードを
カーネルに加えて動作させることができるようになります。
<ENTRY>yourcode あなたのコード
<A HREF="pseudo_dev_skel.c">pseudo_dev_skel.c</A> ファイルが
擬似デバイスとファイルの骨格となります。
<A HREF="pseudo_dev_skel.h">pseudo_dev_skel.h</A>では
kernel 関数プロトタイプと ioctl データ構造 と加えて ioctl 番号自体を定義します。
注として、普通のデバイスドライバーと違って、
擬似デバイスドライバーには、プローブルーチンは必要ないので持っていません。
この質素な生活は autoconfig フレームワークを扱う必要がないからです。
この skeleton ファイルは擬似デバイスの open, close, ioctl calls をサポートします。
これは、実際の擬似デバイスにおいて最低限実用的なコールのセットです。
read, write, mmap や ほかの デバイス 関数 をサポートするためのほかのコールもありますが、
詳細を除けば全て open, close, ioctl のパターンと同様に扱えます。
<p>
たぶん、最初の重要な決定は、新しいデバイスをなんと呼ぶかを決めることです。
関数コール名の前にデバイス名をくっつけた kernel 構造体をつくるのに
便利なマクロがたくさんあることが知られていて、これは
作りたいカーネルコンフィグファイルエントリーの助けになるでしょう。
config ファイル エントリー は ソース コード ファイル 名 と合わせる必要はありません。
この skeleton ドライバー では、 "skeleton" 擬似デバイスと呼ぶ事にしたので
skeleton というエントリーを コンフィグファイルに作る事になります。
つまり、 attach, open, close, ioctl 関数 呼び出しcall の名前を、それぞれ
skeletonattach, skeletonopen, skeletonclose, skeletonioctl とすると言う事です。
もう1つの重要なのはデバイスの種類を決めることで -
あなたのコードが、カーネルと、もちろん、あなたのコード自体と、どう相互作用するかで
キャラクター型 か ブロック デバイス のどちらかにします。
ブロック型対キャラクター型のデバイスの決定は、
ドライバーが会話する根底の ハードウェアーにかなり依存し、
もしドライバーがデータの読書きを一定の塊で行なうデバイスなら
ブロックデバイスが良い選択で、そのようなデバイスの例として、
ハードディスクはたいてい 512 byte セクターをブロックとして読み書きします。
ハードウェアーが一度に読み書きするのが 1 byte のデータなら、
普通、キャラクター型デバイスが最もよい選択で、
例えば、シリアルラインドライバーがそうです。
注として、いくつかのドライバーはデバイスのアクセスに
ブロックモードとキャラクターモードの両方をサポートしていて、
この場合、キャラクターモードはしばしば "raw" デバイスと呼ばれます。
それは、アクセス時に
データブロック抽出操作(the data blocking abstractions operating) なしに
ハードウェアーのアクセスができるからです。
擬似デバイスでは、考慮するべき根底のハードウェアーがないので、より柔軟に選べます
擬似デバイスを使ってする内容により選択し、ブロック型デバイスは
ハードディスクやそのようなもののエミュレートには便利でしょう。
skeleton ドライバーは キャラクター型デバイスです。
<p>
それを決定したら、コードを編集することができますが、
その前にファイルをどこに置くか決めることが必要です。
複数のアーキテクチャーで使われる擬似デバイスを書くなら、
そのドライバーのコードの置き場は <TT>/usr/src/sys/dev</TT> です。
もし擬似デバイスが個別のアーキテクチャーに特有の物なら、
ドライバーのコードは個別のディレクトリーの下に置き、
i386 の例では、 <TT>/usr/src/sys/arch/i386/i386</TT> です。
インクルードファイルについては、
アーキテクチャー非依存のデバイスは <TT>/usr/src/sys/sys</TT> に、
アーキテクチャー特有のデバイスでは、
アーキテクチャー個別のディレクトリーの下の <TT>include</TT> ディレクトリー、
例えば i386 アーキテクチャーでは
<TT>/usr/src/sys/arch/i386/include</TT> に置くべきです。
どちらの場合でも、適切な <TT>Makefile</TT> を更新してください。
それで、インクルードファイルがインストールされます。
<A HREF="pseudo_dev_skel.c">pseudo_dev_skel.c</A> の先頭に
<STRONG>struct skeleton_softc</STRONG> を書いてください。
デバイス名に "_softc" が書き添えられた名前で softc 構造体の宣言が必要で、
この構造体の最初の要素は <STRONG>struct device</STRONG> 型である必要があり、
エントリー名は重要ではありませんが、最初にある必要があり、
autoconfig システムが softc struct が宣言されていることをあてにしているから
(????これ本当????)で、その最初の要素は struct device です。
デバイスハンドル minor 番号 ごとに softc struct が必要です。
softc 構造体は minor デバイスがそれを維持する状態の情報を、必要に応じて
ただの struct device より多くの要素を持つことができます。
<ENTRY>functions 関数
ユーザーレベルプログラムによるアクセスには
カーネルインターフェイスはデバイスに対しては関数のコールを通して行ないます。
デバイスは後に示すように全ての関数をサポートする必要はありませんが、
実用的なデバイスは最低限オープンとクローズをサポートする必要はあります。
関数名はデバイス名を前につけることを思い出してください。
この関数は:
<P>
<OL TYPE=i>
<LI><h4>attach</h4>
この関数はカーネルが初期化するときに一度呼び出されます。
これは、あらゆる変数の設定に使われ、
後のコールや、バッファーが必要とするカーネルメモリー割り当て
で参照されます。
attach 関数は 数あるデバイスのうちの1つのパラメーターを扱い、
このドライバーはハンドルを求めます。???????????
<P>
<LI> <h4>open</h4>
その名が示すように、ユーザーレベルプログラムが
デバイスに対して open(2) コールをした時に呼び出されます。
一番簡単な場合、 open 関数は単に success で戻るだけです。
より一般に open call は
バッファーの要求と可能なら allocate することや
他ドライバー関数のコールをサポートするため
他のドライバーの状態を初期化することを有効にします。
open call は下記のパラメーターが使え:
<P><DL>
<DT>dev
<DD> open しようとするデバイスのマイナー番号
<P>
<DT>flags
<DD> ??? ユーザーによる open call のフラグを素通し ???
<P>
<DT>mode
<DD> ??? open の mode ???
<P>
<DT>proc
<DD> オープン要求をしているプロセスの proc 構造体のポインター
これは、そのプロセスが
信頼できるものである(資格証明を有効にしてよい)ことを示します。
</DL>
<LI> <h4>close</h4>
open されていたデバイスを close します。
ドライバーによって、これはただ単純に success で戻るだけのものだったり、
あるいは、割り当てられていたメモリーの解放や
ドライバーの状態変数を、もはや open されてはいないと示すように変更する
必要があったりします。
close 関数呼び出しのパラメーターは、 open で記述したのと同じです。
<P>
<LI> <h4>read</h4>
デバイスからデータを読み出します。
この関数のパラメーターは:
<P><DL>
<DT>dev
<DD> デバイスのマイナー番号
<DT>uio
<DD> uio 構造体へのポインターです。
read 関数は uio 構造体 にユーザーが欲しいデータを満たして戻ります。
<DT>flags
<DD> ??? wuffor ??
</DL><P>
<LI> <h4>write</h4>
デバイスにデータを書きこみます。
write 関数 のパラメーターは、 read 関数のものと同じで - ただ、違うのは
uio 構造体はデバイスに書きこまれるデータを含んでいることです。
<P>
<LI> <h4>ioctl</h4>
デバイスに ioctl をします。
ioctl call のパラメーターは:
<P><DL>
<DT>dev
<DD> デバイスのマイナー番号
<DT>cmd
<DD> 実行される ioctl コマンドです。
このコマンドはヘッダーファイル内で定義され
カーネルコードおよびユーザーレベルコードが参照します。
例はサンプルのヘッダーを見てください。
<DT>data
<DD> ユーザーレベルコードで使えるパラメーターのポインターです。
このパラメーターは ioctl の実装と、実際に発行されたコマンドに依存します。
<DT>flags
<DD> ??? wuffor ???
<DT>proc
<DD> ioctl 要求を出したユーザーレベルプロセスに関連する proc 構造体 です。
</DL><P>
<LI><h4>stop</h4>
tty 様式デバイスで出力を停止します。
<P><DL>
<DT>tty
<DD> デバイスに結び付けられた tty
<DT>flags
<DD> ???
</DL><P>
<LI> <h4>poll</h4>
デバイスからデータが読めるかチェックします。
パラメーターは:
<P><DL>
<DT>dev
<DD> デバイスが使っているマイナー番号
<DT>events
<DD> ユーザーレベルコールがポーリングするイベント
<DT>proc
<DD> ioctl 要求を出したユーザーレベルプロセスに関連する proc 構造体 です。
</DL><P>
<LI> <h4>mmap</h4>
ドライバーのバッファーをユーザーレベルプログラムのメモリー空間に mmap する
権限をサポートします。
パラメーターは:
<P><DL>
<DT>dev
<DD> デバイスが使っているマイナーデバイス番号
<DT>offset
<DD> mmap 開始時のバッファーの先頭からのオフセット。
<DT>prot
<DD> mmap の挙動のタイプで、
読みこみ専用、書きこみ専用、読み書き可能かのいずれかです。
デバイスドライバーは全てをサポートする必要はありません。
</DL></OL><P>
<ENTRY>newdevice カーネルに新しいデバイスを知らせる
擬似デバイスのコーディングをしたら、
アクセスできるよう、カーネルに組み込みます。
注として、カーネルが擬似デバイスを組みこむ方法は
通常のデバイスの場合と、いろいろ違います。
擬似デバイスは、通常のデバイス検出と auto-configuration を飛ばし、
実行時の代わりにソースレベルでカーネル構造体の中にエントリーを作ったり
そうではなかったりするからです。
カーネルがあなたのコードを使うようにするには、
これらのファイルを変更する必要があって:
<P>
<OL TYPE=i>
<LI> <h4>/usr/src/sys/sys/conf.h</h4>
このファイルには
cdevsw (character device switch) と bdevsw (block device switch) テーブルエントリー
を設定する 定義マクロが入ってます。
あなたが書いているデバイスのタイプがわかっている必要があります。
我々の例において、 skeleton ドライバーはキャラクター型デバイスなので、
エントリーを作る必要があります。
また, skeleton ドライバーは open, close と ioctl のコールだけをサポートしています。
<I>conf.h</I> ファイルに目を通してみると
called <TT>cdev__oci_init</TT> と、まさに私が欲しい(方法で)
定義されている一般デバイスが見つかります。
これは、 <I>conf.h</I> に定義を追加するのにたくさん打ちこむことを省かせてくれて:
<PRE>
#define cdev_skeleton_init(c,n) cdev__oci_init(c,n)
</PRE>
これは、ほかのファイルが cdevsw エントリー を定義できるように、マクロを定義します。
もっと複雑なドライバー では、単にほかの定義の1つをコピーして、
必要に応じ変更を加えればよいです。
<P>
<LI> <h4>/usr/src/sys/arch/i386/i386/conf.c (ほかの arch ではどこ?)</h4>
<I>conf.h</I> に cdevsw 初期化エントリー を作れば、
次のステップの設定をします。
最初にすることはインクルードファイルに 入れ、
エントリーのプロトタイプを設定することです。
これをするには次のコードを置けば良く:
<PRE>
#include "skeleton.h"
cdev_decl(skeleton)
</PRE>
ちょっと待った! skeleton.h は作ってないよ!
その通りです。そのファイルは作っていません。
それは、 config(8) でつくられ、後ほど、どうやる(なる)のか出てくるでしょう。
2行目は skeleton ドライバーの関数プロトタイプの設定で -
skeleton は あなたの擬似デバイスの名前に置き換えます。
それは 宣言を処理します。
bdevsw/cdevsw テーブルの中にデバイスを追加することが必要です
skeleton は cdev なので、 cdevsw 配列 を見つけ、
エントリーを追加する必要があります。
その配列の最後にエントリーを追加するべきで
- cdevsw テーブルの途中に追加すると、ほかのデバイスドライバーを妨害します。
それで cdevsw テーブルの最後にこういうエントリーを追加します:
<PRE>
cdev_skeleton_init(NSKELETON,
skeleton), /* 71: Skeleton pseudo-device */
</PRE>
繰り返しますが、 NSKELETON は私達はどこにも定義していません。
config(8) を実行すると、中に、 NSKELETON が定義された
<I>skeleton.h</I> ファイルができ、
シンボルは作られたデバイスの数だけ定義され - その数は config ファイルに由来します。
注として <TT>cdev_skeleton_init</TT> は
<I>conf.h</I> で定義したマクロで、
2番目のパラメーター ("skeleton") は擬似ドライバーの名前です。
このマクロは我々のコードで定義した関数名を作るために
擬似デバイス名と関数コール名(open, close, ioctl, etc)を連結します。
これは、カーネルがあなたのコードを走らせることを知る方法です。
パズルの最後のかけらは、そこにあるエントリーのコメントにあるその次の番号です。
ほかのエントリーの形式をそのままコピーして、
コメント内の番号を増やしデバイスの関数を書きこむことが必要です。
この番号は重要です。この番号はデバイスの major number で、
後のために、書きとめておく必要があります。
</OL>
<ENTRY>config config<b></b>(8) に新しいデバイスを知らせる
config(8) に新しい擬似デバイスを知らせるには、
<TT>/usr/src/sys/conf/files</TT> ファイルを編集します。
このファイルは 有効な デバイスの名前 と
これらのデバイスと関連するファイルを config に教えます。
最初に擬似デバイスを定義するセクションを捜します。
この セクションは <TT>defpseudo</TT> で始まる行を持っています。
正しいセクションを見つけると、こういう行を加えることができ:
<PRE>
defpseudo skeleton
</PRE>
config(8)に skeleton という擬似デバイスがあることを教えます。
つぎに、 config(8) に skeleton 擬似デバイスに関連するファイルを教えてあげる必要があります。
この場合
on (訳注 one ???)ファイル
があるだけですが、より複雑な擬似デバイスでは より多くのファイルがあるかもしれず、
同じ方法で必要とされるファイルごとに行を単純に追加していきます。
例では、このような1行が必要なだけで:
<PRE>
file dev/skeleton.c skeleton needs-flag
</PRE>
行中の <B><TT>file</TT></B> はデバイスとファイルの関係の定義
を書くためのキーワードです。
2番目のフィールドは、ファイルの
カーネルソースツリールートからの相対位置 (通常、 <TT>/usr/src/sys</TT>) です。
3番目のフィールドはドライバー名でこのファイルが関連しているもので、
この場合 skeleton - サンプル擬似デバイスです。
4番目 で 最後の フィールド は config(8) に
skeleton.h インクルードファイルを書かせるための制御フラグです。
<ENTRY>kernelconfig カーネルコンフィグファイルに新しいデバイスを追加する
config(8) にそのデバイスのことをしゃべらせるようにするために
カーネルコンフィグファイル に追加することは簡単です。
skeleton デバイスを追加するには、このような行を加え:
<PRE>
pseudo-device skeleton
</PRE>
カーネルコンフィグファイルには、
前節での <TT>defpseudo</TT> 行で与えられた名に揃えた
擬似デバイス名を書きます。
新しい定義は
<B><TT>options</TT></B> カーネルコンフィグファイル キーワードをつかうことで、
カーネル makefile に追加することができ、
config は cc コマンドに対し -D コマンドラインオプションを指定した
makefile を作ります。
<ENTRY>userlevel ユーザーレベルプログラム が新しいデバイスにアクセスすることを許す
新しいカーネルを構築しインストールした後、
最後にすることとして、新しい擬似デバイスにアクセスできるように
デバイスノードを作る必要があります。
そのデバイスノードはアクセスすることができる
どんなファイルシステム上に作ることもできますが、
慣習によって、 デバイスノードは <I>/dev</I> に作られます。
デバイスノード をつくるために mknod(8)を使う必要があり、
4.ii 節で書きとめた major 番号で デバイスノード を作ります。
この場合、 mknod(8) コマンドでこのようにでき:
<PRE>
mknod /dev/skel c 71 0
</PRE>
これで、新しいデバイスをオープンして試すことができます。
<A HREF="sample.c">sample.c</A> ファイルは
skeleton 擬似デバイスが元気であることを示します。
このファイルは、ここにある指示に従っていて
<TT>/dev/skel</TT> が作られていると仮定していて、
このデバイスはオープンされ、パラメーター構造体は
ioctl call でデバイスドライバーに移されます。
サンプルコードをコンパイルするには、コマンドラインで:
<PRE>
cc -o sample sample.c
</PRE>
これで、 sample というバイナリーができるでしょう。
<STRONG>注意</STRONG> ヘッダーファイル置き場で、 <TT>pseudo_dev_skel.h</TT> を入れた
システムインクルードファイルのディレクトリーで
<TT>make includes</TT> をする必要があります。
さもなければ、コンパイラーはインクルードファイルが見つからないと訴えるでしょう。
プログラムをコンパイルして実行すると、
カーネルメッセージがコンソールと <TT>/var/log/messages</TT> の両方に現われ、
それはこのようなもので:
<PRE>
May 17 20:32:57 siren /netbsd: Got number of 42 and string of Hello World
</PRE>
skeleton ioctl 要求を受けた時に skeleton ioctl ハンドラーが表示する
メッセージの番号と文字列は sample.c の <TT>param</TT> 構造体の中に入れます。
</LIST>
<DOCLINK>
<hr>
<address>
<small>
<a href="../../../Misc/feedback.html">(Contact us)</a>
$NetBSD: index.list,v 1.5 2000/09/21 11:24:39 dent Exp $<br>
<a href="../../../Misc/disclaimer.html">Copyright ©
2000
The NetBSD Foundation, Inc. ALL RIGHTS RESERVED.</a>
</small>
</address>
</BODY>
</HTML>
----
ITOU Makoto aaa44850@pop01.odn.ne.jp