数字集成电路设计与系统应用专业1字符设备驱动(1)数字集成电路设计与系统应用专业Linux驱动程序基础知识Linux字符设备驱动基础file_opration结构分析用户空间与内核空间数据传输基本字符设备驱动函数模板2数字集成电路设计与系统应用专业3设备驱动程序基础Linux设备驱动概念驱动程序为操作硬件提供良好内部接口驱动程序为应用程序提供了访问设备的机制Linux设备驱动分类字符设备:键盘、鼠标、串口块设备:硬盘、Flash网络接口:以太网特定类型设备:audio设备数字集成电路设计与系统应用专业4系统调用和设备I/O数字集成电路设计与系统应用专业设备驱动健壮性和安全性驱动程序是内核的一部分驱动程序的漏洞和缺陷直接危及内核留心未初始化的指针,恶意用户程序,缓冲区溢出5数字集成电路设计与系统应用专业驱动程序与内核版本号Linux内核版本号简述2.0.x2.2.x2.4.x2.6.x版本号在内核编译过程中的影响模块加载时的版本号检查6数字集成电路设计与系统应用专业Linux下构建和运行模块为什么用模块?模块和应用程序有什么不同#ifdef__KERNEL__#ifdefMODULE7数字集成电路设计与系统应用专业Linux内核与模块中的并发可重入简介共享资源带来的问题并发与同步问题抢占式内核对并发的影响8数字集成电路设计与系统应用专业模块的编译和装载编译模块都需要什么编译模块相关的宏模块工具insmod,rmmod,lsmod,modprobe,modinfo9数字集成电路设计与系统应用专业Linux驱动程序模块加载10数字集成电路设计与系统应用专业模块的版本依赖版本号定义可以区分不同版本内核的接口函数可以在linux/version.h找到版本定义KERNEL_VERSION11数字集成电路设计与系统应用专业模块初始化和关闭模块加载调用的第一个函数init_module模块所使用资源的分配与释放使用计数模块卸载和cleanup_module显式指定初始化和清除函数12数字集成电路设计与系统应用专业一个简单的Linux内核模块1#includelinux/init.h2#includelinux/module.h3MODULE_LICENSE(DualBSD/GPL);4staticinthello_init(void)5{6printk(KERN_ALERTHelloWorldenter\n);7return0;8}9staticvoidhello_exit(void)10{11printk(KERN_ALERTHelloWorldexit\n);12}13module_init(hello_init);14module_exit(hello_exit);1516MODULE_AUTHOR(SongBaohua);17MODULE_DESCRIPTION(AsimpleHelloWorldModule);18MODULE_ALIAS(asimplestmodule);13数字集成电路设计与系统应用专业Linux内核模块的程序结构模块加载函数(必须)模块卸载函数(必须)模块许可证声明(必须)大多数情况下,内核模块应遵循GPL兼容许可权。Linux2.6内核模块最常见的是以MODULE_LICENSE(DualBSD/GPL)语句声明模块采用BSD/GPL双LICENSE。模块参数(可选)。模块导出符号(可选)模块作者等信息声明(可选)14数字集成电路设计与系统应用专业模块参数module_param(参数名,参数类型,参数读/写权限)module_param(myshort,short,0000);MODULE_PARM_DESC(myshort,Ashortinteger);module_param(myint,int,0000);MODULE_PARM_DESC(myint,Aninteger);#insmodhello.komyshort=55myint=45615数字集成电路设计与系统应用专业导出符号Linux2.6的“/proc/kallsyms”文件对应着内核符号表,它记录了符号以及符号所在的内存地址。模块可以使用如下宏导出符号到内核符号表:EXPORT_SYMBOL(符号名);EXPORT_SYMBOL_GPL(符号名);16数字集成电路设计与系统应用专业模块的使用计数Linux2.4内核:MOD_INC_USE_COUNT、MOD_DEC_USE_COUNTLinux2.6内核:try_module_get(&module)该函数用于增加模块使用计数module_put(&module)该函数用于减少模块使用计数17数字集成电路设计与系统应用专业Linux驱动程序基础知识Linux字符设备驱动基础file_opration结构分析用户空间与内核空间数据传输基本字符设备驱动函数模板18数字集成电路设计与系统应用专业19Linux字符设备驱动基础驱动注册与初始化主设备号和次设备号设备名设备文件节点操作与fileoperation结构用户系统调用与驱动函数集合数字集成电路设计与系统应用专业20设备名与主次设备号字符设备文件例子crw-rw----1rootuucp4,642005-03-2003:36/dev/ttyS0主设备号区分设备驱动程序次设备号区分同一个驱动程序创建的多个设备常见于多个串口,硬盘分区等mknod创建设备文件:mknod/dev/mydevicec2540open、close等操作/dev/下设备文件,内核根据文件的主设备号找到对应驱动程序主设备号可以分为动态、静态申请数字集成电路设计与系统应用专业动态分配主设备号alloc_chrdev_region注册动态主设备号(2.6)动态分配主设备号的优缺点/proc/devices和lsmod动态生成设备文件系统节点dev_t和kdev_t21数字集成电路设计与系统应用专业22file_opration结构分析file在内核中定义linux/fs.hfilestruct{Mode_tf_modeLoff_tf_posUnsignedintf_flagsStructfile_operations*f_opvoid*private_dataStructdentry*f_dentry}数字集成电路设计与系统应用专业Linux驱动程序基础知识Linux字符设备驱动基础file_opration结构分析用户空间与内核空间数据传输基本字符设备驱动函数模板23数字集成电路设计与系统应用专业24file_opration结构分析file_operations在内核中定义linux/fs.hstructfile_operations{structmodule*owner;loff_t(*llseek)(structfile*,loff_t,int);ssize_t(*read)(structfile*,char__user*,size_t,loff_t*);ssize_t(*write)(structfile*,constchar__user*,size_t,loff_t*);unsignedint(*poll)(structfile*,structpoll_table_struct*);int(*ioctl)(structinode*,structfile*,unsignedint,unsignedlong);int(*mmap)(structfile*,structvm_area_struct*);int(*open)(structinode*,structfile*);int(*release)(structinode*,structfile*);int(*fsync)(structfile*,structdentry*,intdatasync);……};数字集成电路设计与系统应用专业25file_operationsint(*open)(structinode*,structfile*)增加使用计数,检查错误如果未初始化,则调用初始化识别次设备号,如果必要,更新f_op指针分配并填写被置于filp-private_data的数据结构int(*realse)(structinode*,structfile*)open逆操作数字集成电路设计与系统应用专业26file_operationsssize_t(*read)(structfile*,char*,size_t,loff_t*)用户空间和内核空间数据交互用户空间指针和内核指针驱动调用copy_to_user()将数据返回给用户ssize_t(*write)(structfile*,constchar*,size_t,loff_t*)驱动调用copy_from_user()将用户数据读到本地buffer数字集成电路设计与系统应用专业27file_operationsint(*mmap)(structfile*,structvm_area_struct*)驱动中提供该方法用于支持用户mmap操作用户将设备内存区映射到进程的地址空间,直接操作该物理内存提高效率典型例子:framebuffer,sound,capture等驱动数字集成电路设计与系统应用专业28file_operationsint(*ioctl)(structinode*,structfile*,unsignedintcmd,unsignedlongarg)驱动程序一般需支持通过Ioctl实现各种控制与参数设置,如串口可设置波特率等多参数cmd变量存放命令,驱动代码根据cmd里面的值进行switch-cas处理分支arg存放参数,如为整数,可直接使用。如为指针,驱动程序首先要检查指针的合法性intaccess_ok(inttype,constvoid*addr,unsignedlongsize);检查通过后可以使用驱动程序还可通过intcapable(intcapability)函数来确定调用进程是否有权执行操作数字集成电路设计与系统应用专业29怎样使用ioctl用户系统调用:intioctl(intfd,intcmd,...);cmd命令码格式用户空间与Linux内核中定义需一致:___________________________________|设备类型|序列号|方向|数据尺寸||----------|--------|------|--------||8bit|8bit|2bit|8~14bit|数字集成电路设计与系统应用专业Ioctl权能和受限操作驱动程序的访问控制采用linux文件系统的权限机制驱动程序采用权能机制来控制特殊的操作权限驱动程序通过intcapable(intcapability);函数来确定调用进程是否有权执行操作30数字集成电路设计与系统应用专业用ioctl控制驱动程序实例分析intscull_ioctl(structinode*inode,structfile*filp,unsignedintcmd,unsignedlongarg){interr=0,tmp;intretval=0;if(_IOC_TYPE(cmd)!=SCULL_IOC_MAGIC)return-ENOTTY;if(_IOC_NR(cmd)SCULL_IOC_MAXNR)return-ENOTTY;if(_IOC_DIR(cmd)&_IOC_READ)err=!access_ok(VERIFY_WRITE,(void__user*)arg,_IOC_SIZE(cmd));elseif(_IOC_DIR(cmd)&_IOC_WRITE)err=!ac