您好,欢迎访问三七文档
当前位置:首页 > 商业/管理/HR > 管理学资料 > 实验四-linux驱动程序的编写
1/7中国石油大学(北京)计算机科学与技术系实验报告实验课程:软件系统设计与开发实践实验名称:linux驱动程序的编写学号:2017215538姓名:于宁班级:信息研17-4班完成日期:2018年4月16日一、实验目的1.掌握linux驱动程序的编写方法;2.掌握驱动程序动态模块的调试方法;3.掌握驱动程序填加到内核的方法。二、实验内容1.学习linux驱动程序的编写流程;2.学习驱动程序动态模块的调试方法;3.学习驱动程序填加到内核的流程。三、实验设备1.PentiumII以上的PC机,LINUX操作系统,EL-ARM830实验箱。四、linux的驱动程序的编写嵌入式应用对成本和实时性比较敏感,而对linux的应用主要体现在对硬件的驱动程序的编写和上层应用程序的开发上。嵌入式linux驱动程序的基本结构和标准Linux的结构基本一致,也支持模块化模式,所以,大部分驱动程序编成模块化形式,而且,要求可以在不同的体系结构上安装。linux是评定成绩指导教师2/7可以支持模块化模式的,但由于嵌入式应用是针对具体的应用,所以,一般不采用该模式,而是把驱动程序直接编译进内核之中。但是这种模式是调试驱动模块的极佳方法。设备驱动程序是操作系统内核和机器硬件之间的接口。设备驱动程序为应用程序屏蔽了硬件的细节,这样在应用程序看来,硬件设备只是一个设备文件,应用程序可以像操作普通文件一样对硬件设备进行操作。同时,设备驱动程序是内核的一部分,它完成以下的功能:对设备初始化和释放;把数据从内核传送到硬件和从硬件读取数据;读取应用程序传送给设备文件的数据和回送应用程序请求的数据;检测和处理设备出现的错误。在linux操作系统下有字符设备和块设备两类主要的设备文件类型。字符设备和块设备的主要区别是:在对字符设备发出读写请求时,实际的硬件I/O一般就紧接着发生了;块设备利用一块系统内存作为缓冲区,当用户进程对设备请求满足用户要求时,就返回请求的数据。块设备是主要针对磁盘等慢速设备设计的,以免耗费过多的CPU时间来等待。4.1设备驱动程序的file_operations结构通常,一个设备驱动程序包括两个基本的任务:驱动设备的某些函数作为系统调用执行;而某些函数则负责处理中断(即中断处理函数)。而file_operations结构的每一个成员的名称都对应着一个系统调用。用户程序利用系统调用,比如在对一个设备文件进行诸如read操作时,这时对应于该设备文件的驱动程序就会执行相关的ssize_t(*read)(structfile*,char*,size_t,loff_t*);函数。在操作系统内部,外部设备的存取是通过一组固定入口点进行的,这些入口点由每个外设的驱动程序提供,由file_operations结构向系统进行说明,因此,编写设备驱动程序的主要工作就是编写子函数,并填充file_operations的各个域。file_operations结构在kernel/include/linux/fs.h中可以找到。structfile_operations{structmodule*owner;loff_t(*llseek)(structfile*,loff_t,int);ssize_t(*read)(structfile*,char*,size_t,loff_t*);ssize_t(*write)(structfile*,constchar*,size_t,loff_t*);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*);3/7int(*release)(structinode*,structfile*);int(*fsync)(structfile*,structdentry*,intdatasync);int(*fasync)(int,structfile*,int);int(*lock)(structfile*,int,structfile_lock*);ssize_t(*readv)(structfile*,conststructiovec*,unsignedlong,loff_t*);ssize_t(*writev)(structfile*,conststructiovec*,unsignedlong,loff_t*);ssize_t(*sendpage)(structfile*,structpage*,int,size_t,loff_t*,int);unsignedlong(*get_unmapped_area)(structfile*,unsignedlong,unsignedlong,unsignedlong,unsignedlong);#ifdefMAGIC_ROM_PTRint(*romptr)(structfile*,structvm_area_struct*);#endif/*MAGIC_ROM_PTR*/};其中主要的函数说明如下:1.open是驱动程序用来完成设备初始化操作的,open还会增加设备计数,以防止文件在关闭前模块被卸载出内核。open主要完成以下操作:检查设备错误(诸如设备未就绪或相似的硬件问题);如果是首次打开,初始化设备;标别次设备号;分配和填写要放在file→private_data内的数据结构;增加使用计数。2.read用来从外部设备中读取数据。当其为NULL指针时,将引起read系统调用返回-EINVAL(“非法参数”)。函数返回一个非负值表示成功地读取了多少字节。3.write向外部设备发送数据。如果没有这个函数,write系统调用向调用程序返回一个-EINVAL。如果返回值非负,就表示成功地写入的字节数。4.release是当设备被关闭时调用这个操作。release的作用正好与open相反。这个设备方法有时也称为close。它应该完成以下操作:使用计数减1;释放open分配在file→private_data中的内存,在最后一次关闭操作时关闭设备。5.llseek是改变当前的读写指针。6.readdir一般用于文件系统的操作。7.poll一般用于查询设备是否可读可写或处于特殊的状态。8.ioctl执行设备专有的命令。4/79.mmap将设备内存映射到应用程序的进程地址空间。4.2设备驱动程序编写的具体内容通过了解驱动程序的file_operations结构,用户就可以编写出相关外部设备的驱动程序。首先,用户在自己的驱动程序源文件中定义file_operations结构,并编写出设备需要的各操作函数,对于设备不需要的操作函数用NULL初始化,这些操作函数将被注册到内核,当应用程序对设备相应的设备文件进行文件操作时,内核会找到相应的操作函数,并进行调用。如果操作函数使用NULL,操作系统就进行默认的处理。定义并编写完file_operations结构的操作函数后,要定义一个初始化函数,比如函数名可device_init(),在linux初始化的时候要调用该函数,因此,该函数应包含以下几项工作:a.对该驱动所使用到的硬件寄存器进行初始化。包括中断寄存器。b.初始化设备相关的参数。一般来说每个设备要定义一个设备变量,用来保存设备相关的参数。c.注册设备。Linux内核通过主设备号将设备驱动程序同设备文件相连。每个设备有且仅有一个主设备号。通过查看linux系统中/proc下的devices文件,该文件记录已经使用的主设备号和设备名,选择一个没有使用的主设备号,调用下面的函数来注册设备。intregister_chrdev(unsignedint,constchar*,structfile_operations*),其中的三个参数代表主设备号,设备名,file_operations的结构地址。d.注册设备使用的中断。注册中断使用的函数。intrequest_irq(unsignedirq,void(*handler)(int,void*,structpt_regs*),unsignedlongflags,constchar*device,void*dev_id);其中,irq是中断向量。硬件系统将IRQn映射成中断向量。handler-----中断处理函数。flags-----中断处理中的一些选项的掩码。device-----设备的名称dev_id------在中断共享时使用的id。e.其他的一些初始化工作,比如给设备分配I/O,申请DMA通道等。当设备的驱动程序使用了如下的函数方式,则设备驱动可以动态的加载和卸载int__initdevice_init(void)void__exitdevice_exit(void)5/7module_init(device_init);module_exit(device_exit);当然,也可以编译进内核中。4.3将设备驱动加到linux内核中设备驱动程序写完后,就可以加到linux的内核中了,这需要修改linux的源码,然后重新编译linux内核。1.将设备驱动文件(比如device_driver.c)复制到kernel/drivers/char目录下,该目录保存了linux的字符型设备的设备驱动程序。该驱动程序中,使用int__intdevice_init(void)方式编写。2.在kernel/drivers/char目录下的Makefile文件中填加如下代码:ifeq($(CONFIG_DEVICE_DRIVER),y)L_OBJS+=DEVICE_DRIVER.oendif或obj-$(CONFIG_DEVICE_DRIVER)+=DEVICE_DRIVER.o如果在配置linux内核的时候,选择了支持我们定义的设备,则在编译内核的时候,会编译DEVICE_DRIVER.c,生成DEVICE_DRIVER.o文件。3.在kernel/drivers/char目录下修改config.in文件。在comment'Characterdevices'下面填加:bool‘supportforDEVICE_DRIVER’CONFIG_DEVICE_DRIVER这样在编译内核时,运行makemenuconfig时,在配置字符设备时就会出现supportforDEVICE_DRIVER的字样。当选中它时,编译通过,则驱动程序就加到内核中去了。在文件系统cramfs中加上设备驱动程序对应的设备文件。挂在操作系统中的设备都使用了设备驱动程序,要使一个设备成为应用程序可以访问的设备,必须在文件系统中有一个代表此设备的设备文件,通过使用设备文件,就可以对外部设备进行具体操作。设备文件都包含在/dev目录下,linux使用的根文件系统是cramfs文件系统。这个系统是一个只读压缩文件系统,要在制作cramfs文件系统之前,在root_tech目录结构中的/usr/etc/rc.local文件下,添加相应的设备文件。用mknod命令来创建一个设备文件:mknoddevice_driverc1200,device_driver为设备文件名,c指的是字符设备,120是主设备号,0为次设备号。device_driver这个名字与注册函数中使用的字符串要一致!6/74.4将设备驱动编译成驱动模块使用同一个驱动程序的源代码,当然一定要如下定义某些函数int__initdevice_init(void);void__exitdevice_exit(void);module_init(device_init
本文标题:实验四-linux驱动程序的编写
链接地址:https://www.777doc.com/doc-5870213 .html