资源描述
Linux系统驱动概述,驱动程序与应用程序的区别,应用程序一般有一个main函数,从头到尾执行一个任务;应用程序可以和GLIBC库连接驱动程序没有main函数,通过使用宏module_init(初始化函数名)将初始化函数加入内核全局初始化函数列表中,在内核初始化时执行驱动的初始化函数,从而完成驱动的初始化和注册,之后驱动便停止等待被应用软件调用。驱动程序中有一个宏moudule_exit(退出处理函数名)注册退出处理函数。它在驱动退出时被调用。驱动程序中是不能使用标准C库的,内核版本与编译器的版本依赖,当模块与内核链接时,insmod会检查模块和当前内核版本是否匹配,每个模块都定义了版本符号_module_kernel_version,这个符号位于模块文件的ELF头的.modinfo段中。只要在模块中包含,编译器就会自动定义这个符号每个内核版本都需要特定版本的编译器的支持,高版本的编译器并不适合低版本的内核,Linux-2.4版本的insmod命令装载模块时,首先从/lib/modules目录和内核相关的子目录中查找模块文件,如果需要从当前目录装载,使用insmodmodule.o。,设备驱动程序的作用,设备驱动程序将复杂的硬件抽象成一个结构良好的设备,并通过提供统一的程序接口为系统的其它部分提供使用设备的能力和方法。设备驱动程序(应该只是)为系统的其它部分提供各种使用设备的能力,使用设备的方法应该由应用程序决定。Linux下对外设的访问只能通过驱动程序Linux对于驱动程序有统一的接口,以文件的形式定义系统的驱动程序:Open、Release、read、write、ioctl驱动程序是内核的一部分,可以使用中断、DMA等操作驱动程序需要在用户态和内核态之间传递数据,设备驱动程序的分类,字符设备驱动程序各种串行接口,并行接口等。块设备驱动程序磁盘设备等网络设备驱动程序网卡等。杂项设备驱动程序不属于上述三种设备之外的一些设备,如SCSI,时钟等。,在操作系统中的位置,设备驱动程序是内核代码的一部分。驱动程序的地址空间是内核的地址空间。驱动程序的代码直接对设备硬件(实际是设备的各种寄存器)进行控制(实际就是读写操作)。应用程序通过操作系统的系统调用执行相应的驱动程序函数。中断则直接执行相应的中断程序代码。设备驱动程序的file_operations结构体的地址被注册到内核中的设备链表中。块设备和字符设备以设备文件的方式建立在文件系统中的/dev目录下,而且每个设备都有一个主设备号和一个次设备号。,在操作系统中的位置,主设备号和次设备号,主设备号相同的设备使用相同的驱动程序,次设备号用于区分具体设备的实例。主设备号标识设备对应的驱动程序一个驱动程序可以控制若干个设备,次设备号提供了一种区分它们的方法系统增加一个驱动程序就要赋予它一个主设备号。这一赋值过程在驱动程序的初始化过程中intregister_chrdev(unsignedintmajor,constchar*name,structfile_operations*fops);,主设备号和次设备号,创建设备节点,设备已经注册到内核表中,对于设备的访问通过设备文件(设备文件与设备驱动程序的主设备号匹配),内核会调用驱动程序中的正确函数给程序一个它们可以请求设备驱动程序的名字。这个名字必须插入到/dev目录中,并与驱动程序的主设备号和次设备号相连使用mknod在文件系统上创建一个设备节点mknod/dev/mydevicec2540,动态分配设备号,在Documentation/device.txt文件中可以找到已经静态分配给大部分设备的列表由于许多数字已经分配了,为新设备选择一个唯一的号码是很困难的如果调用register_chrdev时的major为零,函数就会选择一个空闲号码并做为返回值返回,动态分配的问题,动态分配的主设备号不能保证总是一样的,无法事先创建设备节点可以从/proc/devices读取cat/proc/devices利用脚本动态创建设备文件节点,设备管理的问题,如今,Linux支持很多不同种类的硬件。这意味着/dev中都有数百个特殊文件来表示所有这些设备。而且,这些特殊文件中大多数甚至不会映射到系统中存在的设备上,使用devfs,在Linux2.4的内核里引入了devfs来解决linux下设备文件管理的问题在驱动程序中通过devfs_register()函数创建设备文件系统的节点系统启动的时候mount设备文件系统所有需要的设备节点都由内核自动管理。/dev目录下只有挂载的设备,Linux2.6内核与devfs,Linux2.6内核引入了sysfs文件系统为每个系统的硬件树进行分级处理Devfs在Linux2.6中被标记为舍弃的特性(在Linux2.6.15及以后的版本则取消了对它的支持),而使用udev。维护动态设备从sysfs获得的信息,可以提供对特定设备的固定设备名。对于热插拔的设备,这尤其重要udev是在用户空间的脚本文件,这很容易被编辑和修改可以和hotplug脚本配合使用为了保证旧应用程序的兼容性,在嵌入式系统中,用devfs还是一个好方法。即使在Linux2.6.15内核以后,也可以通过ndevfs(nanodevfs)补丁提供对devfs特性的兼容。,在Linux2.6内核中使用udev,建议,在2.6.15以后的版本中使用udev使用ramfs作为udev的载体mounttramfsnone/devudev使用的规则集位于/etc/udev/*udev的官方地址:http:/www.kernel.org/pub/linux/utils/kernel/hotplug/udev.html,设备驱动程序的使用方法,应用层使用open、close、read、write系统调用需要编写应用程序使用系统命令可以进行最基本的测试:cat/dev/urandomecho/dev/urandom/dev/fb0ddif=/dev/touchscreenof=/var/tmp/testbs=16count=100,Linux设备驱动程序结构,结构体file_operations的定义,在include/linux/fs.h中主要包括:open,close(或者release),read,write,ioctl,poll,mmap等,简单的Linux驱动程序原理,Linux设备驱动程序结构的例子(1),Linux设备驱动程序结构的例子(2),/*驱动程序中使用的各种函数的原型声明。标准的作法是将函数原型声明*放在一个头文件中,然后在该文件开始处使用#include引用,并在该*文件中定义。*这里我们将函数的声明和定义放在一起。所以下面的代码既是函数的声明,*也是函数的定义。*/staticssize_tdemo_read(structfile*filp,char*buffsize_tcnt,loof_t*off)/*这里是read函数的代码*/returnret;staticssize_tdemo_write(structfile*filp,char*buffsize_tcnt,loff_t*off)/*这里是write函数的代码*/returnret;,Linux设备驱动程序结构的例子(3),staticintdemo_ioctl(structinode*inode,structfile*filpunsignedintcmd,unsignedlongarg)/*这里是ioctl函数的代码,它的一般格式为一个switch分支语句*switch(cmd)*caseCMD1:*.*break;*.*caseCMDn:*.*break*default:*.*break;*/returnret;ioctl()函数用于控制驱动程序本身的一些特性和参数,如设定驱动程序使用的缓冲区的大小,设定串行通讯的速率等。,Linux设备驱动程序结构的例子(4),staticintdemo_open(structinode*inode,structfile*filp)/*这里是open函数的代码*/returnret;staticintdemo_close(structinode*inode,structfile*filp)/*这里是close函数的代码*/returnret;,上述5个函数,既read(),write(),ioctl(),open(),close(),是一个字符设备驱动程序最基本的需要由驱动程序的作者完成的函数。这5个函数将对应于相应的5个系统调用:,Linux设备驱动程序结构的例子(5),staticstructfile_operationsdemo_fops=read:demo_read,write:demo_write,ioctl:demo_ioctl,open:demo_open,release:demo_close,;,file_operations是一个结构体类型,定义在include/linux/fs.h中。上述代码定义了一个file_operations类型的结构体demo_fops,并将其中的一些成员赋了初值。结构体demo_fops将作为一个参数在注册一个设备驱动程序时传递给内核。内核使用设备链表维护各种注册的设备。不同类型的设备使用不同的链表。,Linux设备驱动程序结构的例子(6),staticint_initdemo_init(void)/*设备初始化代码等*/if(register_chrdev(DEMO_MAJOR,“demo”,Linux设备驱动程序结构的例子(7),module_init(demo_init);module_exit(demo_exit);这两个函数,module_init()和module_exit(),用于告诉内核,当一个驱动程序加载和退出(或撤消)时,需要执行的操作。不同驱动程序在加载和退出时,除了基本的向内核注册设备驱动程序外,还有各自的针对具体设备的操作。,Linux设备驱动程序结构的例子(8),要点总结:宏:_KERNEL_,MODULE,_VERSION_KERNEL_:表明这将是用于内核的代码,否则很多内核过程将无法使用。MODULE:如果是以模块方式编译,需要定义这个宏;如果是静态连接则不用。_VERSION_:定义这个宏则需要驱动程序的内核版本要和内核版本一致。module_init()/module_exit():demo_init()/demo_exit()每个驱动程序都要有这两个函数,它们分别用于设备驱动程序的加载和撤消。staticstructfile_operationsdemo_fops:每个驱动程序都要有这样的结构体,可能不止一个。用register_chrdev()注册驱动程序时这个结构体的起始地址被传送到内核的设备表中。DEMO_MAJOR:每个设备驱动程序有一个主设备号(majornumber)。不同设备驱动程序不能使用相同的主设备号。一个设备驱动程序可以管理不同的(但一般是同一类的)设备,通过次设备号(minornumber)区分。demo_open()/close(),read()/write(),ioctl():根据具体驱动程序定义和使用。一般open()/close()总是需要的,而且open()和close()一定要成对出现。,设备驱动程序的使用,驱动程序模块的动态链接和静态链接创建设备文件使用设备,驱动程序模块的加载,设备驱动程序被静态编译到内核中的情况:module_init()指示内核在启动过程中运行设备的初始化函数,如demo_init()函数。驱动程序的加载随内核的启动一起完成。静态编译的内核模块不能被动态卸载,只有到系统关闭时由内核执行相应的卸载函数,如demo_exit()。嵌入式操作系统一般使用静态内核模块以减少系统的尺寸和复杂性。设备驱动程序被动态加载到内核中的情况:首先,驱动程序需要被编译成目标文件,如demo.o。在操作系统运行之后,使用insmod命令将驱动程序模块动态加载到内核中$insmoddemo.o使用insmod命令动态加载的内核模块可以使用rmmod命令动态地从内核中卸载$rmmoddemo.o使用内核的动态模块加载/卸载功能需要内核支持kmod功能。,创建设备文件,使用设备驱动,应用程序系统调用设备驱动程序设备(寄存器)使用一个设备一般需要执行如下一些操作:1.打开设备文件。2.对设备进行必要的设置,如设置串口速率。3.对设备进行读、写等操作,如通过串口收发数据。4.结束对设备的使用之前,如果改变了设备的某些设置,则将其恢复到缺省状态,保证设备停用后没有任何不好的副作用。5.关闭设备。,使用设备驱动,Linux的驱动开发调试有两种方法,直接编译到内核,再运行新的内核来测试。效率较低,但在某些场合是唯一的方法编译为模块的形式,单独加载运行调试。模块方式调试效率很高,它使用insmod工具将编译的模块直接插入内核,如果出现故障,可以使用rmmod从内核中卸载模块。不需要重新启动内核,这使驱动调试效率大大提高。,模块方式驱动程序编译的两种方法,在内核中编译。“参考内核的移植和编译.ppt中”的一个demo驱动程序编译的例子独立编译。在驱动源代码目录下编写makefile链接内核源代码,将它编译为模块。,
展开阅读全文