27 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);//反注册


测试代码, 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");

//编译加载驱动后,设备文件会自动创建.
// miscdevice设备驱动实现起来比较简单,但无法实现一个设备驱动支持多个同类的设备.因miscdevice只能指定一个次设备号, 也就一个设备号。同类的设备是由不同的次设备号区分开的。所以miscdevice只适用于只有一个设备的设备驱动,例如: RTC, 看门狗.

//
miscdevice的工作原理.

首先可修改file对象的f_op指针,指向另一个file_operations对象的地址。修改后,新的file_operations对象里的操作函数会得到调用.
测试代码:

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

#define MYMA 4095

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

struct file_operations fops2 = {
    .read = myread2,
};

int myopen(struct inod *ind, struct file *fl)
{
    fl->f_op = &fops2; //在open函数里修改file对象的f_op成员,指向fops2对象的地址.
               //当read函数调用时,则fops2对象的read函数会得到调用.
    return 0;
}

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 = {
    .open = myopen,
    .read = myread,
};

static int __init test_init(void)
{
    int ret;

    ret = register_chrdev(MYMA, "mydev", &fops);
    return ret;
}

static void __exit test_exit(void)
{
    unregister_chrdev(MYMA, "mydev");
}


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函数.

///
当我们实现miscdevice设备驱动时,初始化一个miscdevice对象, 调用misc_register(对象的地址)注册.
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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值