misc子系统
跟着内核学框架-从misc子系统到3+2+1设备识别驱动框架
misc子系统在Linux中是一个非常简单的子系统,但是其清晰的框架结构非常适合用来研究设备识别模型。本文从misc子系统的使用出发,通过了解其机制来总结一套的设备识别的驱动框架,即使用使用同一个驱动,向上提供多个设备文件接口,向下控制多个(相应的)设备,这就需要该驱动可以根据不同的设备文件来控制与之相应的设备。
misc的使用
Linux 中有三大类设备:字符,网络,块设备,每一种设备又细分为很多类,比如字符设备就被预先分为很多种类,并在文件中标记了这些种类都使用了哪个主设备号,但即便如此,硬件千千万,总还是有漏网之鱼,对于这些难以划分类别的字符设备,Linux中使用"混杂",设备来统一描述,并分配给他们一个共同的主设备号10,只用此设备号进行区分设备,,这些设备主要包括随机数发生器,LCD,时钟发生器等。此外,和很多同样是对cdev进行再次封装的子系统一样,misc也会自动创建设备文件,免得每次写cdev接口都要使用class_create()和device_create()等。
内核中提供的misc对象:
//include/linux/miscdevice.h
55 struct miscdevice {
56 int minor;
57 const char *name;
58 const struct file_operations *fops;
59 struct list_head list;
60 struct device *parent;
61 struct device *this_device;
62 const char *nodename;
63 umode_t mode;
64 };
我们只要像字符设备一样实现fops接口再给一个minor即可,如果minor使用宏MISC_DYNAMIC_MINOR(其实就是255),内核会自动分配一个次设备号,其他的内核已经实现约定好的次设备号可以参考"include/linux/miscdevice.h"。万事具备之后只需使用下面的API注册/注销到内核
178 int misc_register(struct miscdevice * misc)
238 int misc_deregister(struct miscdevice *misc)
misc的分析
misc的使用是不是很简单?但麻雀虽小五脏俱全,正是因为misc精简的结构,我们可以很容易的抓到其中体现的分层思想,misc的设计方法体现在很多使用cdev作为接口的子系统,而其中的清晰的分层思想更是Linux驱动的两大支柱之一(另外一个是分离)。我们可以借鉴其中的设计思路,提升我们的驱动程序的质量。下面,我们简单的分析一下misc的内部机制。
misc_init
作为Linux的一个子系统,misc子系统在Linux启动过程中就会完成准备工作,主要包括初始化数据结构,创建相应的class,创建、初始化并注册cdev对象到内核等。有了这些基础,我们就可以使用misc的众多好处进行编程。
//drivers/char/misc.c
56 static const struct file_operations misc_fops = {
157 .owner = THIS_MODULE,
158 .open = misc_open,
159 .llseek = noop_llseek,
160 };
268 static int __init misc_init(void)
269 {
272 #ifdef CONFIG_PROC_FS
273 proc_create("misc", 0, NULL, &misc_proc_fops);
274 #endif
275 misc_class = class_create(THIS_MODULE, "misc");
281 if (register_chrdev(MISC_MAJOR,"misc",&misc_fops))
282 goto fail_printk;
283 misc_class->devnode = misc_devnode;
284 return 0;
292 }
293 subsys_initcall(misc_init);
misc_init()
--293-->系统启动的过程中就会初始化misc子系统
--273-->根据系统配置,可能需要提供/proc接口
--275-->在/sysfs中创建一个类,名为misc
--281-->使用静态主设备号(10)、封装好的方法集misc_fops,register_chrdev()内部会创建一个cdev对象并使用这两个参数将其初始化并注册到内核,这个cdev对象将负责所有的混杂设备的设备号。关于cdev对象和设备号之间的关系参见cdev_map。
--158-->misc的cdev对象使用的fops,显然,至此和普通字符设备的调用过程一样,chrdev_open()->misc_open()。
misc_register
接下来,老规矩,我们从"XXX_register"开始分析,在Linux内核中,这些"XXX_register"往往就是一个设备对象注册到内核的接口,是研究当相应对象注册进去之后内核动作的最佳入口。
178 int misc_register(struct miscdevice * misc)
179 {
180 dev_t dev;
187 if (misc->minor == MISC_DYNAMIC_MINOR) {
188 int i = find_first_zero_bit(misc_minors, DYNAMIC_MINORS);
193 misc->minor = DYNAMIC_MINORS - i - 1;
194 set_bit(i, misc_minors);
195 }
206 dev = MKDEV(MISC_MAJOR, misc->minor);
208 misc->this_device = device_create(misc_class, misc->parent, dev,
209 misc, "%s", misc->name);
210 if (IS_ERR(misc->this_device)) {
211 int i = DYNAMIC_MINORS - misc->minor - 1;
212 if (i < DYNAMIC_MINORS && i >= 0)
213 clear_bit(i, misc_minors);
214 err = PTR_ERR(misc->this_device);
216 }
222 list_add(&misc->list, &misc_list);
226 }
misc_register()
--187--> 如果指定的minor是动态分配,那么进入相关语句块。
--188--> 使dev用位图遍历API-find_first_zero_bit找到最小未用的设备号。
--193--> 得到分配好的次设备号。
--208--> (根据设备号)创建设备文件,使用的是misc_init中创建的misc_class,至此就可以实现misc设备文件的自动创建。就相当与我们在纯粹的cdev驱动中使用class_create()+device_create()创建设备文件。一个设备文件和一个设备号相联系,而misc的所有的设备号都和misc_init创建的cdev对象相联系,所以打开的任何一个misc设备文件首先回调的就是(chrdev_open()->)misc_open()。
--222--> 关键,将这个新分配的misc加入到misc链表中,用于管理所有的misc设备,便于misc_open()提取具体设备的fops。
misc_open
构建的misc子系统,将设备添加到了该子系统中,接下来我们来看一下应用层程序是如何打开一个misc设备的。由于misc也是一种字符设备,所以其提供的接口也是位于/dev中。但是正如misc的定义,其中的设备五花八门却共用同一个主设备号,这就意味着最终被chrdev_open回调的misc_open一定要具备根据被打开的不同文件为file结构准备不同的操作方法这一能力,即在驱动中实现对子设备的识别,或者称之为"多态"。
112 static int misc_open(struct inode * inode, struct file * file)
113 {
114 int minor = iminor(inode);
115 struct miscdevice *c;
116 int err = -ENODEV;
117 const struct file_operations *new_fops = NULL;
121 list_for_each_entry(c, &misc_list, list) {
122 if (c->minor == minor) {
123 new_fops = fops_get(c->fops);
124 break;
125 }
126 }
144 replace_fops(file, new_fops);
145 if (file->f_op->open) {
146 file->private_data = c;
147 err = file->f_op->open(inode,file);
148 }
152 }
misc_open()
--121-->遍历misc设备链表,根据被打开的设备的次设备号找到设备对象。
--123-->存储这个设备对象的操作方法集unique_fops。
--144-->将misc设备具体的操作方法集unique_fops替换到filp中的f_op中,这个位置原来是misc的cdev对象的fops,filp带着这个unique_fops从open()返回,就实现了不同的设备对应不同的操作方法,即面向对象的"多态"
3+2+1多设备识别驱动模型
通过上述对misc机制的分析,我们不难总结出一个支持设备识别的3+2+1驱动模型(3个函数+2个数据结构+1个封装):
- 初始化整个驱动组的xxx_init(),通常用模块加载函数或总线的probe函数实现;
- 用于注册一个子驱动的xxx_register(),需要EXPORT到符号表;
- 能够根据传入的inode识别具体的设备并将其操作方法集放到filp中的xxx_open()。
+
- 用于存储每一个驱动对象的通用链表或数组+priv_data
- 用于存储子设备号的位图。
+
- 将所有的不同的设备用一个统一的结构进行封装
至此,我们就可以写一写这个3+2+1驱动模型的模板。
1个封装
struct multidevice{
struct list_head head;
int minor;
struct file_operations* fops;
void *priv; //私有数据,供read/write等接口识别的信息,以及其他数据都放这里
};
2个数据结构
struct list_head multi_dev_list;
unsigned int minors_map; //根据设备号数目的不同选数据类型or数组
3个函数
int major,baseminor = 0,max_dev = sizeof(minors_map)*8;
#define DEV_NAME "multi_device"
struct class *cls;
xxx_open(struct inode *inode,struct file *file){
int minor = iminor(inode);
struct multidevice *dp;
const struct file_operations *new_fops = NULL;
list_for_each_entry(dp, &multi_dev_list, head) {
if (dp->minor == minor) {
new_fops = fops_get(dp->fops);
break;
}
}
replace_fops(file, new_fops);
if (file->f_op->open) {
file->private_data = dp
file->f_op->open(inode,file);
}
}
xxx_init(void){
dev_t devno,
INIT_LIST_HEAD(&multi_dev_list);
init_map(&minors_map);
struct cdev *multi_cdev = cdev_alloc();
cdev_init(multi_cdev, multi_fops);
alloc_chrdev_region(&devno, baseminor, count,DEV_NAME);
major = MAJOR(devno);
cdev_add(multi_cdev , devno, count);
cls = class_create(THIS_MODULE, DEV_NAME);
}
/*---------------下面是给待加驱动用的----------------------*/
xxx_register(struct *multidevice dev){
dev_t dev;
if (dev->minor == MISC_DYNAMIC_MINOR) {
int i = find_first_zero_bit(minors_map, DYNAMIC_MINORS);
dev->minor = DYNAMIC_MINORS - i - 1;
set_bit(i, minors_map);
}
dev_t pri_devno = MKDEV(major, dev->minor);
device_create(multi_class, NULL, pri_devno, "%s", misc->name);
list_add(dev->head, &multi_dev_list);
}
EXPORT_SYMBOL(xxx_register)
misc子系统的更多相关文章
- 跟着内核学框架-从misc子系统到3+2+1设备识别驱动框架
misc子系统在Linux中是一个非常简单的子系统,但是其清晰的框架结构非常适合用来研究设备识别模型.本文从misc子系统的使用出发,通过了解其机制来总结一套的设备识别的驱动框架,即使用使用同一个驱动 ...
- misc设备
WatchDog Timer驱动 混杂设备 Misc(或miscellaneous)驱动是一些拥有着共同特性的简单字符设备驱动.内核抽象出这些特性而形成一些API(在文件drivers/char/mi ...
- MISC混杂设备 struct miscdevice /misc_register()/misc_deregister()【转】
本文转自:http://blog.csdn.net/angle_birds/article/details/8330407 在Linux系统中,存在一类字符设备,他们共享一个主设备号(10),但此设备 ...
- input输入子系统
一.什么是input输入子系统? 1.Linux系统支持的输入设备繁多,例如键盘.鼠标.触摸屏.手柄或者是一些输入设备像体感输入等等,Linux系统是如何管理如此之多的不同类型.不同原理.不同的输入信 ...
- 基于input子系统的sensor驱动调试(一)
要想弄明白世界的本质,就要追根溯源:代码也是一样的道理: 最近调试几个sensor驱动,alps sensor驱动.compass sensor驱动.G-sensor驱动都是一样的架构: 一.基于in ...
- arm Linux 驱动LED子系统 测试
Linux内核在3.0以上引入了设备树概念(具体哪个版本不清楚)在编译内核后需要将与之对应的dtb文件也下载人板子上才能使内核与硬件关联起来. dtb文件是有dts文件编译后生成的:例如 /* * C ...
- 《深入理解Java虚拟机》-----第9章 类加载及执行子系统的案例与实战
概述 在Class文件格式与执行引擎这部分中,用户的程序能直接影响的内容并不太多, Class文件以何种格式存储,类型何时加载.如何连接,以及虚拟机如何执行字节码指令等都是由虚拟机直接控制的行为,用户 ...
- INPUT输入子系统【转】
转自:https://www.cnblogs.com/deng-tao/p/6094049.html 1.Linux系统支持的输入设备繁多,例如键盘.鼠标.触摸屏.手柄或者是一些输入设备像体感输入等等 ...
- JVM系列之三:类装载器子系统
0. JVM架构图 Java虚拟机主要分为五大模块:类装载器子系统.运行时数据区.执行引擎.本地方法接口和垃圾收集模块. 1. 类的加载 虚拟机类装载器子系统:虚拟机把描述类的数据从class文件加载 ...
随机推荐
- Spring view controller
https://www.zifangsky.cn/648.html https://www.zifangsky.cn/665.html https://www.zifangsky.cn/671.htm ...
- 【例题5-4 UVA - 156】Ananagrams
[链接] 我是链接,点我呀:) [题意] 在这里输入题意 [题解] 每个字符串如果每个字符按照升序排一下.假设他们能够互相变化. 则肯定是一样的. 根据这个东西,用一个map来判重就好. [错的次数] ...
- 对照jQuery和AngularJS的不同思维模
对照jQuery和AngularJS的不同思维模 Question 如果我已经熟悉了怎样使用jQuery来开发client应用.我如今打算使用AngularJS.请描写叙述一下有那些思维模式方面的东西 ...
- kafka集群操作指南
目录 kafka集群操作指南 (一)单机版安装 (二)集群安装 (三)集群启停操作 (四)topic相关的操作 (五)某个broker挂掉,本机器可重启 (六)某个broker挂掉且无法重启,需要其它 ...
- 利用java反射将结果集封装成为对象和对象集合
java反射机制是什么 反射机制是在运行状态中,可以知道任何一个类的属性和方法,并且调用类的属性和方法: 反射机制能够做什么 1.判断运行对象的所属类 2.构造任意一个类的对象 3.获取任意一个类的属 ...
- LeetCode——Set Matrix Zeroes
Given a m x n matrix, if an element is 0, set its entire row and column to 0. Do it in place. 原题链接:h ...
- C++基础学习教程(七)----类编写及类的两个特性解析--->多态&继承
类引入 到眼下为止我们所写的自己定义类型都是keywordstruct,从如今起我们将採用class方式定义类,这样的方式对于学习过其它高级语言包含脚本(Such as Python)的人来说再熟悉只 ...
- 【hdu5527】【2015ACM/ICPC亚洲区长春站 】Too Rich
题目链接: pid=5527">http://acm.hdu.edu.cn/showproblem.php?pid=5527 题意&题解: 感觉自己真是弱啊,自己想的贪心是错的 ...
- Codeforces 138C(区间更新+离散化)
题意:有n棵树在水平线上,给出每棵树的坐标和高度,然后向左倒的概率和向右倒的概率,和为1,然后给出了m个蘑菇的位置,每一个蘑菇都有一个魔法值,假设蘑菇被压死了,也就是在某棵树[a[i] - h[i], ...
- php对浮点数小数取整,php除法取整数
如果我们使用" / "操作符进行除法运算时,如果遇到无法除尽的情况,会得到小数值.如果我只希望得到整数部分,怎么办呢? 1.round — 对浮点数进行四舍五入 float rou ...