Linux字符设备驱动框架(三):Linux内核的misc类设备驱动框架

/************************************************************************************

*本文为个人学习记录,如有错误,欢迎指正。

*本文参考资料: 

*        https://www.cnblogs.com/deng-tao/p/6042965.html

*        https://blog.csdn.net/armwind/article/details/52166139

************************************************************************************/

1. misc类设备驱动框架概述

misc类设备,即杂项设备,所有的misc类设备都是字符设备,其主设备号固定为10。因为现在的硬件设备多种多样,有好些设备不好对他们进行一个单独的分类,所以就将这些设备全部归属于misc类设备,譬如adc、buzzer等这些设备一般都归属于misc类设备中。misc类设备在应用层的操作接口:/dev/xxxx,设备类对应在 /sys/class/misc。

Linux内核中提供了一套misc类设备驱动框架,所以我们写一个misc设备的驱动直接利用的是内核中提供的驱动框架来实现的;misc类设备驱动通常嵌套在platform 总线驱动中,配合总线驱动达到更复杂,多功能的效果。misc类设备驱动框架和之前的LED设备驱动框架都是实现为一个模块的形式,在内核配置的时候可以进行动态的编译或者是不编译进内核当中。使用misc类设备驱动框架之前,需确保Linux内核支持misc类设备驱动框架。进入Linux内核的配置界面menuconfig进行设置,具体配置如下:

  Device Drivers  --->

    [*] Misc devices  ---> 

misc类设备驱动框架的核心文件:

                /kernel/ drivers/char/misc.c

                /kernel/include/linux/miscdevice.h

2. misc类设备驱动框架分析

2.1 创建misc设备类

misc类设备驱动框架使用subsys_initcall宏修饰misc_init()函数,因此misc_init()函数在内核启动阶段被调用。

misc_init函数是misc类设备驱动框架模块注册时的一个初始化函数,只有执行了初始化,我们才能够利用misc类设备驱动框架来进行编写misc类设备驱动程序和管理misc类设备。

static const struct file_operations misc_fops =
{
.owner    = THIS_MODULE,
.open    = misc_open,
};


static int __init misc_init(void)
{
    int err;

#ifdef CONFIG_PROC_FS                                //CONFIG_PROC_FS用来控制我们的系统中是否需要proc虚拟文件系统
    proc_create("misc", 0, NULL, &misc_proc_fops);   //在proc文件系统下创建一个名为 misc 的文件
#endif
    misc_class = class_create(THIS_MODULE, "misc");  //在sys文件系统下创建misc设备类
    err = PTR_ERR(misc_class);
    if (IS_ERR(misc_class))
        goto fail_remove;

    err = -EIO;
    if (register_chrdev(MISC_MAJOR,"misc",&misc_fops))//注册misc字符设备,主设备号为MISC_MAJOR = 10
        goto fail_printk;
    misc_class->devnode = misc_devnode;
    return 0;

fail_printk:
    printk("unable to get major %d for misc devices\n", MISC_MAJOR);
    class_destroy(misc_class);
fail_remove:
    remove_proc_entry("misc", NULL);
    return err;
}

subsys_initcall(misc_init);

register_chrdev(MISC_MAJOR,"misc",&misc_fops),从这里可以看出来 misc_fops 就是传入的一个file_operations结构体;misc_fops结构体中只实现了open函数,而没有实现其他的函数,因为具体的驱动实现的open、read、write函数在具体的misc类设备中的file_operations结构体中,并不在这里实现。系统通过这里的open函数去找到具体的要打开的硬件设备,然后找到该设备下的的file_operations结构体,调用结构体中实现的open函数,并且将要打开的设备的file_operations结构体替换当前要操作的这个结构体,之后我们就可以通过这个结构体来调用设备的其他操作函数,例如read、write....等函数。

2.2 misc类设备数据结构分析

在注册misc类设备之前,需要先定义并初始化一个struct miscdevice结构体变量,该结构体包含了该misc类设备的所有信息。

struct miscdevice  
{
    int minor;                          //次设备号
    const char *name;                   //设备名称
    const struct file_operations *fops; //设备操作集
    struct list_head list;              //作为一个链表节点挂接到misc设备维护的一个链表头上去
    struct device *parent;              //父设备
    struct device *this_device;         //本设备的device基类
    const char *nodename;
    mode_t mode;
};

struct miscdevice结构体变量的一个实例。

static struct miscdevice buzzer_device = 
{
    .minor = BUZZER_MINOR, //次设备号
    .name = BUZZER_NAME,   //设备名字宏定义
    .fops = &buzzer_fops,  //文件操作指针集合
};

2.3 misc类设备的注册

misc_register()函数是misc驱动框架提供给驱动工程师编写misc类设备时的注册函数。

int misc_register(struct miscdevice * misc)
{
    struct miscdevice *c;  //定义一个 miscdevice结构体指针
    dev_t dev;             //设备号
    int err = 0;

    INIT_LIST_HEAD(&misc->list);   //初始化链表
    mutex_lock(&misc_mtx);         //上锁
    list_for_each_entry(c, &misc_list, list) 
  {    
                                //遍历misc_list 链表,查找是否存在次设备号与当前注册的设备的次设备号相同的
        if (c->minor == misc->minor) 
     {
            mutex_unlock(&misc_mtx);
            return -EBUSY;        //如果存在直接退出
        }
    }

/*在我们的misc类设备的驱动框架中使用了一种位来表示次设备号是否被占用的情况。使用8个字节的一个变量来表示,这个数据的每一位表示一个次设备号,第一位代表次设备号0,
第二位代表次设备号1,以此类推。
 如果这个位被置1表示已经被分配出去了,置0表示没有被分配出去。所以这段代码就是在找一个最小的没有被使用被置1的位,由此可知misc类设备最多只有64个。
*/ if (misc->minor == MISC_DYNAMIC_MINOR) // misc->minor == 255 表示 自动分配次设备号   { int i = find_first_zero_bit(misc_minors, DYNAMIC_MINORS); if (i >= DYNAMIC_MINORS)      { mutex_unlock(&misc_mtx); return -EBUSY; } misc->minor = DYNAMIC_MINORS - i - 1; //我们这里的意思就是我们是从小到大去寻找,那么分配就是从大到小,例如: i=0 ,minor=63 i =1,minor=62 set_bit(i, misc_minors); //然后将该位置1 } dev = MKDEV(MISC_MAJOR, misc->minor); //使用主次设备号合成设备号 misc->this_device = device_create(misc_class, misc->parent, dev, misc, "%s", misc->name); //创建设备/sys/devices/virtual/misc/xxx if (IS_ERR(misc->this_device))   { int i = DYNAMIC_MINORS - misc->minor - 1; if (i < DYNAMIC_MINORS && i >= 0) clear_bit(i, misc_minors); err = PTR_ERR(misc->this_device); goto out; } /* * Add it to the front, so that later devices can "override" * earlier defaults */ list_add(&misc->list, &misc_list); //将 misc->list 作为节点挂接到 misc_list 链表上去 out: mutex_unlock(&misc_mtx); return err; }

2.4 misc类设备的注销

misc_deregister就是相对应的misc类设备的注销函数。

int misc_deregister(struct miscdevice *misc)
{
    int i = DYNAMIC_MINORS - misc->minor - 1;

    if (list_empty(&misc->list))
        return -EINVAL;

    mutex_lock(&misc_mtx);
    list_del(&misc->list);
    device_destroy(misc_class, MKDEV(MISC_MAJOR, misc->minor));
    if (i < DYNAMIC_MINORS && i >= 0)
        clear_bit(i, misc_minors);
    mutex_unlock(&misc_mtx);
    return 0;
}

3. misc类设备驱动框架应用实例

详见驱动程序实例(三):蜂鸣器驱动程序(misc类设备驱动框架)

猜你喜欢

转载自www.cnblogs.com/linfeng-learning/p/9437904.html