嵌入式应用程序设计第8章嵌入式Linux设备驱动编程2第1章搭建嵌入式Linux开发环境第2章嵌入式文件I/O编程第3章嵌入式Linux多任务编程第4章嵌入式Linux进程间通行第5章嵌入式Linux多线程编程第6章嵌入式Linux网络编程第7章Qt图形编程第8章嵌入式Linux设备驱动编程第9章Qt聊天项目设计课程安排:38.1设备驱动编程基础8.2字符设备驱动编程8.3GPIO驱动程序实例8.4按键驱动编程实例8.5小结8.6思考与练习本章课程:48.1.1Linux设备驱动概述设备驱动概念操作系统是通过各种驱动程序来驾驭硬件设备的,它为用户屏蔽了各种各样的设备,驱动硬件是操作系统最基本的功能,并且提供统一的操作方式。设备驱动程序是操作系统最基本的组成部分之一,在Linux内核源程序中也占有60%以上。因此,熟悉驱动的编写是很重要的。Linux的一个重要特点就是将所有的设备都当做文件进行处理,这一类特殊文件就是设备文件(通常在/dev目录下),这样在应用程序看来,硬件设备只是一个设备文件,应用程序可以象操作普通文件一样对硬件设备进行操作,这样就大大方便了对设备的处理。8.1设备驱动编程基础58.1.1Linux设备驱动概述Linux系统的设备分为三类:字符设备、块设备和网络设备。字符设备通常指像普通文件或字节流一样,以字节为单位顺序读写的设备,如并口设备、虚拟控制台等。块设备通常指一些需要以块为单位随机读写的设备,如IDE硬盘、SCSI硬盘、光驱等。网络设备通常是指通过网络能够与其他主机进行数据通信的设备,如网卡等。8.1设备驱动编程基础68.1.1Linux设备驱动概述设备驱动程序的特点内核代码内核接口内核机制和服务可装载可设置动态性8.1设备驱动编程基础78.1.1Linux设备驱动概述设备驱动程序与整个软硬件系统的关系8.1设备驱动编程基础88.1.2Linux内核模块编程设备驱动和内核模块Linux设备驱动属于内核的一部分,Linux内核的一个模块可以以两种方式被编译和加载。直接编译进Linux内核,随同Linux启动时加载;编译成一个可加载和删除的模块。8.1设备驱动编程基础98.1.2Linux内核模块编程模块相关命令lsmod列出当前系统中加载的模块rmmod是用于将当前模块卸载。insmod和modprobe是用于加载当前模块8.1设备驱动编程基础108.1.2Linux内核模块编程Linux内核模块编程一个Linux内核模块主要由以下几个部分组成。模块加载函数(必须)模块卸载函数(必须)模块许可证声明(必须模块参数(可选)模块导出符号(可选)模块作者等信息声明(可选)8.1设备驱动编程基础118.1.2Linux内核模块编程Linux内核模块编程模块加载函数staticint__initinitialization_function(void){/*初始化代码*/}module_init(initialization_function);模块卸载函数staticvoid__exitcleanup_function(void){/*释放代码*/}module_exit(cleanup_function);8.1设备驱动编程基础128.1.2Linux内核模块编程Linux内核模块编程通常来说,模块卸载函数要完成与模块加载函数相反的功能若模块加载函数注册XXX,则模块卸载函数应该注销XXX。若模块加载函数动态申请了内存,则模块卸载函数应释放该内存。若模块加载函数申请了硬件资源(中断、DMA通道、I/O端口和I/O内存等)的占用,则模块卸载函数应释放这些硬件资源。若模块加载函数开启了硬件,则卸载函数中一般要关闭硬件。8.1设备驱动编程基础138.1.2Linux内核模块编程Linux内核模块编程模块参数“module_param(参数名,参数类型,参数读/写权限)”为模块定义一个参数staticchar*str_param=LinuxModuleProgram;staticintnum_param=4000;module_param(num_param,int,S_IRUGO);module_param(str_param,charp,S_IRUGO);8.1设备驱动编程基础148.1.2Linux内核模块编程Linux内核模块编程导出符号EXPORT_SYMBOL(符号名);EXPORT_SYMBOL_GPL(符号名);模块声明与描述MODULE_AUTHOR(author);MODULE_DESCRIPTION(description);MODULE_VERSION(version_string);MODULE_DEVICE_TABLE(table_info);MODULE_ALIAS(alternate_name);8.1设备驱动编程基础158.1.2Linux内核模块编程Linux内核模块编程模块的使用计数Linux2.4内核中,模块自身通过MOD_INC_USE_COUNT、MOD_DEC_USE_COUNT宏来管理自己被使用的计数。Linux2.6内核提供了模块计数管理接口try_module_get(&module)和module_put(&module),从而取代Linux2.4内核中的模块使用计数管理宏。8.1设备驱动编程基础168.1.2Linux内核模块编程Linux内核模块编程模块编译我们可以为HelloWorld模块程序编写一个简单的Makefile,如下所示:obj-m:=hello.o并使用如下命令编译HelloWorld模块,如下所示:$make-C/usr/src/linux-2.6.15.5/M=/driver_study/modules如果当前处于模块所在的目录,以下命令与上述命令同等:$make–C/usr/src/linux-2.6.15.5M=$(pwd)modules8.1设备驱动编程基础178.1.2Linux内核模块编程Linux内核模块编程模块与GPL对于自己编写的驱动等内核代码,如果不编译为模块则无法绕开GPL,编译为模块后企业在产品中使用模块。8.1设备驱动编程基础188.1.2Linux内核模块编程Linux内核模块编程内核模块示例8.1设备驱动编程基础198.2.1字符设备驱动编写流程8.2字符设备驱动编程用户调用模块init_module()cleanup_module()内核注册设备卸载设备设备功能insmodrmmod208.2.2重要数据结构file_operationsstructfile_operations{loff_t(*llseek)(structfile*,loff_t,int);ssize_t(*read)(structfile*filp,char*buff,size_tcount,loff_t*offp);ssize_t(*write)(structfile*filp,constchar*buff,size_tcount,loff_t*offp);int(*readdir)(structfile*,void*,filldir_t);unsignedint(*poll)(structfile*,structpoll_table_struct*);int(*ioctl)(structinode*,structfile*,unsignedint,unsignedlong);int(*mmap)(structfile*,structvm_area_struct*);int(*open)(structinode*,structfile*);int(*flush)(structfile*);int(*release)(structinode*,structfile*);int(*fsync)(structfile*,structdentry*);int(*fasync)(int,structfile*,int);int(*check_media_change)(kdev_tdev);int(*revalidate)(kdev_tdev);int(*lock)(structfile*,int,structfile_lock*);};8.2字符设备驱动编程218.2.2重要数据结构structinode结构提供了关于设备文件/dev/driver(假设此设备名为driver)的信息,file结构提供关于被打开的文件信息,主要用于与文件系统对应的设备驱动程序使用。structfile{mode_tf_mode;/*标识文件是否可读或可写,FMODE_READ或FMODE_WRITE*/dev_tf_rdev;/*用于/dev/tty*/off_tf_pos;/*当前文件位移*/unsignedshortf_flags;/*文件标志,如O_RDONLY、O_NONBLOCK和O_SYNC*/unsignedshortf_count;/*打开的文件数目*/unsignedshortf_reada;structinode*f_inode;/*指向inode的结构指针*/structfile_operations*f_op;/*文件索引指针*/};8.2字符设备驱动编程228.2.3设备驱动程序主要组成早期版本的字符设备注册register_chrdev()unregister_chrdev()8.2字符设备驱动编程238.2.3设备驱动程序主要组成早期版本的字符设备注册unregister_chrdev()8.2字符设备驱动编程248.2.3设备驱动程序主要组成设备号相关函数获取设备号MAJOR(dev_tdev);/*获得主设备号*/MINOR(dev_tdev);/*获得次设备号*/MKDEV(intmajor,intminor);设备注册于注销8.2字符设备驱动编程258.2.3设备驱动程序主要组成设备号相关函数获取设备号MAJOR(dev_tdev);/*获得主设备号*/MINOR(dev_tdev);/*获得次设备号*/MKDEV(intmajor,intminor);设备注册于注销8.2字符设备驱动编程268.2.3设备驱动程序主要组成新版本设备注册8.2字符设备驱动编程278.2.3设备驱动程序主要组成打开设备int(*open)(structinode*,structfile*);通常情况下在open函数接口中要完成如下工作:如果未初始化,则进行初始化。识别次设备号,如果必要,更新f_op指针。分配并填写被置于filp-private_data的数据结构。检查设备特定的错误(诸如设备未就绪或类似的硬件问题)。8.2字符设备驱动编程288.2.3设备驱动程序主要组成释放设备释放设备的函数接口是release()。释放设备时要完成的工作如下:释放打开设备时系统所分配的内存空间(包括filp-private_data指向的内存空间)。在最后一次关闭设备(使用close()系统调用)时,才会真正释放设备(执行release()函数)。即在打开计数等于0时的close()系统调用才会真正进行设备的释放操作。8.2字符设备驱动编程298.2.3设备驱动程序主要组成读写设备read()和write()函数8.2字符设备驱动编程308.2.3设备驱动程序主要组成读写设备copy_to_user()和copy_from_user()8.2字符设备驱动编程318.2.3设备驱动程序主要组成ioctl大部分设备除了读写操作,还需要硬件配置和控制(例如,设置串口设备的波特率)等很多其他操作。在字符设备驱动中ioctl函数接口给用户提供对设备的非读写