通信原理大综合课件高频全_第1页
通信原理大综合课件高频全_第2页
通信原理大综合课件高频全_第3页
通信原理大综合课件高频全_第4页
通信原理大综合课件高频全_第5页
已阅读5页,还剩39页未读 继续免费阅读

下载本文档

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

文档简介

第8章设备驱动程序开发基础一、Linux驱动程序概念

设备驱动程序是操作系统内核与机器硬件之间的接口。驱动程序为应用程序屏蔽了硬件的细节,在应用程序看来,硬件设备只是一个设备文件,应用程序可以像操作普通文件一样对硬件设备进行操作。8.1设备驱动概述每个设备都对应一个设备文件,设备文件放在/dev目录下。Linux对设备文件的访问就是对硬件设备的操作。设备文件会调用驱动程序对硬件进行操作。设备文件在用户层看来就和普通文件没有区别,也拥有读、写和执行权限,拥有和它对应的索引节点等。文件系统层处理请求的权限,通过设备驱动层的接口将任务传到驱动程序。设备驱动程序一般应具有以下功能:对硬件设备的初始化和释放;对设备进行管理,提供对设备操作的统一接口;在设备和应用程序之间传送数据;检测或处理设备出现的错误。

嵌入式系统有许多设备用于与用户交互。触摸屏、小键盘、滚动轮、传感器、RS232接口、LCD等还有闪存、USB、GSM等专用设备。二、应用程序对设备的操作过程如下:通过系统调用open()打开具体的设备,建立以设备的连接。通过对文件的系统调用read()、write()、ioctl()等对设备进行操作。但是用户进程一般位于内核之外,工作在用户态,驱动程序作为系统内核的一部分,工作在内核态,不能通过指针把用户空间的数据地址传递给内核,要经过Linux提供的函数进行转换。

三、设备的分类字符设备以字节为单位逐个进行I/O操作字符设备中的缓存是可有可无不支持随机访问如串口设备/dev/cua0和/dev/cua1块设备块设备的存取是通过buffer、cache来进行可以进行随机访问例如IDE硬盘设备/dev/hda网络设备是通过BSDsocket接口访问的设备如网卡等.对这三种设备文件编写驱动程序时会有一定的区别。四、设备号设备号是一个数字,它是设备的标志。主设备号表明某一类设备,一般对应着确定的驱动程序;次设备号一般是用于区分标明不同属性,例如不同的使用方法,不同的位置,不同的操作等,它标志着某个具体的物理设备。高字节为主设备号和低字节为次设备号。例如,在系统中的块设备IDE硬盘的主设备号是3,而多个IDE硬盘及其各个分区分别赋予次设备号1、2、3……crw-rw-rw-1rootroot1,3Apr112002nullcrw-------1rootroot10,1Apr112002psauxcrw-------1rootroot4,1Oct2803:04tty1crw-rw-rw-1roottty4,64Apr112002ttys0crw-rw----1rootuucp4,65Apr112002ttyS1crw--w----1vcsatty7,1Apr112002vcs1crw--w----1vcsatty7,129Apr112002vcsa1crw-rw-rw-1rootroot1,5Apr112002zero五、通过mknod命令来创建设备文件mknod设备名设备类型主设备好此设备号如mknodttys0 c641串口设备,设备类型为字符型,主设备号64,此设备号1。六、设备驱动程序开发步骤熟悉硬件设备,重点是设备中的各种寄存器。定义设备号,应用程序利用设备号访问驱动程序。熟悉设备初始化函数,在驱动程序中实现设备的注册和中断注册等。设计所需要的文件操作调用,定义file_operations结构。实现所需的文件操作调用,如read、write、llseek等函数。实现中断服务程序。测试程序。发布驱动程序。8.2设备驱动程序框架一、虚拟文件系统与驱动程序间的接口structfile_operations结构在/include/linux/fs.h中定义。

这个结构的每一个成员的名字都对应着一个系统调用。用户进程利用系统调用在对设备文件进行诸如read/write操作时,系统调用通过设备文件的主设备号找到相应的设备驱动程序,然后读取这个数据结构相应的函数指针,接着把控制权交给该函数。这是linux的设备驱动程序工作的基本原理。既然是这样,则编写设备驱动程序的主要工作就是编写子函数,并填充file_operations的各个域。我们在向内核注册设备的函数中,一个参数就是指向strcutfile_operations的指针。structfile_operations{

int(*seek)(structinode*,structfile*,off_t,int);

int(*read)(structinode*,structfile*,char,int);

int(*write)(structinode*,structfile*,off_t,int);

int(*readdir)(structinode*,structfile*,structdirent*,int);

int(*select)(structinode*,structfile*,int,select_table*);

int(*ioctl)(structinode*,structfile*,unsinedint,unsignedlong);

int(*mmap)(structinode*,structfile*,structvm_area_struct*);

int(*open)(structinode*,structfile*);

int(*release)(structinode*,structfile*);

int(*fsync)(structinode*,structfile*);

int(*fasync)(structinode*,structfile*,int);

int(*check_media_change)(structinode*,structfile*);

int(*revalidate)(dev_tdev);

}

1、打开设备函数open函数完成以下工作检查设备特定的错误(例如设备没准备好,或者类似的硬件错误)如果它第一次打开,初始化设备,包括设备注册、对设备模式、设备参数进行设置,即配置各种寄存器。为设备实现申请中断、设置DMA方式、申请对设备的控制权。识别次设备号,填充file->private_data结构。使用计数增1。另外,当有多个物理设备时,就需要识别次设备号来对各个不同的设备进行不同的操作,在有些驱动程序中并不需要用到。int(*open)(structinode*,structfile*);

这里,实现计数器操作的是用在<linux/module.h>中定义的3个宏如下。·MOD_INC_USE_COUNT:计数器加一。·MOD_DEC_USE_COUNT:计数器减一。·MOD_IN_USE:计数器非零时返回真。2、关闭设备函数函数原型int(*release)(structinode*,structfile*);

关闭设备函数完成以下任务:释放打开设备时分配的资源如中断等;释放open在filp->private_data中分配的内存;计数器减1;在最后的关闭设备3、读设备数据函数#include<linux/fs.h>ssize_t(*read)(structfile*filp,char*buff,size_tcount,loff_t*f_pos)filp:文件指针buff:指向用户缓冲区count:传入的数据长度*f_pos:指向file->f_pos,给出当前读写的位置。返回值为实际所读字节数,返回-1表示出错。返回0表示文件结束4、写设备数据函数#include<linux/fs.h>ssize_t(*write)(structfile*filp,constchar*buff,size_tcount,loff_t*offp)write()函数把count个字节从buf指向的缓冲区写入与handle相连的文件中,返回值为实际写入的字节数。虽然这个过程看起来很简单,但是内核空间地址和应用空间地址是有很大区别的,其中之一就是用户空间的内存是可以被换出的,因此可能会出现页面失效等情况。所以就不能使用诸如memcpy之类的函数来完成这样的操作。在这里就要使用copy_to_user或copy_from_user函数,它们就是用来实现用户空间和内核空间的数据交换的。copy_to_user和copy_from_user的格式如表#include<asm/uaccess.h>Unsignedlongcopy_to_user(void*to,constvoid*from,unsignedlongcount)Unsignedlongcopy_from_user(void*to,constvoid*from,unsignedlongcount)To:数据目的缓冲区函数传入值From:数据源缓冲区count:数据长度函数返回值成功:写入的数据长度失败:-EFAULT5、设备控制函数ioctl入口点执行读、写之外的操作。函数原型为:intioctl(intfd,intcmd,…)参数cmd不经修改的传递给驱动程序,可选的arg参数无论是指针还是整数值,都以unsignedlong的形式传递给驱动程序。在实现ioctl的时候,大多数都包含switch语句,用来根据不同的cmd值来执行不同的硬件操作。二、设备驱动程序的关键数据结构1、file结构Structfile提供关于被打开的文件信息,主要用于与文件系统对应的设备驱动程序使用。系统中每个打开的文件有一个关联的structfile在内核空间).它由内核在open时创建,并传递给在文件上操作的任何函数,直到最后的关闭.在文件的所有实例都关闭后,内核释放这个数据结构.mode_tf_mode;文件模式确定文件是可读的或者是可写的(或者都是),通过位FMODE_READ和FMODE_WRITE.你可能想在你的open或者ioctl函数中检查这个成员的读写许可,但是你不需要检查读写许可,因为内核在调用你的方法之前检查.loff_tf_pos;当前读写位置.loff_t在所有平台都是64位,如果驱动设备需要知道文件当前的位置,可以读取这个字段得值。unsignedintf_flags;f_flags表示文件标志,如O_RDONLY、O_WDONLY、等,为了支持非阻塞型操作,驱动程序需要检查这个标志。structfile_operations*f_op;f_op表示文件的对应操作,指向驱动中的file_operations,内核在执行open时为这个指针赋值,可以调用这个指针指向file_operations中的相应成员。void*private_data;系统调用的open设置这个指针为空,驱动可以江这个字段用于任何目的或者忽略它;驱动程序可以使用这个成员来指向分配的数据,但是必须记住在内核销毁文件结构之前,在release方法中释放那个内存.2、inode结构

文件系统处理的文件所需信息在inode数据结构中。但这个结构只有3个成员对于编写驱动代码有用:dev_ti_rdev;这个字段包含了实际设备的编号,如果需要可用一下函数获得。unsignedintiminor(structinode*inode)//获得从设备号unsignedintimajor(structinode*inode)//获得主设备号2、stuctcdev*i_nodestuctcdev是表示字符设备的内部结构,当inode指向一个字符文件时,该字段包含指向stuctcdev结构的指针。3、stuctblok_devce*i_nodestuctblok_devce是表示块设备的内部结构,当inode指向一个字符文件时,该字段包含指向stuctblok_devce结构的指针。3、设备数据结构分为字符设备、块设备、网络设备三种设备数据结构一字符设备数据结构为例:Sturctcdev结构在/include/linux/cdev.h中定义在设备初始化时,设备驱动程序会构造一个sturctcdev结构作为字符设备向内核注册。8.3设备驱动程序结构Linux的设备驱动程序可以分成三个主要部分:自动配置和初始化子程序。(检测硬件设备是否正常;初始化,仅初始化时调用一次。)服务于I/O请求的子程序。(调用这部分程序是由于系统调用的结果)中断服务子程序。(由Linux系统接收中断,再由系统调用中断服务子程序。)Linux对中断的处理

从应用编程角度来看,编写一个中断处理程序只要根据具体应用实现中断服务子程序,并利用一系列LinuxAPI函数向内核注册该服务子程序就行了,具体的调度处理在linux内部实现。1、注册中断处理程序

向内核注册中断处理程序主要实现两个功能,一是注册中断号,二是注册中断处理函数。在linux中对应的中断处理注册函数为:intrequest_irq(unsignedintirq,void(*handler)(int,void*,structpt_regs*),unsignedlongflags,constchar*device,void*dev_id);返回值:request_irq返回0表示成功,返回-INVAL表示irq>15或handler==NULL,返回-EBUSY,表示中断已经被占用且不能共享。函数注册中断服务函数,其中:irq:中断号handler:中断响应处理函数irq_flags:中断管理选项掩码devname:设备名称dev_id:中断共享时使用

在一个设备驱动程序向内核注册了中断服务程序后,中断到来时的调度就由内核的中断处理子系统来完成了。中断处理子系统的一个主要任务是根据中断号找到正确的中断处理代码段。

设备驱动的初始化

设备驱动的初始化函数主要完成的功能是:a对驱动程序管理的硬件进行必要的初始化。如对硬件寄存器进行设置,设置串口的工作方式等。b初始化设备驱动相关的参数。一般情况下,定义一个设备变量,用以保存设备的相关参数。这里对设备变量的各项进行初始化。c在内核中注册设备。Intregister_chrdev(unsignedint,constchar*,structfile_operation*)3个参数为主设备号、设备名称、file_operation结构地址。d如果设备需要IRQ支持,则要注册中断。初始化程序声明。当驱动程序是内核的一部分,则要按如下方式声明,注意不能缺少__initint__initchr_driver_init(void);则在系统启动的时候会由内核调用chr_driver_init,完成的驱动程序的初始化。当驱动程序是以模块的形式编写分,则要按如下方式声明,当运行insmod命令插入模块时会调用init_module函数完成初始化工作。

intinit_module(void)e其他初始化工作8.4设备驱动程序开发过程定义主设备号、次设备号,也可以动态获取。实现驱动初始化和清除函数。如果驱动程序采用模块方式,则要实现模块初始化和清除函数。设计要实现的文件操作,定义file_operations结构。实现所需的文件操作调用,如read、write等。实现中断服务函数,并用request_irq向内核注册。将驱动编译到内核或编译成模块,用ismod命令加载。生成设备节点文件。一、设备驱动程序开发流程二、模块化驱动程序设计

在linux系统中提供了一种全新的模块化机制,可以在不重新编译内核的情况下,将编译好的模块动态的插入运行中的内核,或者将内核中已经存在的一个模块移走。可以看出这种机制为驱动程序开发调试提供了很大的方便。

对应的模块化编程,源程序中必须至少提供

init_module()和cleanup_module()两个函数。下面就开始写子程序。

#include<linux/types.h>基本的类型定义

#include<linux/fs.h>文件系统使用相关的头文件

#include<linux/mm.h>

#include<linux/errno.h>

#include<asm/segment.h>

unsignedinttest_major=0;

staticintread_test(structinode*inode,structfile*file,char*buf,intcount)

{

intleft;用户空间和内核空间

if(verify_area(VERIFY_WRITE,buf,count)==-EFAULT)

return-EFAULT;

for(left=count;left>0;left--)

{

__put_user(1,buf,1);

buf++;

}

returncount;

}这个函数是为read调用准备的。当调用read时,read_test()被调用,它把用户的缓冲区全部写1。buf是read调用的一个参数。它是用户进程空间的一个地址。但是在read_test被调用时,系统进入核心态。所以不能使用buf这个地址,必须用__put_user(),这是kernel提供的一个函数,用于向用户传送数据。另外还有很多类似功能的函数。请参考,在向用户空间拷贝数据之前,必须验证buf是否可用。这就用到函数verify_area。为了验证BUF是否可以用。staticintwrite_test(structinode*inode,structfile*file,constchar*buf,intcount)

{

returncount;

}

staticintopen_test(structinode*inode,structfile*file)

{

MOD_INC_USE_COUNT;模块计数加以,表示当前内核有个设备加载内核当中去

return0;

}

staticvoidrelease_test(structinode*inode,structfile*file)

{

MOD_DEC_USE_COUNT;

}这几个函数都是空操作。实际调用发生时什么也不做,他们仅仅为下面的结构提供函数指针。

structfile_operationstest_fops={?

read_test,

write_test,

open_test,

release_test,

};

设备驱动程序的主体可以说是写好了。现在要把驱动程序嵌入内核。驱动程序可以按照两种方式编译。一种是编译进kernel,另一种是编译成模块(modules),如果编译进内核的话,会增加内核的大小,还要改动内核的源文件,而且不能动态的卸载,不利于调试,所以推荐使用模块方式。intinit_module(void)

{

intresult;

result=register_chrdev(0,"test",&test_fops);对设备操作的整个接口

if(result<0){

温馨提示

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

评论

0/150

提交评论