1. 程式人生 > >25 驅動裝置申請及原始碼實現裝置檔案建立一體函式(miscdevice)

25 驅動裝置申請及原始碼實現裝置檔案建立一體函式(miscdevice)

驅動裝置申請及原始碼實現裝置檔案建立一體函式(miscdevice)


miscdevice是字元裝置驅動的簡化版本,方便實現一個簡單的字元裝置驅動。
只適用於沒有同類型的裝置驅動,也就是一個驅動只對應一個硬體。


相關變數及函式:

#include <linux/miscdevice.h>

struct miscdevice  {
    int minor; //指定次裝置號,次裝置號為255,則會自分配空閒的次裝置號
               //主裝置號已固定為10
               //次裝置號在(0~255)之間
    const char
*name; //名字,指定的名字也是裝置檔案的名字,裝置檔案會在註冊時自動建立 const struct file_operations *fops; //檔案操作物件的地址 ... }; extern int misc_register(struct miscdevice * misc); //註冊miscdevice物件 extern int misc_deregister(struct miscdevice *misc);//反註冊

當我們實現miscdevice裝置驅動時,初始化一個miscdevice物件,呼叫misc_register(物件的地址)來註冊。


miscdevice的實現原理:

在核心原始碼drivers/char/misc.c檔案裡:

static int __init misc_init(void)
{
    int err;
    ...
    misc_class = class_create(THIS_MODULE, "misc"); //建立/sys/class/misc子目錄,稍後建立裝置資訊用
    ...
    if (register_chrdev(MISC_MAJOR, "misc", &misc_fops)) //當主裝置號為10,次裝置號(0~255)的所有裝置檔案開啟操作時,會呼叫misc_fops物件裡的open函式
        goto fail_printk;
    ...
} subsys_initcall(misc_init); //會在核心子系統初始化自動呼叫misc_init函式

misc_list是在misc.c檔案裡的一個全域性核心連結串列頭,用於通過miscdevice物件的list成員,存放所有的miscdevice物件。

int misc_register(struct miscdevice * misc)
{
    struct miscdevice *c;
    dev_t dev;
    int err = 0;

    INIT_LIST_HEAD(&misc->list);

    mutex_lock(&misc_mtx);
    //遍歷連結串列,確認指定次裝置號是否已用
    list_for_each_entry(c, &misc_list, list) {
        if (c->minor == misc->minor) {
            mutex_unlock(&misc_mtx);
            return -EBUSY;
        }
    }

    if (misc->minor == MISC_DYNAMIC_MINOR) {
    //當需要動態分配空閒的次裝置號時的操作
    ...
    }

    //建立裝置檔案
    dev = MKDEV(MISC_MAJOR, misc->minor);
    misc->this_device = device_create(misc_class, misc->parent, dev,
                      misc, "%s", misc->name);

    list_add(&misc->list, &misc_list); //通過list成員,把miscdevice物件加入misc_list連結串列
                                       //也可以在misc_list連結串列里根據次裝置號找到相應的miscdevice物件
 out:
    mutex_unlock(&misc_mtx);
    return err;
}

static const struct file_operations misc_fops = {
    .open       = misc_open,
    ...
};
//當主裝置號為10,次裝置號(0~255)的所有裝置檔案開啟操作時,會呼叫misc_fops物件裡的open函式
static int misc_open(struct inode * inode, struct file * file)
{
    int minor = iminor(inode); //獲取開啟的裝置檔案的次裝置號
    struct miscdevice *c;
    int err = -ENODEV;
    const struct file_operations *old_fops, *new_fops = NULL;

    mutex_lock(&misc_mtx);
    //遍歷misc_list連結串列,根據次裝置號minor找到對應的miscdevice物件
    list_for_each_entry(c, &misc_list, list) {
        if (c->minor == minor) {
            new_fops = fops_get(c->fops); //找到後,則用new_fops指定存放miscdevice物件的fops成員存放file_operations物件的地址,也就是我們自己miscdevice裝置驅動裡實現file_operatins物件的地址
            break;
        }
    }

    err = 0;
    old_fops = file->f_op;
    file->f_op = new_fops; //把file物件的f_op成員指向我們的file_operations物件的地址,以後我們的file_operations裡實現的函式就會得到呼叫

    //如果我們實現的file_operations物件裡有open函式,則呼叫
    if (file->f_op->open) {
        file->private_data = c;
        err=file->f_op->open(inode,file);
        if (err) {
            fops_put(file->f_op);
            file->f_op = fops_get(old_fops);
        }
    }
    fops_put(old_fops);
fail:
    mutex_unlock(&misc_mtx);
    return err;
}

測試程式碼(test.c):

#include <linux/init.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>

ssize_t myread(struct file *fl, char __user *buf, size_t len, loff_t *off)
{
    printk("in myread ...\n");
    return 0;
}

struct file_operations fops = {
    .read = myread,
};

struct miscdevice mymdev = {
    .minor = MISC_DYNAMIC_MINOR, //255
    .name = "mymdev",
    .fops = &fops,
};

static int __init test_init(void)
{
    return misc_register(&mymdev);
}

static void __exit test_exit(void)
{
    misc_deregister(&mymdev);
}

module_init(test_init);
module_exit(test_exit);

MODULE_LICENSE("GPL");