Tech と Culture

テクノロジーとカルチャー

IOCTL

write,read ハンドラが動作しましたので、次は、ioctl です。
write, read は、どのように動作するのかが一意に決められていましたが、ioctlでは、どのようにデータを受け渡してどのように動作するかをユーザーが自由に決めることができます。

とりあえず、YCbCr-RGB変換モジュールのAHB slave側レジスタを読む関数、16個のunsigned intを書き込む関数、モジュールがready信号を出している時に16個のunsigned intを書き込む関数、モジュールの設定レジスタを書き換える関数等を作成してみます。

まず最初に、kmjpeg.h というインクルードファイルを作ります。ioctlでデータの受け渡しは構造体で行いますが、その構造体定義はアプリケーション側でも使用しますので、インクルードファイルにしておき、デバイスドライバだけではなく、アプリケーションからもインクルードできるようにします。

ioctl1.png
最初に受け渡すデータの構造体を定義しています。
次にioctlが、どのデバイスドライバを指示しているか、どのようなデータを受け渡そうとしているかを定義します。
IOC_MAGICというのは、このデバイスドライバ固有の文字を定義します。
$SNAPGEAR_HOME/linux-2.6.xx.x/Documentation/ioctl-number.txt
を参照し、使用されていない文字として k を選びました。

次に関数をコールするときの識別子を定義します。
ここで定義されている IOCTL_WRITE 等をアプリケーション側からコールする際に使用します。
例: ioctl(fd, IOCTL_WRITE, cmdw);

これはマクロが準備されていますので、それにしたがって順番に定義していきます。
_IOR, _IOW _IOWR, _IO はデータのやりとりの方向を示しています。
次に順番に番号を割り振り、その後、データやり取りに使用する構造体を示します。
ここらへんはおまじないのように使えば良いと思います。

次にデバイスドライバ側の kmjpeg.c を作成します。

ioctl2.png

switch(cmd) で先ほど決めた識別子毎に処理内容を記述していきます。
ioctlは構造体のポインタ渡しで自由にデータを送ることができます。とは言っても、そこで変なポインタがカーネル側に送られた時、最悪の場合はカーネルパニックでリブートが必要になります。
そのため、渡された構造体のユーザー空間のポインタを使ってアクセスしても大丈夫かをまずチェックします。access_okの一文がそのために記述されています。
IOCTL_READは、YCbCr-RGB変換モジュールからアプリケーションがデータを読み取る関数ですが、アプリケーションから見ると、カーネル空間からデータが書き込まれる動作となります。そこで、access_okの最初の引数が VERIFY_WRITEとなっています。ちなみに、VERIFY_WRITEはVERIFY_READのスーパーセットとなっていますので、読み書き両方の動作がある場合は、VERIFY_WRITEにします。

つぎにチェックするポインタをしめし、3番目の引数にサイズを示します。ここで、_IOC_SIZEは、cmdからサイズを逆引きするマクロですのです。
アクセスに問題がなければ、read ハンドラと同様に、copy_to_userでデータを渡します。

次に示すのは、データ16個を書き込む関数です。先ほどの関数と似たようなデータの受け渡しをした後に、すでにinit関数でioremapされているアドレスに連続して書き込んでいます。

ioctl3.png

次は、YCbCr-RGB変換モジュールがready信号を出すまで待機してからデータを16個書き込む関数です。実際のシステムは、FIFOに16個のデータが溜まればフレームバッファに書き出します。また、AHBのアービタはラウンドロビンとなっていますので、本当は16個単位でデータを送る場合にはreadyのチェックは必要ではありません。しかし、今後のことを考え実装し実験してみました。このように読み書き両方を自由なフォーマットで行えることが、ioctlの利点だと思われます。
ioctl4.png
ただ単に条件が成り立つまでループしているだけです。

その他、画面サイズを示すレジスタを書き換えるものも作りました。このように自由に作ることができます。
ioctl5.png

最後にレジスタを指定できるものも作りました。
ioctl6.png