Linux驱动框架之misc类设备驱动框架
1、何为misc设备
(1)misc中文名就是杂项设备\杂散设备,因为现在的硬件设备多种多样,有好些设备不好对他们进行一个单独的分类,所以就将这些设备全部归属于
杂散设备,也就是misc设备,例如像adc、buzzer等这些设备一般都归属于misc中。
(2)需要注意的是,虽然这些设备归属于杂散设备中,但是其实你也可以不把设备放在这个类中,这都是驱动工程师按照自己的想法做的,你想把他们写在
misc类设备中也可以,自己单独建立一个类也是可以的,只不过是否标准化而已,因为人家既然建立了这个类,那你就把这个设备放在这个类下,不是很好吗?
你还自己单独搞一个类,虽然这也没错,只不过是说你不按套路出牌。
(3)所有的misc类设备都是字符设备,也就是misc类设备其实是字符设备中分出来的一个小类。
(4)misc类设备在应用层的操作接口:/dev/xxxx, 设备类对应在 /sys/class/misc
(5)misc类设备有自己的一套驱动框架,所以我们写一个misc设备的驱动直接利用的是内核中提供的驱动框架来实现的。misc驱动框架是对内核提供的原始的字符设备
注册接口的一个类层次的封装,很多典型的字符设备都可以归于misc设备,都可以利用misc提供的驱动框架来编写驱动代码,通过misc驱动框架来进行管理。
2、misc驱动框架源码分析
在内核中,misc驱动框架的源码实现在: driver/char/misc.c 相应的头文件在:include/linux/miscdevice.h
但是如果我们自己添加的misc类设备,那么驱动源文件最好放在 driver/misc 这个目录下,这个目录是官方推荐的目录
misc驱动框架和之前的led的驱动框架都是实现为一个模块的形式,在内核配置的时候可以进行动态的编译或者是不编译进内核当中。这样做的一个好处就是能够对内核
进行一个最大化的裁剪,将不需要的模块统统拿掉,能够使得内核在满足要求的情况下实现最小化。
(1)一个重要的结构体 struct miscdevice
- struct miscdevice {
- int minor; // 次设备号
- const char *name; // 名字
- const struct file_operations *fops; // file_operations 结构体指针
- struct list_head list; // 作为一个链表节点挂接到misc设备维护的一个链表头上去 misc_list
- struct device *parent; // 次设备的父设备
- struct device *this_device; // 本设备的device 结构体指针
- const char *nodename;
- mode_t mode;
- };
(2)misc_init函数分析
misc_init函数是misc驱动框架模块注册时的一个初始化函数,只有执行了初始化,我们才能够利用misc提供的框架来进行编写misc设备驱动程序和管理misc类设备。
misc_init函数是misc驱动框架的入口函数。
- static int __init misc_init(void)
- {
- int err;
- #ifdef CONFIG_PROC_FS /* CONFIG_PROC_FS用来控制我们的系统中是否需要proc虚拟文件系统 */
- proc_create("misc", , 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 字符设备 主设备号10 misc_fops*/
- goto fail_printk;
- misc_class->devnode = misc_devnode;
- return ;
- 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;
- }
proc文件系统在2.4版本中用的比较流行,现在主要用的就是sys文件系统,因为sys文件系统比proc文件系统做的更好,功能更加齐全,目录层次设计的很好
所以现在proc文件系统成为了一个可以选择添加或者删除的一个选项了,可以通过在内核配置的时候进行相应的配置。
(2)misc_register函数与misc_deregister函数
misc_register函数是misc驱动框架提供给驱动工程师编写misc类设备时的注册函数,一个重要的接口,misc_deregister就是相对应的卸载函数
- int misc_register(struct miscdevice * misc)
- {
- struct miscdevice *c; // 定义一个 miscdevice 结构体指针
- dev_t dev; // 设备号
- int err = ;
- 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; // 如果存在直接退出
- }
- }
- if (misc->minor == MISC_DYNAMIC_MINOR) { // misc->minor == 255 表示 自动分配次设备号
- int i = find_first_zero_bit(misc_minors, DYNAMIC_MINORS); // 在我们的misc类设备的驱动框架中使用了一种位来表示次设备号是否被占用的情况
- if (i >= DYNAMIC_MINORS) { // 使用8个字节的一个变量来表示,这个数据的每一位表示一个次设备号,
- mutex_unlock(&misc_mtx); // 第一位代表次设备号0 第二位代表次设备号1 ....... 如果这个位被置1表示已经被分配出去了,置0表示没有被分配出去
- return -EBUSY; // 所以这段代码就是在找一个最小的没有被使用被置1的位,
- } // 由此可知misc类设备最多只有64个
- misc->minor = DYNAMIC_MINORS - i - ; // 我们这里的意思就是我们是从小到大去寻找,那么分配就是从大到小,例如: 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, // 创建设备 /sys/devices/virtual/misc/xxx
- misc, "%s", misc->name);
- if (IS_ERR(misc->this_device)) {
- int i = DYNAMIC_MINORS - misc->minor - ;
- if (i < DYNAMIC_MINORS && i >= )
- 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.1)misc_list是misc驱动框架中提供的用来挂载所有的已经注册的misc设备的一个链表,当我们 cat /proc/misc 时查看系统中注册的所有misc类设备就是通过遍历
这个链表来实现的。与字符设备的用数组管理的方式一定要区分开来,misc设备的主设备号在这个数组中也占有一个位置,不要将他们之间的关系脱离了。
(2.2)对代码中宏的解析
#define LIST_HEAD_INIT(name) { &(name), &(name) }
#define LIST_HEAD(name) \
struct list_head name = LIST_HEAD_INIT(name)
原式子:static LIST_HEAD(misc_list);
展开后:static struct list_head misc_list = { &(misc_list), &(misc_list) } // 其实就是定义了一个链表,next指针和prev指针都指向本身
3、一些需要注意的细节部分
(1)misc_init函数中调用的注册字符设备的函数 register_chrdev
register_chrdev(MISC_MAJOR,"misc",&misc_fops),从这里可以看出来 misc_fops 就是传入的一个file_operations结构体,之前说过了这个结构体在注册
字符设备时的一个重要性,这里就不再重复了,misc_fops 如下:
static const struct file_operations misc_fops = {
.owner = THIS_MODULE,
.open = misc_open,
};
从上面可以看出来结构体中只实现了open函数,而没有实现其他的函数,因为具体的驱动实现的open、read、write函数在他们的file_operations结构体中,并不在这里实现,
我们需要通过这里的open函数去找到具体的要打开的硬件设备,然后找到他下面的file_operations结构体,调用结构体中实现的open函数,并且将要打开的设备的file_operations结构体替换当前要操作的这个结构体,之后我们就可以通过这个结构体来调用设备的其他操作函数,例如read、write....等函数。
为什么会有这样的一种操作模式呢? 其原因就是字符设备的管理的问题,调用register_chrdev函数一次就是注册了一个设备组,而这一个设备组共用了一个file_operations,所以打开这个
设备组中的任何一个设备节点最开始是对应到这个共用的file_operations,所以我们需要通过这个file_operations中的函数找到我们需要正真打开的设备的对应函数。
先来看看misc_open函数:
- static int misc_open(struct inode * inode, struct file * file)
- {
- int minor = iminor(inode); // 由传进了的inode结构体找到设备的次设备号 inode结构体之前说了它里面有一个元素记录的就是设备号,由上层传下来的,之前已经讲过
- struct miscdevice *c; // 定义一个miscdevice指针
- int err = -ENODEV;
- const struct file_operations *old_fops, *new_fops = NULL; // 定义两个file_operations指针
- mutex_lock(&misc_mtx); // 互斥锁上锁
- list_for_each_entry(c, &misc_list, list) { // 遍历我们的misc_list链表找到次设备号与当前需要打开的设备的次设备号相同的
- if (c->minor == minor) {
- new_fops = fops_get(c->fops); // 然后 获取这个设备的fops结构体 放入new_fops
- break;
- }
- }
- if (!new_fops) { // 这里是错误校验
- mutex_unlock(&misc_mtx);
- request_module("char-major-%d-%d", MISC_MAJOR, minor);
- mutex_lock(&misc_mtx);
- list_for_each_entry(c, &misc_list, list) {
- if (c->minor == minor) {
- new_fops = fops_get(c->fops);
- break;
- }
- }
- if (!new_fops)
- goto fail;
- }
- err = ;
- old_fops = file->f_op; // 将file中的fops先放在 old_fops , 这是用来以免后面出错的时候能够恢复的一种手段
- file->f_op = new_fops; // 将我们的获取到的fops放入 file->fops中,也就是替换 那么之后操作file时,对应的就是我们上面获取到的具体的设备的fops
- if (file->f_op->open) { // 如果我们的open函数存在
- file->private_data = c; // 将miscdevice变量指针c作为 file的私有数据
- err=file->f_op->open(inode,file); // 打开次设备的open函数
- 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;
- }
参考:《朱友鹏嵌入式Linux开发\5.Linux驱动开发\5.6.misc类设备与蜂鸣器驱动》
Linux驱动框架之misc类设备驱动框架的更多相关文章
- misc类设备
何为misc (1)中文名:杂项设备\杂散设备,它是一种典型的字符设,在一般情况下在内核中,所有的misc设备的主设备号是固定的,为10,它们的次设备号不一样:(2)可以在根文件系统中看到:/sys/ ...
- Linux设备驱动编程---miscdevice杂类设备的使用方法
miscdev简称杂类设备杂类设备就是对字符设备驱动做一个封装,方便简单使用杂类设备封装字符设备需要包含的头文件:#include <linux/miscdevice.h>(1)杂类设备的 ...
- Linux I2C核心、总线和设备驱动
目录 更新记录 一.Linux I2C 体系结构 1.1 Linux I2C 体系结构的组成部分 1.2 内核源码文件 1.3 重要的数据结构 二.Linux I2C 核心 2.1 流程 2.2 主要 ...
- linux 块设备驱动 (三)块设备驱动开发
一: 块设备驱动注册与注销 块设备驱动中的第1个工作通常是注册它们自己到内核,完成这个任务的函数是 register_blkdev(),其原型为:int register_blkdev(unsigne ...
- usb驱动开发4之总线设备驱动模型
在上文说usb_init函数,却给我们留下了很多岔路口.这次就来好好聊聊关于总线设备驱动模型.这节只讲理论,不讲其中的函数方法,关于函数方法使用参考其他资料. 总线.设备.驱动对应内核结构体分别为bu ...
- Linux 驱动框架---cdev字符设备驱动和misc杂项设备驱动
字符设备 Linux中设备常见分类是字符设备,块设备.网络设备,其中字符设备也是Linux驱动中最常用的设备类型.因此开发Linux设备驱动肯定是要先学习一下字符设备的抽象的.在内核中使用struct ...
- 嵌入式Linux学习笔记(三) 字符型设备驱动--LED的驱动开发
在成功构建了一个能够运行在开发板平台的系统后,下一步就要正式开始应用的开发(这里前提是有一定的C语言基础,对ARM体系的软/硬件,这部分有疑问可能要参考其它教程),根据需求仔细分解任务,可以发现包含的 ...
- 如何编写一个简单的Linux驱动(二)——完善设备驱动
前期知识 1.如何编写一个简单的Linux驱动(一)——驱动的基本框架 2.如何编写一个简单的Linux驱动(二)——设备操作集file_operations 前言 在上一篇文章中,我们编写设备驱动遇 ...
- 乾坤合一~Linux设备驱动之USB主机和设备驱动
如果不能陪你到最后 是否后悔当初我们牵手 如果当初没能遇见你 现在的我 在哪里逗留 所有的爱都是冒险 那就心甘情愿 等待我们一生中 所有悬念 我一往情深的恋人 她是我的爱人 她给我的爱就像是 带着露水 ...
随机推荐
- HTTP请求中的form data,request payload,query string parameters以及在node服务器中如何接收这些参数
http://www.cnblogs.com/hsp-blog/p/5919877.html 今天,在工作(倒腾微信小程序)的时候,发现发送post请求到node后台服务器接收不到前端传来的参数.其实 ...
- ZedGraph控件的使用 --归类(转帖)
在我们编写程序的时候,有时候是要做一些统计的,为了达到一目了然的效果,饼状图,曲线图,柱状图都是很好的表现统计的直观形式.这个时候,ZedGraph控件给我们带来了极大的方便. 1.下载ZedGrap ...
- android基于adb的性能测试
应用CPU.内存.点亮.流量等数据,是应用测试的重要性能指标.本次以UC浏览器为例. 当前UC测试中,每个正式版本都会专门做一轮性能测试,测试数据包括: 1小时内存数据 1小时CPU数据 24小时电量 ...
- python的with用法
转自http://blog.kissdata.com/2014/05/23/python-with.html With语句是什么? 有一些任务,可能事先需要设置,事后做清理工作.对于这种场景,Pyth ...
- git/github基本命令
Git与项目 git的使用,主要包括: 本地仓库的命令 远程仓库的命令 项目需求.页面.模型类的设计,及页面的使用 sudo apt-get install git 安装成功后,运行如下命令 git ...
- Ubuntu无法sudo提权,报当前用户不在sudoers文件中错误
Ubuntu安装后默认root不能登陆系统,密码也是随机生成,其他用户使用root权限,可以使用sudo提权,前提是该用户在/etc/sudoers配置列表中. 但是有时用户名从/etc/sudoer ...
- xml 注释中不允许出现字符串“--“(再也不要来坑爹了,好么,XML)
转自:https://blog.csdn.net/randomnet/article/details/18708575?utm_source=blogxgwz3 关于xml文件时出现中文注释出错的一个 ...
- git_基本使用
1.默认你已经安装了,git的客户端,这里我们使用git bash操作. 2.执行git init命令: git ini 3.在本地创建ssh key: ssh-keygen -t rsa - ...
- UNITY 中List.Sort的BUG
List<int> lst = new List<int>(); lst.Add(); lst.Add(); lst.Add(); lst.Add(); lst.Add(); ...
- layer插件open方法回掉值问题
最近做项目需用到一个弹出层加载iframe页面的东西,首先想到layer插件,此插件用到过多次,兼容性很好,功能也强大,废话不多说上代码. 其实功能很简单,就是在目标页面选择一个值,回掉回来,说明一下 ...