13 字符设备驱动程序框架
合集下载
相关主题
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
return ret;
return fd;
open系统调用分析
而do_filp_open()函数会调用nameidata_to_filp()函 数,nameidata_to_filp()调用__dentry_open();
__dentry_open()函数
//获取文件的inode信息 inode = dentry->d_inode;
每一个文件对应 一个inode结构
inode .i_rdev .i_fop 磁盘上的文件 /dev/xxx
底层操作函 数,操作硬件
字符设备驱动框架
每一个设备都有自己的底层操作函数;也就是说每 一个设备都对应着自己的file_operations结构体; 那么打开的文件file->f_op指针应该指向那一个 file_operations结构体呢?或者说 file->f_op怎样找 到对应的file_operations呢?
操作硬件
内核里面,通过一个宏来建立实地址到虚地址的映 射,这个宏是:
ioremap( paddr,size); 宏的第一个参数是要映射的实地址; 第二个参数是要映射的大小; 宏返回映射后的虚拟地址;
例如,GPF控制寄存的地址映射如下:
unsigned long viraddr; viraddr = ioremap(0x56000050 ,12 ); 通过 *(volatile unsigned long *)viraddr 就可以在 内核中访问GPF寄存器。
def_chr_fops
.open = chrdev_open
(2)而inode->i_fop指向 def_chr_fops,其成员open执行 函数chrdev_open()
硬件设备
open系统调用分析
chrdev_open函数分析
chrdev_open函数
从全局变量cdev_map中,以主设备号为索引找到cdev结构; kobj = kobj_lookup(cdev_map, inode->i_rdev, &idx); new = container_of(kobj, struct cdev, kobj);
//将inode->i_fop拷贝给F->f_op f->f_op = fops_get(inode->i_fop);
//调用Inode->i_fop->open() open = f->f_op->open; open(inode, f);
__dentry_open()函数最终会调 用inode->i_fop->open函数 我们知道,inode对应一个文件; 我们在创建一个设备文件时,就 是创建一个inode; 当我们在用户空间创建设备文件 时,对应的inode->i_fop指向 fs/char_dev.c中的def_chr_fops 。 def_chr_fops的成员open指向 chrdev_open()函数。
两个函数都会调用__register_chrdev_region()函数; 这个函数完成如下操作:
▶如果是自动分配设备号
在全局数组chrdevs[ ]中找到一个空位,这个空位的数组下标就 是主设备号;设置这个空位。
▶如果是直接注册设备号
以主设备号为下标,找到数组chrdevs[ ]中的成员,设置这个成 员;
跟打开的设备文件里面的信息有关
通过分析字符设备的代码来了解其核心过程
字符设备的源码在文件 fs/char_dev.c中,包括设备号 申请函数,cdev注册函数都在这个文件里面实现。
字符设备驱动框架
申请设备号的过程分析
设备号的注册或自动分配函数是下面两个
▶ alloc_chrdev_region() ▶ register映射的宏是:iounmap(viraddr);
设备文件的自动建立
设备文件的建立有手动和自动两种方法来创建;
手动方法就是使用mknod命令来建立设备文件; 而自动创建设备文件的方法经历了很多的变化; 早期的自动建立设备文件是由内核去建立的,也就是说 内核有个功能模块(devfs)专门负责创建设备文件, 这个访问增加了内核的负担。现在已经被丢弃了。 现在自动创建设备文件的方法是通过用户的一个应用程 序mdev(udev)来实现。
字符设备驱动程序框架
教学回顾
字符设备驱动程序编写的流程 生成设备文件的命令是什么?
教学内容
字符设备驱动框架 open系统调用分析 操作硬件 设备文件的自动建立
教学要求
理解字符设备的框架 掌握操作硬件的方法 掌握自动生成设备文件的方法
字符设备驱动框架
file、inode、file_operations之间的关系
open系统调用分析
open系统调用分析
open系统调用的实现在fs/open.c文件中
sys_open()函数 do_sys_open()函数
ret=do_sys_open() //获取空的文件描述符号 int fd=get_unused_fd_flags(); //调用do_filp_open()打开文件 设置file结构体 //关联fd与file结构 fd_install(fd, f);
return f;
open系统调用分析
open系统调用的层次如下
int fd = open(“/dev/xxx”,); sys_open() do_sys_open() (1)系统调用sys_open经过重 do_filp_open() 重函数的调用,最终会调用文件 nameidata_to_filp() 结点inode的i_fop->open方法 内核中 __dentry_open() inode->i_fop->open() inode .i_rdev .i_fop 磁盘上的文件 /dev/xxx
设备文件的自动建立
mdev的工作原理
mdev扫描/sys/class目录下的dev属性文件; 从该dev 属性文件中获取到设备编号; 并以包含该dev属性文件的目录名称作为设备名 ; 在 /dev目录下创建相应的设备文件; 例如,在开发中:
设备文件名为 event0,主设备号是:13,次设备号 是64 mdev程序就根据这个来创建设备文件。
.ops probe结构 .dev .dev .*data cdev结构 .ops .dev lock
字符设备驱动框架
内核使用一个数据结构cdev_map来记录系统中所 有的字符设备(cdev); 而设备号的维护是由全局chrdevs[]来完成的; 在驱动模块入口函数中:
申请设备号:就是在数组chrdevs[]中找一个空位; 注册字符设备:就是将cdev添加到cdev_map中;
def_chr_fops
inode .i_rdev .i_fop
.open = chrdev_open (2)chrdev_open函数以 inode的主设备号为索引, 通过cdev_map找到cdev结构 lock
磁盘上的文件 /dev/xxx
硬件设备
结论
从上面分析字符设备的框架我们可以得出以下结论 :
file 表示打开的文件 inode 表示保存在磁盘上的文件
▶所以硬盘格式化的时候,操作系统自动将硬盘分成两个区域。 一个是数据区,存放文件数据;另一个是inode区(inode table),存放inode所包含的信息。 ▶查看每个硬盘分区的inode总数和已经使用的数量。 df -i ▶查看每个inode节点的大小,可以用如下命令: sudo dumpe2fs -h /dev/hda | grep "Inode size"
全局数组chrdevs[]当前的成员数为255,下标从0-254 ,也就是说,当前内核的字符设备主设备号范围 0254.
字符设备驱动框架
注册cdev分析
cdev结构的注册分两步,第一步是初始化cdev结构;第 二步是注册。cdev_add()函数分析如下:
cdev_map cdev_add()函数完成的动作如下: (1)创建一个probe结构体变量,并 用cdev出初始化这个变量; 如图:probe->data指向cdev; probe->dev 设备号
字符设备驱动框架
字符设备注册过程如下图:
cdev_map probe指针 probe指针 chrdevs[]
(1)自动分配设备号,就是在数组 chrdevs[]中申请一个空位 *probes[255] probe结构 .dev .*data
内核中
lock
(2)初始化cdev,就是使cdev.ops 指向底层操作函数 cdev结构 file_operations .ops .open .dev .release .read (3)注册cdev,就是将 .write cdev关联到全局的映射 cdev_map中 xxx_open xxx_release 硬件设备
file_operations 表示硬件操作函数的集合
字符设备驱动框架
三者的关系如下图
int fd = open(“/dev/xxx”,); file .f_op
应用层用fd表示打开 的文件,fd与内核的 file结构关联
file中的f_op成员指 向底层操作函数集
file_operations .open .release .read .write xxx_open xxx_release 硬件设备
操作硬件
而对于linux系统来说,启动了MMU单元,因此 CPU要访问的地址有要经过MMU,而不是直接操 作内存; 启动MMU之后,CPU发出的地址是虚地址; 相对应的实地址,就是真真实实的,由布板时候决 定的地址。 因此,在驱动程序里面,想要访问控制寄存器,就 必须建立实地址到虚地址的映射;通过映射后的 虚地址去访问相应的控制寄存器;
更新设备结点inode的i_cdev成员; inode->i_cdev = p = new;
更系file结构的f_op成员; p = inode->i_cdev; filp->f_op = fops_get(p->ops);
调用底层的open函数; filp->f_op->open(inode,filp);
编写字符设备驱动程序的流程如下:
▶编写底层操作函数 ▶在全局数据结构cdev_map中找到一个空位;(注册设备号) ▶ 将底层操作函数的集合填到cdev_map结构中;(注册cdev)
操作硬件
对于嵌入式的CPU来说,CPU里面集成了各种设 备的控制器,要对外设进行操作,主要是通过对于 控制器的寄存器来实现。 而对于ARM来说,控制器的寄存器跟内存是同一 编址的,访问控制器的寄存器就好像访问内存一样 。 例如:FS2410开发板上的LED灯对于GPF IO口寄 存器的地址如下:
open系统调用总体过程
open系统调用的总体过程如下图:
int fd = open(“/dev/xxx”,); sys_open()
(1)系统调用最终 会调用inode的 i_fop->open file cdev_map probe指针 probe指针
.f_op
内核中
(3)将cdev结构中的ops 复给file的f_op; probe结构 .dev cdev结构 .*data .ops file_operations .dev .open .release .read .write xxx_open xxx_release
设备文件的自动建立
从mdev的工作原理可以得出结论:
驱动程序只需要在/sys/class目录下创建相应的目录和 文件,就可以让mdev帮我们生成设备文件。 相关的函数如下: class_create(owner, name);
*probes[255] probe指针 probe指针 probe结构 .dev .*data cdev结构
.ops probe结构 .dev .dev .*data cdev结构
(2)在全局的字符设备映射 cdev_map的数组probes[]中,以主设 备号为下标,将probe结构体的地址 填进去。