[精选]Linux字符设备驱动程序培训教材.pptx
LinuxLinuxLinuxLinux字符设备驱动程序字符设备驱动程序字符设备驱动程序字符设备驱动程序Linux驱动程序的分类驱动程序的分类p字符设备驱动:用于驱动能够像字节流文件一样被访问的设备。应用程序通常可以利用open、close、read、write等系统调用访问字符设备驱动。p块设备驱动:块设备和字符设备只在系统内核内部的管理上有所区别。应用程序对于字符设备的每一个I/O操作都会被内核直接传递给对应的驱动程序;而应用程序对于块设备的操作要经过虚拟文件系统VFS和缓冲区管理系统间接地传递给驱动程序处理。p网络设备驱动:应用程序必须利用套接字socket接口访问网络设备。网络设备驱动程序网络设备驱动程序字符设备驱动程序基本结构字符设备驱动程序基本结构 字符设备开发的基本步骤确定主设备号和次设备号实现字符驱动程序实现file_operations结构体构造字符设备结构体cdev在模块加载函数中注册字符设备在模块卸载函数中注销字符设备创立设备文件节点设备文件与设备号设备文件与设备号p为了表达“一切都是文件的设计思想,linux将每个已安装的设备都表示为一个设备文件。p设备文件通常位于/dev子目录。p对于字符设备,应用程序可以利用open、close、read、write等系统调用访问其设备文件,这些I/O操作都被直接传递给该设备文件所对应的设备。p每个设备文件中都存储了该设备的“主设备号和“次设备号。p一般由同一个内核模块管理的多个设备占用同一个主设备号,具体设备用次设备号标识。p用mknodfilenamecmajorminor命令创立设备文件p用rmfilename命令删除设备文件。注意删除设备文件并不会影响驱动模块。应用程序如何访问设备应用程序如何访问设备fd1=open“/dev/ttyS1,O_RDWR;/阻塞fd2=open“/dev/ttyS1,O_RDWR|O_NONBLOCK;/非阻塞intreadintfd,constvoid*buf,size_tlength;intwriteintfd,constvoid*buf,size_tlength;intlseekintfd,offset_toffset,intwhence;intioctlintfd,intcmd,void*arg;intcloseintfd;设备号的内部表达设备号的内部表达设备编号的内部表达设备编号的内部表达dev_t类型32位:用来保存设备编号包括主设备号12位和次设备号20位从dev_t获得主设备号和次设备号:MAJORdev_t;MINORdev_t;将主设备号和次设备号转换成dev_t类型:MKDEVint major,int minor;主设备号与次设备号主设备号与次设备号分配主设备号分配主设备号手工分配主设备号:找一个内核没有使用的主设备号来使用。#include int register_chrdev_region dev_t first,unsigned int count,char*name;要分配的设备编要分配的设备编号范围的起始值,号范围的起始值,次设备号经常为次设备号经常为0所请求的连续设所请求的连续设备编号的个数备编号的个数和该编号范围关和该编号范围关联的设备名称联的设备名称主设备号与次设备号主设备号与次设备号动态分配主设备号:#include int alloc_chrdev_resiondev_t*dev,unsigned int firstminor,unsigned int count,char*name;输出的设备号要使用的被请求的第一个次设备号主设备号与次设备号主设备号与次设备号释放设备号void unregister_chrdev_regiondev_t first,unsigned int count;通常在模块的去通常在模块的去除函数中调用。除函数中调用。记录字符设备的结构体记录字符设备的结构体cdev实现字符驱动程序实现字符驱动程序cdev 结构体结构体structcdevstructkobjectkobj;/*内嵌的kobject对象*/structmodule*owner;/*所属模块*/structfile_operations*ops;/*文件操作结构体*/structlist_headlist;dev_tdev;/*设备号*/unsignedintcount;cdev的的kobj、list、count字段不用我们关系和维护内核代字段不用我们关系和维护内核代劳,我们只需将其劳,我们只需将其ops字段指向为我们自己的字段指向为我们自己的file operations结构。结构。对cdev结构体的操作操作操作cdev的函数的函数voidcdev_initstructcdev*,structfile_operations*;structcdev*cdev_allocvoid;intcdev_addstructcdev*,dev_t,unsigned;voidcdev_delstructcdev*;用于初始化cdev的成员,并建立cdev和file_operations之间的连接分别向系统删除一个cdev,完成字符设备的注销,通常在模块的卸载函数中调用分别向系统添加一个cdev,完成字符设备的注册,通常在模块加载函数中调用函数用于动态申请一个cdev内存file_operations 结构体结构体file_operations 结构体结构体字符驱动和内核的接口:在include/linux/fs.h定义字符驱动只要实现一个file_operations结构体并注册到内核中,内核就有了操作此设备的能力。struct file_operations struct module*owner;loff_t*llseek struct file*,loff_t,int;ssize_t*read struct file*,char _user*,size_t,loff_t*;ssize_t*write struct file*,const char _user*,size_t,loff_t*;unsigned int*poll struct file*,struct poll_table_struct*;int*ioctl struct inode*,struct file*,unsigned int,unsigned long;int*open struct inode*,struct file*;int*flush struct file*,fl_owner_t id;int*release struct inode*,struct file*;/;file_operations 结构体结构体file_operations的主要成员:的主要成员:struct module*owner:指向模块自身open:翻开设备release:关闭设备read:从设备上读数据write:向设备上写数据ioctl:I/O控制函数llseek:定位读写指针mmap:映射设备空间到进程的地址空间file operations的初始化的初始化struct file_operations my_fops=.owner=THIS_MODULE,.llseek=my_llseek,.read=my_read,.write=my_write,.ioctl=my_ioctl,.open=my_open,.release=my_release,;file结构体结构体pfile结构代表一个翻开的文件,它由内核在应用程序open时创立,并将该文件所对应的fileoperations记录在file结构中。p在应用程序调用close函数,内核会释放该数据结构。struct file struct file_operations*f_op;unsigned int f_flags;fmode_tf_mode;loff_tf_pos;structdentry*f_dentryvoid*private_data;file结构体结构体file 结构体结构体file结构:结构:file_operations结构相关的一个结构体。描述一个正在翻开的设备文件。成员:loff_t f_pos:当前读/写位置unsigned int f_flags标识文件翻开时,是否可读或可写O_RDONLYO_NONBLOCKO_SYNCstruct file_operations*f_op文件相关的操作,指向所实现的structfile_operationsvoid*private_data:私有数据指针。驱动程序可以将这个字段用于任何目的或者忽略这个字段。inode结构体结构体p内核用inode结构在内部表示文件,用于存储文件访问权限、属主、组、大小、生产时间等VFS关心的信息。p其字段中我们只关心i_rdev设备号,和i_cdev和该文件所对应的cdev结构p我们在创立设备文件时,内核会自动创立一个对应的inode结构体,并将其i_cdev字段指向对应的字符设备结构体cdev事先已经在内核中注册过。pInode与file的区别:file表示翻开的文件描述符,多个file结构,可以指向单个inode结构。struct inode dev_t i_rdev;struct cdev*i_cdev;/;Inode结构体结构体Inode结构中的两个主要字段:结构中的两个主要字段:dev_t i_rdev;对表示设备文件的inode结构,该字段包含了真正的设备编号。struct cdev*i_cdev;structcdev是表示字符设备的内核的内部结构。当inode指向一个字符设备文件时,该字段包含了指向structcdev结构的指针从一个从一个inode中获得主设备号和次设备号:中获得主设备号和次设备号:unsignedintiminorstructinode*inode;unsignedintimajorstructinode*inode;字符设备驱动程序基本结构字符设备驱动程序基本结构 注册设备注册设备,在模块或驱动初始化时调用Linux-2.4及之前Linux-2.6intregister_chrdevunsignedintmajor,constchar*name,structfile_operations*fops如何操作字符设如何操作字符设备的接口备的接口voidcdev_initstructcdev*,structfile_operations*;intcdev_addstructcdev*,dev_t,unsigned;字符设备驱动程序基本结构字符设备驱动程序基本结构 注销设备:在模块卸载时调用Linux-2.4及之前Linux-2.6intunregister_chrdevunsignedintmajor,constchar*name;voidcdev_del structcdev*;字符设备驱动程序基本结构字符设备驱动程序基本结构/设备驱动模块加载函数设备驱动模块加载函数static int _init xxx_initvoid .cdev_init&xxx_dev.cdev,&xxx_fops;/初始化初始化cdevxxx_dev.cdev.owner=THIS_MODULE;/获取字符设备号获取字符设备号if xxx_major register_chrdev_regionxxx_dev_no,1,DEV_NAME;elsealloc_chrdev_region&xxx_dev_no,0,1,DEV_NAME;ret=cdev_add&xxx_dev.cdev,xxx_dev_no,1 ;/注册设备注册设备.字符设备驱动程序基本结构字符设备驱动程序基本结构/*设备驱动模块卸载函数*/staticvoid_exitxxx_exitvoidunregister_chrdev_regionxxx_dev_no,1;/释放占用的设备号cdev_del&xxx_dev.cdev;/注销设备.字符设备驱动程序基本结构字符设备驱动程序基本结构 翻开翻开模块使用计数加1识别次设备号硬件操作:检查设备相关错误诸如设备未就绪或类似的硬件问题;如果设备是首次翻开,则对其初始化;如果有中断操作,申请中断处理程序;intopenstructinode*inode,structfile*filp;字符设备驱动程序基本结构字符设备驱动程序基本结构 关闭关闭模块使用计数减1释放由open分配的,保存在filpprivate_data里的所有内容。硬件操作:如果申请了中断,则释放中断处理程序。在最后一次关闭操作时关闭设备。intreleasestructinode*inode,structfile*filp;字符设备驱动程序基本结构字符设备驱动程序基本结构 read/writessize_treadstructfile*filp,char_user*buff,size_tcount,loff_t*offp;ssize_twritestructfile*filp,constchar_user*buff,size_tcount,loff_t*offp;指向用户空间的缓冲区,这个缓冲区或者保存将写入的数据,或者是一个存放新读入数据的空缓冲区。用户在文件中存取操作的位置字符设备驱动程序基本结构字符设备驱动程序基本结构 用户空间和内核空间之间的数据拷贝过程,不能简单的用指针操作或者memcpy来进行数据拷贝用户空间的数据是可以被换出的,会产生一个页面失效异常。用户空间的地址无法在内核空间中使用。用户空间和内核空间之间进行数据拷贝的函数:如果要复制的内存是简单类型,如char、int、long等,put_user和get_userunsignedlongcopy_from_uservoid*to,constvoid_user*from,unsignedlongcount;unsignedlongcopy_to_uservoid_user*to,constvoid*from,unsignedlongcount;字符设备驱动程序基本结构字符设备驱动程序基本结构 l读设备模板读设备模板ssize_t xxx_readstruct file*filp,char _user*buf,size_t count,loff_t*f_pos .copy_to_userbuf,.,.;.l写设备模板写设备模板 ssize_t xxx_writestruct file*fil p,const char _user*buf,size_t count,loff_t*f_pos .copy_from_user.,buf,.;.字符设备驱动程序基本结构字符设备驱动程序基本结构 ioctl函数函数为设备驱动程序执行“命令提供了一个特有的入口点用来设置或者读取设备的属性信息。int ioctl struct inode*inode,struct file*filp,unsigned int cmd,unsigned long arg;事先定义的IO控制命令代码argarg为对应于为对应于cmdcmd命令的参数命令的参数 字符设备驱动程序基本结构字符设备驱动程序基本结构 cmd 参数的定义不推荐用0 x1,0 x2,0 x3之类的值Linux对ioctl的cmd参数有特殊的定义构造命令编号的宏:构造命令编号的宏:_IO_IOtypetype,nrnr用于构造无参数的命令编号;_IOR_IORtypetype,nrnr,datatypedatatype用于构造从驱动程序中读取数据的命令编号;_IOW_IOWtypetype,nrnr,datatypedatatype用于写入数据的命令;_IOWR_IOWRtypetype,nrnr,datatypedatatype用于双向传输。type和number位字段通过参数传入,而size位字段通过对datatype参数取sizeof获得。设备类型type 序列号number方向direction数据尺寸size8bit8bit2bit13/14bit字符设备驱动程序基本结构字符设备驱动程序基本结构 lIoctl函数模板函数模板int xxx_ioctl struct inode*inode,struct f ile*filp,unsigned int cmd,unsigned long arg .switch cmd case XXX_CMD1:.break;case XXX_CMD2:.break;default:/*不能支持的命令不能支持的命令*/return-ENOTTY;return 0;阶段总结阶段总结阶段总结阶段总结添加驱动程序到内核添加驱动程序到内核配置内核配置内核编译内核编译内核添加驱动程序到内核中添加驱动程序到内核中添加驱动程序到内核添加驱动程序到内核配置内核配置内核配置命令包括配置命令包括:makeconfigmakemenuconfigmakexconfigmakegconfig可通过“上、“下、“左、“右键移动菜单,选择某项按“Y,取消选择按“N,如果选择某项编译为模块按“M,进入子菜单按“Enter,返回上一级菜单按“Esc 使用make config、make menuconfig等命令后,会生成一个.config配置文件是隐身文件,通过ls a才能看到 添加驱动程序到内核添加驱动程序到内核编译内核编译内核可用如下命令编译内核可用如下命令编译内核:makeARC=armCROSS_PILE=arm-linux-zImage源代码根目录的Makefile中将ARCH和CROSS_PILE直接指定为arm和arm-linux-,如:这样就没有必要每次编译的时候都指定体系结构和交叉编译器了,只须使用下面命令就可以了:makezImageARCH?=armCROSS_PILE?=arm-linux-添加驱动程序到内核添加驱动程序到内核添加驱动程序到内核添加驱动程序到内核Linux 2.6内核的配置系统由以下3个局部组成。MakefileMakefile:分布在Linux内核源代码中的Makefite定义Linux内核的编译规则配置文件配置文件KconfigKconfig:给用户提供配置选择的功能。配置工具:配置工具:包括配置命令解释器对配置脚本中使用的配置命令进行解释配置用户界面提供字符界面和图形界面。这些配置工具都是使用脚本语言编写的,如Tcl/TK、Perl等。在Linux内核中增加程序需要完成以下3项工作。将编写的源代码复制到Linux内核源代码的相应目录。在目录的Kconfig文件中增加新源代码对应工程的编译配置选项。在目录的Makefile文件中增加对新源代码的编译条目。添加驱动程序到内核添加驱动程序到内核实例:实例:在内核源代码drivers目录下为ARM体系结构新增testdrivertest driver的树形目录:的树形目录:步骤:步骤:1 1、拷贝、拷贝testtest到到driversdrivers路径下路径下2 2、为新增目录创立、为新增目录创立KconfigKconfig和和MakefileMakefile3 3、修改新增目录父目录的、修改新增目录父目录的KconfigKconfig和和MakefileMakefile,以便新增的,以便新增的KconfigKconfig和和MakefileMakefile能够被引用能够被引用4 4、在、在arch/arm/Kconfigarch/arm/Kconfig里增加里增加source“drivers/test/Kconfigsource“drivers/test/Kconfig添加驱动程序到内核添加驱动程序到内核步骤:步骤:1 1、拷贝、拷贝testtest到到driversdrivers路径下路径下 cp fr test linux_kernel_path/drivers2 2、为新增目录创立、为新增目录创立KconfigKconfig和和MakefileMakefile添加驱动程序到内核添加驱动程序到内核修改新增目录的父目录的修改新增目录的父目录的KconfigKconfig和和MakefileMakefile在在drivers/Kconigdrivers/Kconig中参加:中参加:sourcedrivers/test/Kconfig“在在drivers/Makefiledrivers/Makefile中参加:中参加:obj-$CONFIG_TEST+=test/在在arch/arm/Kconfigarch/arm/Kconfig里参加:里参加:source“drivers/test/Kconfig增加了增加了Kconfig和和Makefile文件之后的新的文件之后的新的test树型目录如下所示:树型目录如下所示:用户空间调用设备驱动程序用户空间调用设备驱动程序创立设备节点创立设备节点$mknod/dev/node_namecmajorminor例如代码例如代码intmainvoidintdev_fd;charread_buf10;dev_fd=open/dev/node_name,O_RDWR|O_NONBLOCK;ifdev_fd=-1printfCanntopenfile/dev/node_namen;exit1;readdev_fd,read_buf,5;ioctldev_fd,XXX_IOCTL_CMD,0;closedev_fd;return0;9、静夜四无邻,荒居旧业贫。11月-2311月-23Sunday,November26,202310、雨中黄叶树,灯下白头人。05:49:1005:49:1005:4911/26/20235:49:10AM11、以我独沈久,愧君相见频。11月-2305:49:1005:49Nov-2326-Nov-2312、故人江海别,几度隔山川。05:49:1005:49:1005:49Sunday,November26,202313、乍见翻疑梦,相悲各问年。11月-2311月-2305:49:1005:49:10November26,202314、他乡生白发,旧国见青山。26十一月20235:49:10上午05:49:1011月-2315、比不了得就不比,得不到的就不要。十一月235:49上午11月-2305:49November26,202316、行动出成果,工作出财富。2023/11/265:49:1005:49:1026November202317、做前,能够环视四周;做时,你只能或者最好沿着以脚为起点的射线向前。5:49:10上午5:49上午05:49:1011月-239、没有失败,只有暂时停止成功!。11月-2311月-23Sunday,November26,202310、很多事情努力了未必有结果,但是不努力却什么改变也没有。05:49:1005:49:1005:4911/26/20235:49:10AM11、成功就是日复一日那一点点小小努力的积累。11月-2305:49:1005:49Nov-2326-Nov-2312、世间成事,不求其绝对圆满,留一份缺乏,可得无限完美。05:49:1005:49:1005:49Sunday,November26,202313、不知香积寺,数里入云峰。11月-2311月-2305:49:1005:49:10November26,202314、意志坚强的人能把世界放在手中像泥块一样任意揉捏。26十一月20235:49:10上午05:49:1011月-2315、楚塞三湘接,荆门九派通。十一月235:49上午11月-2305:49November26,202316、少年十五二十时,步行夺得胡马骑。2023/11/265:49:1005:49:1026November202317、空山新雨后,天气晚来秋。5:49:10上午5:49上午05:49:1011月-239、杨柳散和风,青山澹吾虑。11月-2311月-23Sunday,November26,202310、阅读一切好书如同和过去最杰出的人谈话。05:49:1005:49:1005:4911/26/20235:49:10AM11、越是没有本领的就越加自命非凡。11月-2305:49:1005:49Nov-2326-Nov-2312、越是无能的人,越喜欢挑剔别人的错儿。05:49:1005:49:1005:49Sunday,November26,202313、知人者智,自知者明。胜人者有力,自胜者强。11月-2311月-2305:49:1005:49:10November26,202314、意志坚强的人能把世界放在手中像泥块一样任意揉捏。26十一月20235:49:10上午05:49:1011月-2315、最具挑战性的挑战莫过于提升自我。十一月235:49上午11月-2305:49November26,202316、业余生活要有意义,不要越轨。2023/11/265:49:1005:49:1026November202317、一个人即使已登上顶峰,也仍要自强不息。5:49:10上午5:49上午05:49:1011月-23MOMODA POWERPOINTLoremipsumdolorsitamet,consecteturadipiscingelit.Fusceidurnablandit,eleifendnullaac,fringillapurus.Nullaiaculistemporfelisutcursus.感感 谢谢 您您 的的 下下 载载 观观 看看专家告诉