設備制御インターフェース(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構造について、ここでは新しい方法を紹介します。
int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
これはドライバデバイス制御インターフェース関数のカーネルプロトタイプの定義であり、struct inode*とstruct file*は操作ファイルを記述しています。unsigned intはioctlコマンド番号を記述しています。これは重要なパラメータです。後で詳細に紹介します。最後のパラメータはunsigned longデータタイプで、ioctlコマンドが持つ可能性のあるパラメータを記述しています。整数またはポインタデータかもしれません。
  • 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コマンド番号を生成します。
    _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コマンド番号を自動的に生成し、後の例で実際の使用シーンを知ることができる。
  •  
  • ioctl戻り値
  • ioctl関数の戻り値は整数タイプの値です。コマンド実行が成功すれば、ioctlはゼロに戻ります。エラーが発生したら、ioctl関数は負の値を返します。この負の値はerrno値としてこのioctlを呼び出すユーザ空間プログラムにフィードバックします。戻り値の具体的な意味については、<linux/errno.h>と<asm/errno.h>ヘッダファイルを参照してください。
  •  
  • ioctlパラメータ
  • ここでは、ioctlコマンドのパラメータを説明する必要があります。それはミスをしやすいからです。ioctl命令パラメータが整数だけなら、仕事は簡単です。ioctl関数で直接使用できます。しかし、もしポインタデータであれば、使用上は注意しなければなりません。まず、このパラメータはユーザ空間のプログラムによって渡されるので、このポインタが指すアドレスはユーザ空間アドレスであり、Linuxではユーザ空間アドレスは仮想アドレスであり、カーネル空間では直接に使用できない。カーネル空間でユーザ空間アドレスを使用するデータを解決するために、Linuxカーネルは、カーネル空間でユーザ空間にアクセスするためのデータを提供し、「asm/uccess.h」ヘッダファイルに定義される:
    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メカニズムを詳細に紹介します。