設備制御インターフェース(ioctl関数)
5542 ワード
来た:http://www.cnblogs.com/noaming1900/archive/2010/10/20/1856581.html
http://www.cnblogs.com/geneil/archive/2011/12/04/2275372.html
はじめに、デバイスドライバの基本的な機能は、ユーザアプリケーションの管理と制御デバイスのインタフェースを提供することである。前の「Hello World」ドライバはもう読み書き機能を提供できます。ここでは私達の駆動を拡張して設備コントロールインターフェースをサポートします。Linuxではこのインターフェースはioctl関数で実現します。
デバイスコントロールインターフェース(ioctl関数)を使って、私たちが文字デバイスドライバで紹介したstruct fileを思い出してください。operations構造について、ここでは新しい方法を紹介します。 ioctlコマンド番号ioctlコマンド番号は、この関数の中で最も重要なパラメータであり、それが記述されているioctlが処理するコマンドです。Linuxでは32ビットのデータを使用して、ioctlコマンドを符号化します。これは4つの部分を含みます。dir、type、nr、size。 dir: データ転送の方向を表します。2桁を占めます。IOCNONE(無数のデータ転送、0 U)、_IOCWRITE(デバイスにデータ、1 U)または_IOCREAD(デバイスからデータを読み、2 U)や彼らの論理や組み合わせはもちろん、_だけです。IOCWRITEと_IOCREADのロジックは意味があります。 type: ioctl命令の種類、8桁を述べました。各デバイスまたはシステムは自分のタイプ番号を指定できます。このタイプのioctlコマンドが属するデバイスまたはドライバを表します。普通はASCII文字で'a'などと表します。 nr: ioctl命令番号は、一般8桁です。指定されたデバイスドライバに対して、そのioctlコマンドを逐次コード化します。一般的にゼロから始まります。このコードはioctlコマンドのシーケンス番号です。 size: ioctlコマンドのパラメータサイズは、一般14桁です。Octlコマンドのこのデータのメンバーは強制的に使わなくてもいいですが、このデータのメンバーを指定してください。これによって、エラーを回避するためにユーザ空間データのサイズを確認できます。また、古いバージョンに対応するioctlコマンドも実現できます。私たちは自分で直接にioctlコマンド番号を指定できます。これは整数セットだけかもしれませんが、Linuxのioctlコマンド番号には特定の意味がありますので、通常は推奨しません。Linuxカーネルは既に対応するマクロを提供しています。自動的にioctlコマンド番号を生成します。 ioctl戻り値 ioctl関数の戻り値は整数タイプの値です。コマンド実行が成功すれば、ioctlはゼロに戻ります。エラーが発生したら、ioctl関数は負の値を返します。この負の値はerrno値としてこのioctlを呼び出すユーザ空間プログラムにフィードバックします。戻り値の具体的な意味については、<linux/errno.h>と<asm/errno.h>ヘッダファイルを参照してください。 ioctlパラメータ ここでは、ioctlコマンドのパラメータを説明する必要があります。それはミスをしやすいからです。ioctl命令パラメータが整数だけなら、仕事は簡単です。ioctl関数で直接使用できます。しかし、もしポインタデータであれば、使用上は注意しなければなりません。まず、このパラメータはユーザ空間のプログラムによって渡されるので、このポインタが指すアドレスはユーザ空間アドレスであり、Linuxではユーザ空間アドレスは仮想アドレスであり、カーネル空間では直接に使用できない。カーネル空間でユーザ空間アドレスを使用するデータを解決するために、Linuxカーネルは、カーネル空間でユーザ空間にアクセスするためのデータを提供し、「asm/uccess.h」ヘッダファイルに定義される:
http://www.cnblogs.com/geneil/archive/2011/12/04/2275372.html
はじめに、デバイスドライバの基本的な機能は、ユーザアプリケーションの管理と制御デバイスのインタフェースを提供することである。前の「Hello World」ドライバはもう読み書き機能を提供できます。ここでは私達の駆動を拡張して設備コントロールインターフェースをサポートします。Linuxではこのインターフェースはioctl関数で実現します。
デバイスコントロールインターフェース(ioctl関数)を使って、私たちが文字デバイスドライバで紹介したstruct fileを思い出してください。operations構造について、ここでは新しい方法を紹介します。
int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
これはドライバデバイス制御インターフェース関数のカーネルプロトタイプの定義であり、struct inode*とstruct file*は操作ファイルを記述しています。unsigned intはioctlコマンド番号を記述しています。これは重要なパラメータです。後で詳細に紹介します。最後のパラメータはunsigned longデータタイプで、ioctlコマンドが持つ可能性のあるパラメータを記述しています。整数またはポインタデータかもしれません。_IO(type,nr)
_IOR(type,nr,size)
_IOW(type,nr,size)
_IOWR(type,nr,size)
マクロ_。IOは無数のデータ転送に使用され、マクロ_IORはデバイスからデータを読むために用いられます。マクロ_IOWは設備にデータを書くために用いられ、マクロ_IOWRは、データを読み書きするIOCTLコマンドを同時に使用する。これに対して、Linuxカーネルは、対応するマクロを提供して、ioctlコマンド番号から対応するドメイン値を復号する:_IOC_DIR(cmd)
_IOC_TYPE(cmd)
_IOC_NR(cmd)
_IOC_SIZE(cmd)
これらのマクロは、<asm/ioctl.h>ヘッダファイルに定義されています。使用中は、まず各IOCTLコマンドの順序番号を指定し(一般的には0から開始する)、そして使用環境に応じてこれらのマクロでIOCTLコマンド番号を自動的に生成し、後の例で実際の使用シーンを知ることができる。unsigned long __must_check copy_to_user(void __user *to,
const void *from, unsigned long n);
unsigned long __must_check copy_from_user(void *to,
const void __user *from, unsigned long n);
copy_。fromuserとcopy_to_userは、一般的に、複雑または大きなデータ交換のために使用され、簡単なデータタイプについて、intまたはcharのように、カーネルは、簡単なマクロを提供して、この機能を実現する。#define get_user(x,ptr)
#define put_user(x,ptr)
では、xはカーネル空間の簡単なデータタイプアドレスであり、ptrはユーザ空間アドレスポインタである。私達はしっかり覚えておく必要があります。カーネルの中でユーザーの空間アドレスデータに直接アクセスすることはできません。したがって、ユーザ空間から伝達されるポインタデータは、必ずカーネルが提供する関数を使用してそれらにアクセスします。ここでもう一度強調する必要があるのは、カーネルモジュールやドライバの作成において、カーネルが提供するインターフェースを使って、ioctlコマンド番号を生成して操作することを強く勧めます。これはコマンド番号に特定の意味を与えて、プログラムをより強固にします。一方で、プログラムの移植性を向上させることもできる。例を挙げたら、時には例を挙げます。私たちは私たちのハロルド駆動にioctl関数を追加します。まず、Octlインターフェースに必要なデータ(hello.h)を定義するためのイニシャルファイルを追加します。#ifndef _HELLO_H
#define _HELLO_H
#include <asm/ioctl.h>
#define MAXBUF 20
typedef struct _buf_data{
int size;
char data [MAXBUF];
}buf_data;
#define HELLO_IOCTL_NR_BASE 0
#define HELLO_IOCTL_NR_SET_DATA (HELLO_IOCTL_NR_BASE + 1)
#define HELLO_IOCTL_NR_MAX (HELLO_IOCTL_NR_GET_BUFF + 1)
#define HELLO_IOCTL_SET_DATA _IOR('h', HELLO_IOCTL_NR_SET_DATA, buf_data*)
#endif
、その後、私たちのドライバにioctlインターフェースhelloを追加します。ioctlは、この関数を実現しました。static int hello_ioctl (struct inode *inode, struct file *filp,
unsigned int cmd, unsigned long arg)
{
int cmd_nr;
int err;
buf_data buff;
err = 0;
cmd_nr = _IOC_NR (cmd);
switch (cmd_nr){
case HELLO_IOCTL_NR_SET_DATA:
if (copy_from_user(&buff, (unsigned char *)arg, sizeof(buf_data)))
{
err = -ENOMEM;
goto error;
}
memset(hello_buf, 0, sizeof(hello_buf));
memcpy(hello_buf, buff.data, buff.size);
break;
default:
printk("hello_ioctl: Unknown ioctl command (%d)
", cmd);
break;
}
error:
return err;
}
static struct file_operations hello_fops = {
.read = hello_read,
.write = hello_write,
.open = hello_open,
.ioctl = hello_ioctl,
.release = hello_release,
};
の後記はここでLinuxカーネルドライバのデバイス制御インターフェースを示しました。その使用を詳しく紹介し、実際の例を示しました。それは簡単ですが、十分です。ここに行けば標準的なLinuxドライバが作成できます。しかし、ここではまだ問題があります。つまり、私たちは設備番号をファイルから読み取り、手動でデバイスノードを作成しなければなりません。このデバイスノードファイルを自動的にシステムに作成させてもいいですか?もちろんいいです。でもその前に、Linuxの設備駆動モデルを深く理解しなければなりません。Linuxのデバイス駆動モデルとHotplugメカニズムを詳細に紹介します。