




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
字符设备驱动第5章字符设备驱动计算机科学技术系李伟民2012年8月字符设备驱动提纲Linux字符设备驱动结构
-Linux设备号管理
-一些重要的数据结构globalmem设备驱动
-globalmem驱动编写方法和分析-globalmem设备驱动在用户空间的验证
字符设备驱动引言Linux提供3种基本的设备驱动接口,包括字符,块设备和网络设备驱动,其中字符设备是应用最广,和用户应用程序联系最直接的驱动。比起块设备或者网络驱动程序更加易于理解。字符设备驱动字符设备开发的基本步骤字符设备的特点像字节流一样来存取的设备(如同文件)通过/dev下的文件系统结点来访问通常至少需要实现open,close,read,和write等系统调用只能顺序访问的数据通道,不能前后移动访问指针。特例:比如framebuffer设备就是这样的设备,应用程序可以使用mmap或lseek访问图像的各个区域。字符设备驱动字符设备开发的基本步骤确定主设备号和次设备号实现字符驱动程序实现file_operations结构体实现初始化函数,注册字符设备实现销毁函数,释放字符设备创建设备文件节点字符设备驱动主设备号与次设备号主设备号与次设备号主设备号:主设备号是内核识别一个设备的标识。整数(占12bits),范围从0到4095,通常使用1到255。可以通过/proc/devices文件来查看驱动系统设备的主设备号。次设备号:次设备号由内核使用,用于正确确定设备文件所指的设备,驱动程序遍历设备时,每发现一个它能驱动的设备,就创建一个设备对象,并为其分配一个次设备号以区分不同的设备。这样当应用程序访问设备节点时驱动程序就可以根据次设备号知道它所访问的设备了。整数(占20bits),范围从0到1048575,一般使用0到255。字符设备驱动主设备号与次设备号ls-l/devcrw-r1rootroot1,1Jan100:00memcrw-r1rootroot1,2Jan100:00kmemcrw-rw-rw-1rootroot1,3Jan100:00nullcrw-r1rootroot1,4Jan100:00portcrw-rw-rw-1rootroot1,5Jan100:00zerocrw-rw-rw-1rootroot1,7Jan100:00fullcrw-r--r--1rootroot1,8Jan100:00randomcrw-r--r--1rootroot1,9Jan100:00urandomcrw-rw-rw-1rootroot5,0Jan100:00ttycrw1rootroot5,1Jan100:00consolecrw-rw-rw-1rootroot5,2Jan100:00ptmxdrwxr-xr-x1rootroot0Jan100:00ptydrwxr-xr-x2rootroot0Jan100:00ptsdrwxr-xr-x1rootroot0Jan100:00rddrwxr-xr-x1rootroot0Jan100:00mtddrwxr-xr-x1rootroot0Jan100:00mtdblockcrw1rootroot4,64Jan100:15ttyS0crw1rootroot4,65Jan100:00ttyS1crw1rootroot4,66Jan100:00ttyS2crw1rootroot4,67Jan100:00ttyS3crw1rootroot4,68Jan100:00ttyS4drwxr-xr-x1rootroot0Jan100:00miscc:字符设备b:块设备主设备号次设备号字符设备驱动设备编号的内部表达dev_t类型(32位):用来保存设备编号(包括主设备号(12位)和次设备号(20位))。从dev_t获得主设备号和次设备号
:MAJOR(dev_t);
MINOR(dev_t)。将主设备号和次设备号转换成dev_t类型:MKDEV(intmajor,intminor)字符设备驱动主设备号与次设备号分配主设备号手工分配:找一个内核没有使用过的主设备号来使用动态分配:我们通常不知道设备将要使用哪些主设备号#include<linux/fs.h>intregister_chrdev_region(dev_tfirst,unsignedintcount,char*name);要分配的设备编号范围的起始值,次设备号经常为0所请求的连续设备编号的个数和该编号范围关联的设备名称,它将出现在/proc/devices和sysfs中#include<linux/fs.h>intalloc_chrdev_resion(dev_t*dev,unsignedintfirstminor,
unsignedintcount,char*name);输出的设备号要使用的被请求的第一个次设备号字符设备驱动主设备号与次设备号释放设备号voidunregister_chrdev_region(dev_tfirst,unsignedintcount);通常在模块的清除函数中调用。字符设备驱动Linux字符设备驱动结构cdev结构体Linux2.6内核中,使用cdev结构体描述一个字符设备structcdev{ structkobjectkobj;/*内嵌的kobject对象*/ structmodule*owner;/*所属模块*/ structfile_operations*ops;/*文件操作结构体*/ structlist_headlist; dev_tdev;/*设备号*/ unsignedintcount;};字符设备驱动Linux字符设备驱动结构操作cdev的函数voidcdev_init(structcdev*,structfile_operations*);structcdev*cdev_alloc(void);intcdev_add(structcdev*,dev_t,unsigned);voidcdev_del(structcdev*);用于初始化cdev的成员,并建立cdev和file_operations之间的连接函数用于动态申请一个cdev内存分别向系统删除一个cdev,完成字符设备的注销,通常在模块的卸载函数中调用分别向系统添加一个cdev,完成字符设备的注册,通常在模块加载函数中调用字符设备驱动Linux字符设备驱动结构file_operations结构体字符驱动和内核的接口,在include/linux/fs.h中定义,它定义了维系在设备驱动上的操作函数。在进程PCB中,每个打开文件,包括设备文件(内部用一个structfile结构来代表,稍后我们会看到)与它自身的函数集合相关连,它通过包含一个称为f_op的指向一个file_operations结构成员指针建立关系字符驱动只要实现一个file_operations结构体,并注册到内核中,内核就有了操作此设备的能力用户通过系统调用open,read,write等在这个结构中有相应的方法对应字符设备驱动Linux字符设备驱动结构file_operations结构体的主要成员structmodule*owner:它是一个指向拥有这个结构的模块的指针,它被简单初始化为THIS_MODULE,一个在<linux/module.h>中定义的宏。loff_t(*llseek)(structfile*,loff_t,int):
llseek方法用作改变文件中的当前读/写位置,并且新位置作为(正的)返回值。ssize_t(*read)(structfile*,char__user*,size_t,loff_t*):用来从设备中获取数据。ssize_t(*aio_read)(structkiocb*,char__user*,size_t,loff_t):初始化一个异步读--可能在函数返回前不结束的读操作.字符设备驱动Linux字符设备驱动结构file_operations结构体的主要成员ssize_t(*write)(structfile*,constchar__user*,size_t,loff_t*):发送数据给设备.如果NULL,-EINVAL,如果非负,返回值代表成功写的字节数。ssize_t(*aio_write)(structkiocb*,constchar__user*,size_t,loff_t*):初始化设备上的一个异步写。
int(*readdir)(structfile*,void*,filldir_t):对于设备文件这个成员应当为NULL;它用来读取目录,并且仅对文件系统有用unsignedint(*poll)(structfile*,structpoll_table_struct*):poll方法是3个系统调用的后端:poll,epoll,和select,都用作查询对一个或多个文件描述符的读或写是否会阻塞字符设备驱动Linux字符设备驱动结构file_operations结构体的主要成员
int(*ioctl)(structinode*,structfile*,unsignedint,unsignedlong):ioctl系统调用提供了发出设备特定命令的方法(例如格式化软盘的一个磁道,这不是读也不是写)。int(*mmap)(structfile*,structvm_area_struct*):mmap用来请求将设备内存映射到进程的地址空间.如果这个方法是NULL,mmap系统调用返回–ENODEV。int(*open)(structinode*,structfile*):尽管这常常是对设备文件进行的第一个操作,不要求驱动声明一个对应的方法.如果这个项是NULL,设备打开一直成功,但是你的驱动不会得到通知.int(*flush)(structfile*):flush操作在进程关闭它的设备文件描述符的拷贝时调用,它应当执行(并且等待)设备的任何未完成的操作.如果flush为NULL,内核简单地忽略用户应用程序的请求.字符设备驱动Linux字符设备驱动结构file_operations结构体的主要成员int(*release)(structinode*,structfile*):在文件结构被释放时用该操作。int(*fsync)(structfile*,structdentry*,int):这个方法是fsync系统调用的后端,用户调用来刷新任何挂着的数据。int(*fasync)(int,structfile*,int):这个操作用来通知设备它的FASYNC标志的改变int(*lock)(structfile*,int,structfile_lock*):lock方法用来实现文件加锁ssize_t(*sendfile)(structfile*,loff_t*,size_tread_actor_t,void*):sendpage是sendfile的另一半;它由内核调用来发送数据,一次一页,到对应的文件.设备驱动上不实现sendpage字符设备驱动Linux字符设备驱动结构file_operations结构体的主要成员int(*release)(structinode*,structfile*):在文件结构被释放时用该操作。int(*fsync)(structfile*,structdentry*,int):这个方法是fsync系统调用的后端,用户调用来刷新任何挂着的数据。int(*fasync)(int,structfile*,int):这个操作用来通知设备它的FASYNC标志的改变int(*lock)(structfile*,int,structfile_lock*):lock方法用来实现文件加锁ssize_t(*sendfile)(structfile*,loff_t*,size_tread_actor_t,void*):sendpage是sendfile的另一半;它由内核调用来发送数据,一次一页,到对应的文件.设备驱动上不实现sendpage字符设备驱动Linux字符设备驱动结构file结构体file结构与用户空间程序中的FILE没有任何关联,FILE在C库中定义且不会出现在内核代码中,structfile是一个内核结构,不会出现在用户程序。file结构代表一个打开的文件(不限定于设备驱动程序)。它由内核在open时创建,并传递给在该文件上进行操作的所有函数,直到close函数。在文件的所有实例都被关闭之后,内核会释放。指向structfile的指针通常被称为filp。字符设备驱动Linux字符设备驱动结构file结构体的主要成员mode_tf_mode:文件模式,它通过FMODE_READ和FMODE_WRITE来标识文件是否可读或可写(或可读写)。loff_tf_pos:初始化当前读写位置。驱动可以读这个值,如果它需要知道文件中的当前位置,但是正常地不应该改变它;读和写会使用它们接收到的最后那个指针参数来更新一个位置,代替直接作用于filp->f_pos。
unsignedintf_flags:这些是文件标志,例如O_RDONLY,O_NONBLOCK,和O_SYNC。驱动应当检查O_NONBLOCK标志来看是否是请求非阻塞操作;其他标志很少使用。字符设备驱动Linux字符设备驱动结构file结构体的主要成员structfile_operations*f_op:和文件关联的操作.内核安排指针作为它的open实现的一部分,接着读取它当它需要分派任何的操作时.filp->f_op中的值从不由内核保存为后面的引用;这意味着你可改变你的文件关联的文件操作,在你返回调用者之后新方法会起作用。void*private_data:open系统调用设置这个指针为NULL,在为驱动调用open方法之前.你可自由使用这个成员或者忽略它;你可以使用这个成员来指向分配的数据,但是接着你必须记住在内核销毁文件结构之前,在release方法中释放那个内存。
structdentry*f_dentry:关联到文件的目录入口(dentry)结构。
字符设备驱动Linux字符设备驱动结构inode结构体inode结构由内核在内部用来表示文件.因此,它和代表打开文件描述符的文件结构是不同的.可能有代表单个文件的多个打开描述符的许多文件结构,但是它们都指向一个单个inode结构.inode结构包含大量关于文件的信息.作为一个通用的规则,这个结构只有2个成员对于编写驱动代码有用:dev_ti_rdev:对表示设备文件的inode结构,这个成员包含实际的设备编号structcdev*i_cdev:代表字符设备;当节点指的是一个字符设备文件时,这个成员包含一个指针,指向这个结构。从一个inode中获取主次编号:unsignedintiminor(structinode*inode);unsignedintimajor(structinode*inode);字符设备驱动Linux字符设备驱动结构注册设备,在模块或驱动初始化时调用Linux2.4及之前:Linux2.6:intregister_chrdev(unsignedintmajor,constchar*name, structfile_operations*fops)voidcdev_init(structcdev*,structfile_operations*);intcdev_add(structcdev*,dev_t,unsigned);在运行时获取一个独立的cdev结构该设备关联的设备编号的数量驱动程序没有完全准备好处理设备上的操作,不能调用cdev_add字符设备驱动Linux字符设备驱动结构注销设备,在模块或驱动卸载时调用Linux2.4及之前:Linux2.6:intunregister_chrdev(unsignedintmajor, constchar*name);voidcdev_del(structcdev*);字符设备驱动Linux字符设备驱动结构//设备驱动模块加载函数staticint__initxxx_init(void){ ... cdev_init(&xxx_dev.cdev,&xxx_fops);//初始化cdev xxx_dev.cdev.owner=THIS_MODULE; //获取字符设备号 if(xxx_major) { register_chrdev_region(xxx_dev_no,1,DEV_NAME); } else { alloc_chrdev_region(&xxx_dev_no,0,1,DEV_NAME); } ret=cdev_add(&xxx_dev.cdev,xxx_dev_no,1);//注册设备 ...}字符设备驱动Linux字符设备驱动结构/*设备驱动模块卸载函数*/staticvoid__exitxxx_exit(void){ unregister_chrdev_region(xxx_dev_no,1);//释放占用的设备号
cdev_del(&xxx_dev.cdev);//注销设备 ...}字符设备驱动Linux字符设备驱动结构设备的打开open应当进行下面的工作:1)检查设备特定的错误(例如设备没准备好,或者类似的硬件错误
2)如果它第一次打开,初始化设备
3)如果需要,更新f_op指针,模块计数加1 4)分配并填充要放进filp->private_data的任何数据结构。
5)识别次设备号
6)如果有中断操作,申请中断处理程序。intopen(structinode*inode,structfile*filp);字符设备驱动Linux字符设备驱动结构设备的关闭release方法在应用程序调用close()系统调用时被调用,它的角色是open的反面. 1)释放open分配在filp->private_data中的任何东西
2)在最后的close关闭设备,模块计数减1
3)如果申请了中断,则释放中断处理程序.intrelease(structinode*inode,structfile*filp);字符设备驱动Linux字符设备驱动结构read和writessize_tread(structfile*filp,char__user*buff,size_tcount,loff_t*offp);ssize_twrite(structfile*filp,constchar__user*buff,size_tcount,loff_t*offp);指向用户空间的缓冲区,这个缓冲区或者保存将写入的数据,或者是一个存放新读入数据的空缓冲区。用户在文件中存取操作的位置buff参数是用户空间指针,内核代码不能直接引用其中的内容。字符设备驱动Linux字符设备驱动结构用户空间与内核空间的数据拷贝不能简单的用指针操作或者memcpy来进行数据拷贝,要用内核函数如果要复制的内存是简单类型,如char、int、long等,用put_user()和get_user()unsignedlongcopy_from_user(void*to,constvoid__user*from,unsignedlongcount);unsignedlongcopy_to_user(void__user*to,constvoid*from,unsignedlongcount);字符设备驱动Linux字符设备驱动结构读设备模板ssize_t
xxx_read(structfile*filp,char__user*buf,
size_tcount,loff_t*f_pos){ ...
copy_to_user(buf,...,...); ...}写设备模板
ssize_t
xxx_write(structfile*filp,constchar__user*buf,
size_tcount,loff_t*f_pos){ ...
copy_from_user(...,buf,...); ...}字符设备驱动Linux字符设备驱动结构ioctl函数ioctl函数主要完成以下两个功能:
1)为设备驱动程序执行“命令”提供了一个特有的入口点
2)用来设置或者读取设备的属性信息
intioctl(structinode*inode,structfile*filp, unsignedintcmd,unsignedlongarg);事先定义的IO控制命令代码arg为对应于cmd命令的参数字符设备驱动Linux字符设备驱动结构cmd参数的定义不推荐用0x1,0x2,0x3之类的值,会导致不同的设备驱动拥有相同的命令号。Linux对ioctl()的cmd参数有特殊的定义
设备类型(type)
序列号(number)方向(direction)
数据尺寸(size)
8bit
8bit
2bit13/14bit字符设备驱动Linux字符设备驱动结构cmd参数的定义内核还定义了以下四个宏来辅助生成命令:IO(type,nr)用于构造无参数的命令编号;_IOR(type,nr,datatype)用于构造从驱动程序中读取数据的命令编号
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 二零二五年度融资租赁合同印花税税率变动及税务处理
- 二零二五学校教师职工学生安全管理责任合同
- 2025年度绿色交通基础设施建设合作协议书
- 教育行业招生销售流程优化方案
- 2025年MCL(BCL)系列离心压缩机合作协议书
- 合资合作协议及利润分配条款
- 家政服务行为约束协议和免责声明
- 诊断血清生物制品项目风险识别与评估综合报告
- 2025-2030年中国PP反应釜行业深度研究分析报告
- 木工工程承包协议书
- 初中八年级语文课件-桃花源记 全国公开课一等奖
- 《无人机操控技术》教案全套 1.1 无人机概述 -6.2 自动机场操控
- ISO27001标准培训课件
- 《审核员培训教程》课件
- 小学劳动教育四下第四单元第2课《水培生菜》课件
- 《光催化技术》课件
- 办公打印机的租赁合同范文
- 危大工程监理巡视检查用表
- 大埔县生活垃圾填埋场应急加固及渗滤液处理站扩容改造工程环境影响报告
- 餐饮行业仪容仪表标准规范
- 110kVGIS组合电器(含PT)试验作业指导书
评论
0/150
提交评论