1、驱动硬件是操作系统最基本的功能。它通过驱动程序来操作硬件。驱动程序是内核和硬件之间的接口,为应用程序屏蔽了硬件的细节。驱动程序最为操作系统最基本组成部分,具有以下功能:对设备初始化和释放。驱动程序要完成设备注册,初始化及对设备的卸载数据传送。驱动程序最重要的功能就是在内核,硬件和应用程序之间传送数据。即把数据从内核传到硬件和从硬件读取数据;读取应用程序传给设备文件的数据和回送应用程序要求的数据检测和处理设备出现的错误。2024/5/21 周二1Linux输入/输出系统层次结构和功能:用户应用程序用户应用程序(设备)文件系统(设备)文件系统设备驱动程序设备驱动程序物理设备控制器输入输入/输出请求
2、输出请求输入输入/输出响应输出响应物理设备物理设备物理设备控制器物理设备控制器2024/5/21 周二2驱动程序通常是以内核模块的形式加载进内核 或直接 编译进内核,(但编译进内核是某些驱动运行的唯一方法。例如:console驱动,flash驱动和对至少一种文件系统的支持等等。)是内核的一部分。与应用程序的执行过程不同,模块通常只是预先向内核注册自己,当内核需要时响应请求。用户的应用程序以设备文件方式访问驱动程序。即linux把设备当文件,通过文件系统对设备进行访问所有的设备的驱动程序有共性,了解驱动程序的结构,对嵌入式系统的开发有价值2024/5/21 周二3设备驱动程序流程图:insmod
3、rmmodinit_module()clean_module()模块内核设备功能设备注册设备卸载用户调用2024/5/21 周二4LINUX的设备管理 嵌入式嵌入式Linux设备驱动程序的设计大致包括以下步骤:设备驱动程序的设计大致包括以下步骤:向系统申请也可以动态获得主、次设备号。向系统申请也可以动态获得主、次设备号。实现设备初始化和卸载模块。实现设备初始化和卸载模块。(以模块方式)以模块方式)设计对设备文件操作。如定义设计对设备文件操作。如定义file_operations结构。结构。设计对设备文件操作调用。如设计对设备文件操作调用。如read、write等操作。等操作。实现中断服务函数,
4、用实现中断服务函数,用request_irq向内核注册。向内核注册。将驱动程序编译到内核或编译成模块,用将驱动程序编译到内核或编译成模块,用ismod命令加载。命令加载。生成设备节点文件。生成设备节点文件。2024/5/21 周二5模块作为一种抽象数据类型,它具有一个可以通过静态内核中断的接口。最小的模块结构必须包括两个函数:init_module()和cleanup_module(),它们在系统加载模块和卸载模块时被调用。也可以编写一个只包括这两个函数的模块,这样该模块中唯一会被调用的函数就是模块被加载时所调用的函数init_module()和模块被卸载时所调用的函数cleanup_modu
5、le()。并且用函数init_module()来启动模块加载期间的操作,用函数cleanup_module()来停止这些操作。由于模块可以实现相当复杂的功能,故可以在模块中加入很多新函数以实现所期望的功能。不过加入模块的每个新函数都必须在该模块加载到内核中时进行注册。若该模块是静态加载的,则该模块的所有函数都是在内核启动时进行注册;若该模块是动态加载的,则这些新函数必须在加载这个模块时动态注册。当然,如果该模块被动态卸载了,则该模块的函数都必须从系统中注销。通过这种方式,当这个模块不在系统中时,就不能调用该模块的函数。其中注册工作通常是在函数init_module()中完成的,而注销工作则是在
6、函数cleanup_module()中完成。1 1 模块的组织模块的组织结构结构2024/5/21 周二6应用程序与内核模块的比较源程序:有main();没有main(内核模块结构)编译:gcc;gcc加参数(幻33)运行:运行命令;模块加载(用命令insmod rmmod 或内核裁减,以模块方式或编译进内核,由内核需要时调用)2024/5/21 周二7内核模块结构#include /说明是个内核功能说明是个内核功能#include /声明是一个模块声明是一个模块 /其它其它header信息信息 int init_module()/加载时,初始化模块的编码加载时,初始化模块的编码 如注册如注册
7、register_chrdev /期望该模块所能实现的一些功能函数,如期望该模块所能实现的一些功能函数,如open()、release()、write()、read()、ioctl()等等函数函数 void cleanup_module()/卸载时,注销模块的编码卸载时,注销模块的编码 module_init(dri_arch_init_module);加载本驱动是执行加载本驱动是执行module_exit(dri_arch_cleanup_module);卸载驱动时执行卸载驱动时执行2024/5/21 周二8一般编译模块文件的命令格式如下:#gcc-O2 g-Wall-DMODULE-D_K
8、ERNEL_-c f -I/usr/src/linux-2.4/include filename.c /filename.c为自己编写的模块程序源代码文件(2)2)模块的编译模块的编译 (3)(3)模块的加载模块的加载(4)(4)模块的卸载模块的卸载Insmod命令调用module_initrmmod命令调用module_exit2024/5/21 周二9LINUX内核模块结构介绍内核模块结构介绍#include /所有模块都需要的头文件#include /init&exit相关宏static int _init hello_init(void)printk(Hello module init
9、n);return 0;内核模块的基本框架(1/2):2024/5/21 周二10LINUX内核模块结构介绍内核模块结构介绍static void _exit hello_exit(void)printk(Hello module exitn);module_init(hello_init);module_exit(hello_exit);内核模块的基本框架(2/2):2024/5/21 周二11LINUX内核模块结构介绍内核模块结构介绍gcc-D_KERNEL_-DMODULE-DLINUX-I/usr/src/linux2.4/include-c-o hello.o hello.c ins
10、mod./hello.o rmmod hello 内核模块的编译和加载:2024/5/21 周二128.2.1设备驱动程序程序简介介2024/5/21 周二138.2.2 设备驱动程序与外界的接口设备驱动程序与外界的接口2024/5/21 周二14模块作为一种抽象数据类型,它具有一个可以通过静态内核中断的接口。最小的模块结构必须包括两个函数:init_module()和cleanup_module(),它们在系统加载模块和卸载模块时被调用。也可以编写一个只包括这两个函数的模块,这样该模块中唯一会被调用的函数就是模块被加载时所调用的函数init_module()和模块被卸载时所调用的函数clea
11、nup_module()。并且用函数init_module()来启动模块加载期间的操作,用函数cleanup_module()来停止这些操作。由于模块可以实现相当复杂的功能,故可以在模块中加入很多新函数以实现所期望的功能。不过加入模块的每个新函数都必须在该模块加载到内核中时进行注册。若该模块是静态加载的,则该模块的所有函数都是在内核启动时进行注册;若该模块是动态加载的,则这些新函数必须在加载这个模块时动态注册。当然,如果该模块被动态卸载了,则该模块的函数都必须从系统中注销。通过这种方式,当这个模块不在系统中时,就不能调用该模块的函数。其中注册工作通常是在函数init_module()中完成的,
12、而注销工作则是在函数cleanup_module()中完成。7.2.1 7.2.1 模块的组织模块的组织结构结构2024/5/21 周二15#include /说明是个内核功能#include /声明是一个模块 /其它header信息 int init_module()/加载时,初始化模块的编码 /期望该模块所能实现的一些功能函数,如open()、release()、write()、read()、ioctl()等函数 void cleanup_module()/卸载时,注销模块的编码 2024/5/21 周二16一般编译模块文件的命令格式如下:#gcc-O2 g-Wall-DMODULE-D_
13、KERNEL_-c f -I/usr/src/linux-2.4/include filename.c /filename.c为自己编写的模块程序源代码文件 7.2.2 7.2.2 模块的编译模块的编译 7.2.3 7.2.3 模块的加载模块的加载7.2.4 7.2.4 模块的卸载模块的卸载2024/5/21 周二17图7-1 模块链接到内核的示意图7.2.5 7.2.5 模块链接到内核的示意图模块链接到内核的示意图2024/5/21 周二18在内核是用一个file结构来识别模块,而且内核使用file_operations结构来访问模块程序中的函数。file_operations结构是一个定义
14、在中的函数指针表。管理模块的文件操作,通常也称为“方法”,它们都为struct file_operations提供函数指针。在struct file_operations中的操作一般按如下顺序出现,除非特别说明,一般它们返回0值时表示访问成功;发生错误时会返回一个负的错误值(目前共有13个操作):int(*lseek)()、int(*read)()、int(*write)()int(*readdir)()、int(*select)()、int(*ioctl)()int(*mmap)()、int(*open)()、void(*release)()int(*fsync)()、int(*fasync
15、)()int(*check_media_change)()int(*revalidate)()u用于字符设备的I/O调用主要有:open()、release()、read()、write()和ioctl()。7.2.6 7.2.6 模块管理程序中的文件操作模块管理程序中的文件操作2024/5/21 周二198.2.4 设备驱动程序的代程序的代码设备驱动程序是一些函数和数据结构的集合,这些函数和数据结构是为实现设备管理的一个简单接口。操作系统内核使用这个接口来请求驱动程序对设备进行I/O操作。甚至,我们可以把设备驱动程序看成是一个抽象数据类型,它为计算机中的每个硬件设备都建立了一个通用函数接口。
16、由于一个设备驱动程序就是一个模块,所以在内核内部用一个file结构来识别设备驱动程序,而且内核使用file_operations结构来访问设备驱动程序中的函数。2024/5/21 周二20模模块程序程序结构分析构分析file_operations结构的定构的定义头文件及程序定文件及程序定义open()函数函数init_module()函数函数release()函数函数read()函数函数write()函数函数cleanup_module()函数函数驱动驱动2024/5/21 周二21字符设备的注册字符设备的注册 字符设备驱动程序入口点在xxx_init_modules函数。通过系统调用regi
17、ster_chrdev()向系统注册字符型设备驱动程序,在内核中登记设备驱动程序。register_chrdev()定义如下:#include#include int register_chrdev (unsigned int major,const char*name,struct file_operations*ops)2024/5/21 周二22字符设备驱动程序注册要用到的数据结构定义:struct device_struct const char*name;struct file_operations*chops;static struct device_struct chrdevsM
18、AX_CHRDEV;8.4.1 8.4.1 字符字符类类型型设备设备的的驱动驱动程序程序 当字符设备向内核进行注册时,设备的file_operations结构及其名字将被添加到一个全局性的chrdevs数组中去,这个数组是由一些device_struct结构组成,数组的下标就是设备的主设备号,该数组被成为字符设备切换表。因此,通过查看chrdevsYOUR_MAJOR-fops,内核就会指导如何与设备进行交互一级设备支持那些入口点。当成功调用register_chrdev后,设备注册到了内核,并且给定的文件操作结构也添加到了字符设备切换表里。2024/5/21 周二23*file_operat
19、ions结构定义如下,即定义chr设备的_fops*static int scull_open(struct inode*inode,struct file*filp);/打开字符设备。static int scull_release(struct inode*inode,struct file*filp);/释放字符设备。static ssize_t scull_write(struct inode*inode,struct file*filp,const char*buffer,int count);/将数据送往字符设备。static ssize_t scull_read(struct i
20、node*inode,struct file*filp,char*buffer,int count);/从字符设备读出数据,写入用户空间。static int scull_ioctl(struct inode*inode,struct file*filp,unsigned long int cmd,unsigned long arg);/字符设备的控制操作。字符设备的结构字符设备的结构2024/5/21 周二24file_operations结构的定义结构的定义当一个进程试图对生成的设备进行操作的时刻就利用下面这个数据结构,这个结构就是提供给操作系统的接口,它的指针保存在设备表中,在init_
21、module()中被传递给操作系统。struct file_operations Fops_Get=read:device_read,write:device_write,open:device_open,ioctl:device_ioctl,release:device_release,;2024/5/21 周二25open()函数 static int scull_open(struct inode*inode,struct file*filp)MOD_INC_USE_COUNT;return 0;release()函数static int scull_release(struct ino
22、de*inode,struct file*filp)MOD_DEC_USE_COUNT;return 0;2024/5/21 周二26驱动程序驱动程序的测试的测试 在该驱动模块程序编译加载后,再在/dev目录下创建模块设备文件chrdev,使用命令:#mknod /dev/chrdev c major minor其中“c”表示moduledev是字符设备,“major”是moduledev的主设备号。(该字符设备驱动程序编译加载后,可在/proc/devices获得主设备号)2024/5/21 周二27函数函数scull_open()scull_open()的的流程图流程图开始该设备的用户数目
23、增加1;MOD_INC_USE_COUNT;打印“This chrdev is in open!”成功,返回0结束图8-52024/5/21 周二28函数函数scull_write()scull_write()的的流程图流程图图8-62024/5/21 周二29函数函数scull_read()的的流程图流程图图8-72024/5/21 周二30函数函数scull_ioctl()的的流程图流程图图8-82024/5/21 周二31函数函数scull_release()的的流程图流程图图8-92024/5/21 周二32字符设备驱动程序的测试函数流程图字符设备驱动程序的测试函数流程图测试函数测试函数:图8-102024/5/21 周二33