《通信嵌入式系统与应用》 课件 第6讲 ARM Linux设备驱动程序开发_第1页
《通信嵌入式系统与应用》 课件 第6讲 ARM Linux设备驱动程序开发_第2页
《通信嵌入式系统与应用》 课件 第6讲 ARM Linux设备驱动程序开发_第3页
《通信嵌入式系统与应用》 课件 第6讲 ARM Linux设备驱动程序开发_第4页
《通信嵌入式系统与应用》 课件 第6讲 ARM Linux设备驱动程序开发_第5页
已阅读5页,还剩35页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

《通信嵌入式系统技术

与应用》第6讲第四章ARMLinux设备驱动程序开发一.设备驱动概述1.设备驱动简介及驱动模块2.设备文件分类 3.设备号 4.驱动层次结构 5.设备驱动程序与外界的接口 6.设备驱动程序的特点1.设备驱动简介及驱动模块操作系统是通过各种驱动程序来驾驭硬件设备的,它为用户屏蔽了各种各样的设备,驱动硬件是操作系统最基本的功能,并且提供统一的操作方式。设备驱动程序是内核的一部分,硬件驱动程序是操作系统最基本的组成部分,在Linux内核源程序中也占有60%以上。因此,熟悉驱动的编写是很重要的。Linux内核中采用可加载的模块化设计(LKMs,LoadableKernelModules),一般情况下编译的Linux内核是支持可插入式模块的,也就是将最基本的核心代码编译在内核中,其他的代码可以选择在内核中,或者编译为内核的模块文件。常见的驱动程序也是作为内核模块动态加载的,比如声卡驱动和网卡驱动等,而Linux最基础的驱动,如CPU、PCI总线、TCP/IP协议、APM(高级电源管理)、VFS等驱动程序则直接编译在内核文件中。有时也把内核模块叫做驱动程序,只不过驱动的内容不一定是硬件罢了,比如ext3文件系统的驱动。因此,加载驱动时就是加载内核模块。2.设备文件分类在前面提到过,Linux的一个重要特点就是将所有的设备都当做文件进行处理,这一类特殊文件就是设备文件,它们可以使用前面提到的文件、I/O相关函数进行操作,这样就大大方便了对设备的处理。它通常在/dev下面存在一个对应的逻辑设备节点,这个节点以文件的形式存在。Linux系统的设备文件分为三类:块设备文件、字符设备文件和网络设备文件。块设备文件通常指一些需要以块(如512字节)的方式写入的设备,如IDE硬盘、SCSI硬盘、光驱等。字符型设备文件通常指可以直接读写,没有缓冲区的设备,如并口、虚拟控制台等。网络设备文件通常是指网络设备访问的BSDsocket接口,如网卡等。3.设备号设备号是一个数字,它是设备的标志。一个设备文件(也就是设备节点)可以通过mknod命令来创建,其中指定了主设备号和次设备号。主设备号表明某一类设备,一般对应着确定的驱动程序;次设备号一般是用于区分标明不同属性,例如不同的使用方法,不同的位置,不同的操作等,它标志着某个具体的物理设备。高字节为主设备号和底字节为次设备号。例如,在系统中的块设备IDE硬盘的主设备号是3,而多个IDE硬盘及其各个分区分别赋予次设备号1、2、3……4.驱动层次结构Linux下的设备驱动程序是内核的一部分,运行在内核模式,也就是说设备驱动程序为内核提供了一个I/O接口,用户使用这个接口实现对设备的操作。下图显示了典型的Linux输入/输出系统中各层次结构和功能。4.驱动层次结构Linux设备驱动程序包含中断处理程序和设备服务子程序两部分。设备服务子程序包含了所有与设备操作相关的处理代码。它从面向用户进程的设备文件系统中接受用户命令,并对设备控制器执行操作。这样,设备驱动程序屏蔽了设备的特殊性,使用户可以像对待文件一样操作设备。设备控制器需要获得系统服务时有两种方式:查询和中断。因为Linux下的设备驱动程序是内核的一部分,在设备查询期间系统不能运行其他代码,查询方式的工作效率较低,所以只有少数设备如软盘驱动程序采取这种方式,大多设备以中断方式向设备驱动程序发出输入/输出请求。5.设备驱动程序与外界的接口每种类型的驱动程序,不管是字符设备还是块设备都为内核提供相同的调用接口,因此内核能以相同的方式处理不同的设备。Linux为每种不同类型的设备驱动程序维护相应的数据结构,以便定义统一的接口并实现驱动程序的可装载性和动态性。Linux设备驱动程序与外界的接口可以分为如下三个部分。驱动程序与操作系统内核的接口:这是通过数据结构file_operations来完成的。驱动程序与系统引导的接口:这部分利用驱动程序对设备进行初始化。驱动程序与设备的接口:这部分描述驱动程序如何与设备交互,与具体设备密切相关。它们之间的相互关系如下图所示。6.设备驱动程序的特点(1)内核代码:设备驱动程序是内核的一部分,如果驱动程序出错,则可能导致系统崩溃。(2)内核接口:设备驱动程序必须为内核或者其子系统提供一个标准接口。比如,一个终端驱动程序必须为内核提供一个文件I/O接口;一个SCSI设备驱动程序应该为SCSI子系统提供一个SCSI设备接口,同时SCSI子系统也必须为内核提供文件的I/O接口及缓冲区。(3)内核机制和服务:设备驱动程序使用一些标准的内核服务,如内存分配等。(4)可装载:大多数的Linux操作系统设备驱动程序都可以在需要时装载进内核,在不需要时从内核中卸载。(5)可设置:Linux操作系统设备驱动程序可以集成为内核的一部分,并可以根据需要把其中的某一部分集成到内核中,这只需要在系统编译时进行相应的设置即可。(6)动态性:在系统启动且各个设备驱动程序初始化后,驱动程序将维护其控制的设备。如果该设备驱动程序控制的设备不存在也不影响系统的运行,那么此时的设备驱动程序只是多占用了一点系统内存罢了。二.字符设备驱动编写1.字符设备驱动编写流程说明 2.重要数据结构 3.设备驱动程序主要组成 4.proc文件系统1.字符设备驱动编写流程说明在上一节中已经提到,设备驱动程序可以使用模块的方式动态加载到内核中去。加载模块的方式与以往的应用程序开发有很大的不同。以往在开发应用程序时都有一个main函数作为程序的入口点,而在驱动开发时却没有main函数,模块在调用insmod命令时被加载,此时的入口点是init_module函数,通常在该函数中完成设备的注册。同样,模块在调用rmmod函数时被卸载,此时的入口点是cleanup_module函数,在该函数中完成设备的卸载。在设备完成注册加载之后,用户的应用程序就可以对该设备进行一定的操作,如read、write等,而驱动程序就是用于实现这些操作,在用户应用程序调用相应入口函数时执行相关的操作,init_module入口点函数则不需要完成其他如read、write之类功能。1.字符设备驱动编写流程说明上述函数之间的关系如图所示2.重要数据结构用户应用程序调用设备的一些功能是在设备驱动程序中定义的,也就是设备驱动程序的入口点,它是一个在<linux/fs.h>中定义的structfile结构,这是一个内核结构,不会出现在用户空间的程序中,它定义了常见文件I/O函数的入口。每个设备的驱动程序不一定要实现其中所有的函数操作,若不需要定义实现时,则只需将其设为NULL即可。其中,structinode提供了关于设备文件/dev/driver(假设此设备名为driver)的信息。structfile提供关于被打开的文件信息,主要用于与文件系统对应的设备驱动程序使用。structfile较为重要3.设备驱动程序主要组成(1)设备注册设备注册使用函数register_chrdev,调用该函数后就可以向系统申请主设备号,如果register_chrdev操作成功,设备名就会出现在/proc/devices文件里。register_chrdev函数格式如表所示。所需头文件#include<linux/fs.h>函数原型intregister_chrdev(unsignedintmajor,constchar*name,structfile_operations*fops)函数传入值major设备驱动程序向系统申请的主设备号,如果为0则系统为此驱动程序动态地分配一个主设备号name设备名fops对各个调用的入口点函数返回值成功:如果是动态分配主设备号,此返回所分配的主设备号。且设备名就会出现在函数返回值/proc/devices文件里出错:-13.设备驱动程序主要组成打开设备的接口函数是open,根据设备的不同,open函数完成的功能也有所不同,但通常情况下在open函数中要完成如下工作。递增计数器。检查特定设备的特殊情况。初始化设备。识别次设备号。其中递增计数器是用于设备计数的。由于设备在使用时通常会打开较多次数,也可以由不同的进程所使用,所以若有一进程想要关闭该设备,则必须保证其他设备没有使用该设备。因此使用计数器就可以很好地完成这项功能。这里,实现计数器操作的是用在<linux/module.h>中定义的3个宏如下。MOD_INC_USE_COUNT:计数器加一。MOD_DEC_USE_COUNT:计数器减一。MOD_IN_USE:计数器非零时返回真。另外,当有多个物理设备时,就需要识别次设备号来对各个不同的设备进行不同的操作,在有些驱动程序中并不需要用到。注意:虽然这是对设备文件执行的第一个操作,但却不是驱动程序一定要声明的操作。若这个函数的入口为NULL,那么设备的打开操作将永远成功,但系统不会通知驱动程序。3.设备驱动程序主要组成(4)释放设备释放设备的接口函数是release。要注意释放设备和关闭设备是完全不同的。当一个进程释放设备时,其他进程还能继续使用该设备,只是该进程暂时停止对该设备的使用;而当一个进程关闭设备时,其他进程必须重新打开此设备才能使用。释放设备时要完成的工作如下。递减计数器MOD_DEC_USE_COUNT。在最后一次释放设备操作时关闭设备。3.设备驱动程序主要组成(5)读写设备读写设备的主要任务就是把内核空间的数据复制到用户空间,或者从用户空间复制到内核空间,也就是将内核空间缓冲区里的数据复制到用户空间的缓冲区中或者相反。虽然这个过程看起来很简单,但是内核空间地址和应用空间地址是有很大区别的,其中之一就是用户空间的内存是可以被换出的,因此可能会出现页面失效等情况。所以就不能使用诸如memcpy之类的函数来完成这样的操作。在这里就要使用copy_to_user或copy_from_user函数,它们就是用来实现用户空间和内核空间的数据交换的。要注意,这两个函数不仅实现了用户空间和内核空间的数据转换,而且还会检查用户空间指针的有效性。如果指针无效,那么就不进行复制3.设备驱动程序主要组成(6)获取内存在应用程序中获取内存通常使用函数malloc,但在设备驱动程序中动态开辟内存可以有基于内存地址和基于页面为单位两类。其中,基于内存地址的函数有kmalloc,注意的是,kmalloc函数返回的是物理地址,而malloc等返回的是线性地址,因此在驱动程序中不能使用malloc函数。与malloc()不同,kmalloc()申请空间有大小限制。长度是2的整次方,并且不会对所获取的内存空间清零。基于页为单位的内存函数族有:get_zeroed_page:获得一个已清零页面。get_free_page:获得一个或几个连续页面。get_dma_pages:获得用于DMA传输的页面。与之相对应的释放内存用也有kfree或free_pages族。3.设备驱动程序主要组成(7)打印信息就如同在编写用户空间的应用程序,打印信息有时是很好的调试手段,也是在代码中很常用的组成部分。但是与用户空间不同,在内核空间要用函数printk而不能用平常的函数printf。printk和printf很类似,都可以按照一定的格式打印消息,printk还可以定义打印消息的优先级。这些不同优先级的信息可以输出到控制台上、/var/log/messages里。其中,对输出给控制台的信息有一个特定的优先级console_loglevel。若优先级小于这个整数值时,则消息才能显示到控制台上,否则,消息会显示在/var/log/messages里。若不加任何优先级选项,则消息默认输出到/var/log/messages文件中。注意:要开启klogd和syslogd服务,消息才能正常输出。4.proc文件系统/proc文件系统是一个伪文件系统,它是一种内核和内核模块用来向进程发送信息的机制。这个伪文件系统让用户可以和内核内部数据结构进行交互,获取有关进程的有用信息,在运行时通过改变内核参数改变设置。与其他文件系统不同,/proc存在于内存之中而不是硬盘上。读者可以通过“ls”查看/proc文件系统的内容。三.块设备驱动编写1.块设备驱动程序描述符2.块设备驱动编写流程1.块设备驱动程序描述符块设备文件通常指一些需要以块(如512字节)的方式写入的设备,如IDE硬盘、SCSI硬盘、光驱等。它的驱动程序的编写过程与字符型设备驱动程序的编写有很大的区别。块设备驱动程序描述符是一个包含在<linux/blkdev.h>中的blk_dev_struct类型的数据结构,其定义如下所示:structblk_dev_struct{request_queue_trequest_queue;queue_proc*queue;void*date;};1.块设备驱动程序描述符在这个结构中,请求队列request_queue是主体,包含了初始化之后的I/O请求队列。对于函数指针queue,当其为非0时,就调用这个函数来找到具体设备的请求队列,这是为考虑具有同一主设备号的多种同类设备而设的一个域,该指针也在初始化时就设置好。指针data是辅助queue函数找到特定设备的请求队列,保存一些私有的数据。所有块设备的描述符都存放在blk_dev表structblk_dev_structblk_dev[MAX_BLKDEV]中;每个块设备都对应着数组中的一项,可以使用主设备号进行检索。每当用户进程对一个块设备发出一个读写请求时,首先调用块设备所公用的函数generic_file_read()和generic_file_write()。如果数据存在在缓冲区中或缓冲区还可以存放数据,那么就同缓冲区进行数据交换。否则,系统会将相应的请求队列结构添加到其对应项的blk_dev_struct中,如下图所示。1.块设备驱动程序描述符2.块设备驱动编写流程流程说明块设备驱动程序的编写流程同字符设备驱动程序的编写流程很类似,也包括了注册和使用两部分。但与字符驱动设备所不同的是,块设备驱动程序包括一个request请求队列。它是当内核安排一次数据传输时在列表中的一个请求队列,用以最大化系统性能为原则进行排序。在后面的读写操作时会详细讲解这个函数,下图给出了块设备驱动程序的流程图,请注意与字符设备驱动程序的区别。四.中断编程四.中断编程前面所讲述的驱动程序中都没有涉及到中断处理,而实际上,有很多Linux的驱动都是通过中断的方式来进行内核和硬件的交互。这是驱动程序申请中断和释放中断的调用。在include/linux/sched.h里声明。request_irq()调用的定义:intrequest_irq(unsignedintirq,void(*handler)(intirq,void*dev_id,structpt_regs*regs),unsignedlongirqflags,constchar*devname,oid*dev_id);irq是要申请的硬件中断号。在Intel平台,范围是0~15。handler是向系统登记的中断处理函数。这是一个回调函数,中断发生时,系统调用这个函数,传入的参数包括硬件中断号,deviceid,寄存器值。dev_id就是下面的request_irq时传递给系统的参数dev_id。irqflags是中断处理的一些属性。比较重要的有SA_INTERRUPT,标明中断处理程序是快速处理程序(设置SA_INTERRUPT)还是慢速处理程序(不设置SA_INTERRUPT)。快速处理程序被调用时屏蔽所有中断。慢速处理程序不屏蔽。还有一个SA_SHIRQ属性,设置了以后运行多个设备共享中断。dev_id在中断共享时会用到。一般设置为这个设备的device结构本身或者NULL。中断处理程序可以用dev_id找到相应的控制这个中断的设备,或者用irq2dev_map找到中断对应的设备。voidfree_irq(unsignedintirq,void*dev_id);五.键盘驱动实现1.键盘工作原理 2.键盘驱动综述 3.键盘驱动流程1.键盘工作原理(1)原理简介在键盘产生按键动作之后,键盘上的扫描芯片(一般为8048)获得键盘的扫描码,并将其发送到主机端。在主机端的处理过程为是端口读取扫描码之后,对键盘模式作一个判断,如果是RAW模式,则直接将键盘扫描码发送给应用程序;如果是其他模式,则就将扫描码转化成为键盘码,然后再判断模式以决定是否将键盘码直接发送给应用程序;如果是XLATE或Unicod。模式,则将键盘码再次转化成为符号码,然后根据对符号码解析,获得相应的处理函数,并将其送到TY设备的缓存中。键盘模式有4种,这4种模式的对应关系如图所示。Scancodemode(RAW)模式:将键盘端口上读出的扫描码放入缓冲区,通过参数s设置。Keycodemode(MEDIUMRAW)模式:将扫描码过滤为键盘码放入缓冲区,通过参数k设置。ASCIImode(XLATE)模式:识别各种键盘码的组合,转换为TTY终端代码放入缓冲区,通过参数a可以设置。UTF-8mode(Unicode)模式:Unicode模式基本上与XLATE相同,只不过可以通过数字小键盘I旬接枪入Unicode代码,通过参数u可以设置。1.键盘工作原理(2)扫描码一个基本按键的扫描码由3个字节组成:1个字节的接通扫描码和2个字节的断开扫描码。其中第1和第2个字节相同,中间字节是断开标志F0H。例如B键的接通扫描码是32H,断开扫描码是F0H32H,B键被按下时,32H被发送出去,如果移植按住不放,则键盘将以按键重复率不停地发送32H,直到该键释放,才发出断开扫描码F0H32H。扫描码与按键的位置有关,与该键的ASCII码并无对应关系。键盘上还有部分扩展键(功能键和控制键等),这些键的扫描码由5个字节组成,与基本键的扫描码相比,接通扫描码与断开扫描码前各多了一个固定值字节E0H。例如Home键的接通扫描码是E0H70H,断开扫描码是E0HF0H70H。还有两个特殊键,PrintScreen键的接通扫描码是E0H12HE0H7CH;断开扫描码是E0HF0H7CHE0HF0H77H,无断开扫描码。1.键盘工作原理(3)键盘码由前面的分析可见,单单一个键的按下与断开,键盘最多要产生一系列多达6个字节的扫描码序列,而内核必须解析扫描码序列从而定位某个键被按下与释放的事件。为达到这个目的,每一个键被分配一个键盘码k(k的范围1-127)如果按键按下产生的键盘码为k,则释放该键产生的键盘码为k+128。按照键盘码的分配规则,对于产生单个扫描码范围0x0l~0x58的键,其键盘码与扫描码相同。而对于0x59~0x71范围的键,可以查表获得其扫描码与键盘码对应。1.键盘工作原理(4)符号码符号码(keysym)最终是用来标志一个按键事件的惟一值;根据上面的分析,它由键盘码经过Keymap表映射而来。它包括2个字节,高8位表示type,根据type的不同,我们最终选择不同的处理函数来处理不同类型的事件,type相同的事件由同一个函数来处理。Type包括一般键、方向键、字母键、函数键等。在<linux/keyboard.h>中可以找到13种键类型的宏定义。1.键盘工作原理(5)Keymap表在使用键盘时常常使用组合键,而组合键的意义通常是系统另外赋予的,所以从键盘码向TY输入符的转换需要借助Keymap表来索引。intshift_final=shiftstate^kbd-Aockstate;ushort'keymap=keymaps[shiftfinall];keysym=key_map[keycodel];由于共计有8个修饰符(modifier),即Shift,AitGr,Control,Alt,ShiftL,ShiftR,CtrIL和CtrlR,因此共有256张可能的Keymap,而在实际使用时,内核缺省只分配7张Keymap:plain,Shift,AltR,Ctrl,Ctrl+Shift,AItL和Ctrl+AitL。1.键盘工作原理Keymap表是一张二维表,结构图如图所示。通过这张Keymap表,就能完成键盘码到符号码的转化,获得相应的符号码。Ctrl...shift......键盘码排序:符号码Keysym2.键盘驱动综述Linux中的大多数驱动程序都采用了层次型的体系结构,键盘驱动程序也不例外。在Linux中,键盘驱动被划分成两层来实现。其中,上层是一个通用的键盘抽象层,完成键盘驱动中不依赖于底层具

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论