【組み込みLinux駆動開発】三、文字設備駆動(一)


1.基本手順
(1)主設備番号と副設備番号を確定する
(2)文字ドライバの実装
  • 実装file_operations構造体;
  • 初期化関数を実現し、文字デバイスを登録する.
  • は関数を破棄し、文字デバイスを解放することを実現する.
  • は、文字デバイスの他の基本メンバー関数を実装する.

  • (3)デバイスファイルノードの作成
    2.プライマリ・デバイス番号/セカンダリ・デバイス番号
    プライマリ・デバイス番号は、カーネルがデバイスを識別する識別子です.これは整数(12ビットを占める)で、通常は1~255を使用します.
    セカンダリ・デバイス番号は、デバイス・ファイルが指すデバイスを正確に特定するためにカーネルで使用されます.これも整数(20ビットを占める)であり、通常は0~255が用いられる.
    注意:同じ種類のデバイスのプライマリ・デバイス番号は同じで、セカンダリ・デバイス番号は異なります.複数のシリアルポートのプライマリ・デバイス番号が同じである場合、セカンダリ・デバイス番号は異なります.
    3.デバイス番号の内部表現
    (1)dev_tタイプ(32ビット):デバイス番号を保存する
    (2)dev_からt取得者、サブデバイス番号:
  • MAJOR(dev_t):   //メイン
  • MINOR(dev_t);   //次
  • (3)プライマリ・セカンダリ・デバイス番号をdev_に変換tタイプ:
  • MKDEV(int major, int minor);

  • 4.設備番号の割当
    通常、モジュールロード関数で呼び出されます.
    (1)手作業で割り当てる:カーネルが使用していないプライマリ・デバイス番号を探して使用する
    #include<linux/fs.h>
    int register_chrdev_region(dev_t first, unsigned int count, char *name);
    //first    ,       0;count          ,       ;name        。

    (2)動的割当て:
    #include<linux/fs.h>
    int alloc_chrdev_region(dev_t *dev, unsigned int firstminor, unsigned int count, char *name);
    //dev       ;firstminor           ;count          ;name        。

    5.デバイス番号のリリース
    通常、モジュールクリーンアップ関数で呼び出されます.
    #include<linux/fs.h>
    void unregister_chrdev_region(dev_t dev, unsigned int count);

    6.  重要な構造体
    (1)cdev構造体
    struct cdev
    {
        struct kobject kobj;    //   kobject  
        struct module *owner;    //    
        struct file_operations *ops;    //       
        struct list_head list;
        dev_t dev;    //   
        unsigned int count;
    };

    cdev構造体の関数を操作します.
    void cdev_init(struct cdev *cdev, struct file_operations *ops);
    //         cdev  ,   cdev file_operations     
    
    struct cdev *cdev_alloc(void);
    //        cdev  
    
    int cdev_add(struct cdev *cdev, dev_t num, unsigned int count);
    //       cdev,         ,            
    
    void cdev_del(struct cdev *cdev);
    //    cdev,         ,            

    (2)file_operations構造体
  • は、include/linux/fs.hで定義された文字駆動およびカーネルのインタフェースである.
  • 文字ドライバはfileを1つ実現します.operations構造体;

  • file_operationsの主要メンバー:
    struct module*owner:モジュール自体を指す(THIS_MODULE)
    Open:デバイスを開く
    release:デバイスを閉じる
    read:デバイスからデータを読み出す
    write:デバイスにデータを書き込む
    ioctl:I/O制御関数
    llseek:現在の読み書き位置ポインタの位置決め
    mmap:プロセスへのデバイス空間のマッピングのアドレス空間
    (3)file構造体
  • file_Openations構造に関連する構造体で、開いているデバイスファイルを記述します.

  • fileのメンバー:
    loff_t  f_pos:現在の読み書き位置
    unsigned  int  f_flags:ファイルが開いたときに読み取り可能または書き込み可能かどうかを識別します.O_RDONLY、O_NONBLOCK、O_SYNC
    struct  file_operations *f_op:実装されたstructへのファイル関連操作  file_operations
    void  *private_data:プライベートデータポインタ、ドライバはこのフィールドを任意の目的で使用したり無視したり(NULLに設定)することができます.
    (4)inode構造体
  • カーネルはinode構造で内部にファイルを表す.
  • inodeとfileの違い:fileは開いているファイル記述子を表し、開いているファイル記述子を表す複数のfile構造は1つのinode構造を指すことができる.

  • inodeの重要なメンバー:
    dev_t  i_rdev:デバイスファイルを表すinode構造で、このフィールドには本物のデバイス番号が含まれています.
    struct  cdev  *i_cdev:カーネル内の文字デバイスの内部構造を表す
    7.文字デバイスドライバテンプレート
    //            
    static int __init xxx_init(void)
    {
        ...
        cdev_init(&xxx_dev.cdev, &xxx_fops);    //   cdev
        xxx_dev.cdev.owner =  THIS_MODULE;
        //        
        if(xxx_major)
        {
            register_chrdev_region(xxx_dev_no, 1, DEV_NAME);
        }
        else
        {
            alloc_chrdev_region(&xxx_dev_no, 0, 1, DEV_NAME);
        }
        ret = cdev_add(&xxx_dev.cdev, xxx_dev_no, 1);    //    
        ...
    }
    //            
    static void __exit xxx_exit(void)
    {
        unregister_chrdev_region(xxx_dev_no, 1);    //        
        cdev_del(&xxx_dev.cdev);    //    
        ...
    }