版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1
7.1设备驱动编程基础7.2字符设备驱动编程7.3GPIO驱动程序实例7.4按键驱动编程实例第7章嵌入式Linux设备驱动编程2
7.1.1Linux设备驱动概述设备驱动概念操作系统是通过各种驱动程序来驾驭硬件设备的,它为用户屏蔽了各种各样的设备,驱动硬件是操作系统最基本的功能,并且提供统一的操作方式。设备驱动程序是操作系统最基本的组成部分之一,在Linux内核源程序中也占有60%以上。因此,熟悉驱动的编写是很重要的。Linux的一个重要特点就是将所有的设备都当做文件进行处理,这一类特殊文件就是设备文件(通常在/dev目录下),这样在应用程序看来,硬件设备只是一个设备文件,应用程序可以象操作普通文件一样对硬件设备进行操作,这样就大大方便了对设备的处理。7.1设备驱动编程基础
在ARM系统中,每个物理设备都有自己的控制器,每个硬件控制器都有各自的控制状态寄存器,这些寄存器用来启动、停止、初始化设备,以及对设备进行诊断,对硬件的控制主要是针对这些寄存器进行操作——设备驱动程序的主要功能就是完成对硬件寄存器的操作。
没有操作系统时用户程序对寄存器的操作:
#definerGPFCON(*(volatileunsigned*)0x56000050) rGPFCON=0x55aa;7.1设备驱动编程基础4
7.1.1Linux设备驱动概述Linux系统的设备分为三类:字符设备、块设备和网络设备.字符设备通常指像普通文件或字节流一样,以字节为单位顺序读写的设备,如并口设备、虚拟控制台等。块设备通常指一些需要以块为单位随机读写的设备,如IDE硬盘、SCSI硬盘、光驱等。
字符设备和块设备可以像文件一样被访问,例如,驱动程序都会实现open(打开)、close(关闭,或叫release)、read(读取)、write(写入)或seek(定位)等操作。7.1设备驱动编程基础5
7.1.1Linux设备驱动概述网络设备通常是指通过网络能够与其他主机进行数据通信的设备,如网卡等。Linux网络设备不是面向流的设备,因此不会将网络设备的名字(如eth0)映射到文件系统中。7.1设备驱动编程基础6
7.1.1Linux设备驱动概述设备驱动程序的特点内核代码:驱动程序是内核的一部分内核接口:为内核提供标准接口内核机制和服务:设备驱动程序使用标准的内核服务可装载:可加载/卸载可设置:可配置为内核的一部分动态性7.1设备驱动编程基础7
7.1.1Linux设备驱动概述设备驱动程序与整个软硬件系统的关系7.1设备驱动编程基础设备文件
Linux把所有设备都看作是特殊的文件,都纳入文件系统的范畴,例如,系统中第一个IDE硬盘使用/dev/hda来表示。系统通过处理文件的接口—虚拟文件系统VFS来管理和控制各种设备,Linux的设备又称为设备文件。Linux抽象了对硬件的处理,所有的硬件设备都可以作为普通文件来看待,对它们的使用和操作文件相同,使用标准的系统调用接口来完成打开、关闭、读写和I/O控制等操作,驱动程序的主要任务也就是要实现这些系统调用函数。由于引入了设备文件这一概念,Linux为文件和设备提供了一致的用户接口,对用户来说,设备文件与普通文件并无区别。7.1设备驱动编程基础主设备号与次设备号
Linux下每个设备都对应两个设备号,一个是主设备号,标识该设备的类别,也标识该设备的驱动程序;另一个是从设备号,标识使用同一个设备驱动程序的、不同的硬件设备。设备文件的主设备号必须与设备驱动程序在登记时申请的主设备号一致,否则用户进程将无法访问驱动程序。习惯上,设备文件都存在于系统的/dev目录下。使用命令#ls–l/dev,可以列出系统的设备文件,下面是该命令显示的部分内容:crw-------1rootroot10,10Jan302003adbmousecrw-------1rootroot14,3Jan302003dspbrw-rw----1rootfloppy2,0Jan302003fd0brw-rw----1rootfloppy2,4Jan302003fd0CompaQ7.1设备驱动编程基础主设备号与次设备号使用命令mknod可以创建指定类型的设备文件,同时为其分配主设备号和次设备号:
#mknod/dev/dspc143命令中c表示字符设备,14是主设备号,3是次设备号。主设备号的范围是1~255,次设备号可以使0~255间的值。系统依靠主设备号标识不同的驱动程序,因此在同一个系统中,一类设备的主设备号是唯一的。所有已经注册(即已经加载了驱动程序)的硬件设备的主设备号都可以在文件/proc/devices中得到。#cat/proc/devices7.1设备驱动编程基础11
7.1.2Linux内核模块编程设备驱动和内核模块Linux设备驱动属于内核的一部分,Linux内核的一个模块可以以两种方式被编译和加载。直接编译进Linux内核,随同Linux启动时加载;编译成一个可加载和删除的模块。7.1设备驱动编程基础12
7.1.2Linux内核模块编程模块相关命令lsmod列出当前系统中加载的模块
7.1设备驱动编程基础13
7.1.2Linux内核模块编程模块相关命令rmmod——用于模块卸载。insmod和modprobe——用于加载模块modprobe可以根据模块间依存关系以及/etc/modules.conf文件中的内容自动加载其他有依赖关系的模块,而insmod不会自动解决依存关系,即如果要加载的模块引用了当前内核符号表中不存在的符号,则无法加载。7.1设备驱动编程基础14
小结操作系统通过各种驱动程序来驾驭硬件设备,为用户屏蔽了各种各样的设备的差异性,提供统一的操作方式。Linux系统的设备分为三类:字符设备、块设备和网络设备。Linux下每个设备都对应两个设备号,一个是主设备号,另一个是从设备号。
#mknod/dev/dspc143Linux内核的一个模块可以以两种方式被编译和加载。模块命令:lsmod insmod rmmod7.1设备驱动编程基础15
7.1.2Linux内核模块编程一个Linux内核模块主要由以下几个部分组成。模块加载函数(必须)模块卸载函数(必须)模块许可证声明(必须)模块参数(可选):接收加载模块时传入的数据,对应模块的全局变量;模块导出符号(可选):对应于模块的函数或变量,导出后可供其它模块使用。模块作者等信息声明(可选)7.1设备驱动编程基础16
7.1.2Linux内核模块编程模块加载函数staticint__initinitialization_function(void){ /*初始化代码*/}module_init(initialization_function);模块加载函数以“module_init(funcName)”的形式指定,当通过insmod或modprobe命令加载内核模块时,模块的加载函数会自动被内核执行,完成本模块的相关初始化工作。包括向内核注册设备、申请内存等。7.1设备驱动编程基础初始化完成后释放该函数占用的内存17
7.1.2Linux内核模块编程模块卸载函数staticvoid__exitcleanup_function(void){ /*释放代码*/}module_exit(cleanup_function);模块卸载函数以“module_exit(funcName)”的形式指定,通常来说,模块卸载函数要完成与模块加载函数相反的功能(释放内存、释放硬件资源)。7.1设备驱动编程基础函数在运行完成后自动回收内存18
7.1.2Linux内核模块编程——模块参数定义方式:module_param(参数名,参数类型,读写权限):
staticchar*str_param=“LinuxModule”; staticintnum_param=4000; module_param(num_param,int,S_IRUGO); module_param(str_param,charp,S_IRUGO);传值方式
insmode模块名参数名=参数值7.1设备驱动编程基础19
7.1.2Linux内核模块编程——模块参数参数类型:
byte,short,ushort,int,uint,long,ulong,charp读写权限
模块加载后,在/sys/module/目录下将出现以此模块命名的目录,如果此模块存在读写权限不用0的参数,在此模块的目录下还将出现parameters目录,包含一系列以参数名命名的文件节点,这些文件的权限值就是对应参数的权限值。通常使用<linux/stat.h>中定义的值来表示权限值。7.1设备驱动编程基础20
7.1.2Linux内核模块编程导出符号:
Linux2.6的/proc/kallsyms文件对应着内核符号表,记录了符号及符号所在的内存地址。
模块中可使用如下宏导出符号到内核符号表:
EXPORT_SYMBOL(符号名); EXPORT_SYMBOL_GPL(符号名);
导出的符号可以被其它模块使用,使用前需要进行声明。7.1设备驱动编程基础21
7.1.2Linux内核模块编程——模块参数模块声明与描述:
MODULE_AUTHOR(author); MODULE_DESCRIPTION(description); MODULE_VERSION(version); MODULE_DEVICE_TABLE(table_info); MODULE_ALIAS(alternate_name);7.1设备驱动编程基础22
7.1.2Linux内核模块编程模块的使用计数Linux2.4内核中,模块自身通过MOD_INC_USE_COUNT、MOD_DEC_USE_COUNT宏来管理自己被使用的计数。Linux2.6内核提供了模块计数管理接口try_module_get(&module)和module_put(&module),从而取代Linux2.4内核中的模块使用计数管理宏。
7.1设备驱动编程基础23
7.1.2Linux内核模块编程模块许可证声明:MODULE_LICENSE("DualBSD/GPL");声明内核模块的许可权限,linux2.6内核中,可接受的LICENSE包括GPL、GPLv2、GPLandadditioanlrights、DualBSD/GPL等,如果不声明LICENSE,模块被加载时将收到内核被污染的警告(KernelTainted)。7.1设备驱动编程基础Linux下的设备驱动程序可以按照两种方式进行编译,一种是直接静态编译成内核的一部分,另一种是编译成可以动态加载的模块,在系统调试阶段,多使用模块方式。1、模块的入口函数用户程序通常从main()函数开始,执行一系列指令直到程序结束,内核模块与此不同,内核模块从函数init_module或是用宏module_init指定的函数调用开始,这就是内核模块的入口函数。它通常向内核注册模块提供哪些功能并且让内核准备好在需要时调用它。当它完成这些后,该函数就结束执行了。模块在被内核调用前什么都不做。模块编程实例(2.4内核下)
2、模块的退出函数所有的模块或是调用cleanup_module或是用宏module_exit指定函数。这是模块的退出函数,它撤销入口函数所做的一切。例如注销入口函数所注册的功能。所有模块都必须有入口函数和退出函数。模块编程实例
3、模块可调用的函数每一个module都可以提供一些函数或变量让别人使用,同时也可以使用已经载入kernel里的module提供的function。除此之外,kernel本身也会提供一些funciton或variable供module使用。也就是说:module所使用的function或variable,要么是写在module中,要么就是别的module提供的,再不就是kernel所提供的。不能使用libc或glibc所提供的function,像printf之类的东西。模块编程实例
4、最简单的模块程序实例——helloworld#include<linux/init.h>#include<linux/module.h>MODULE_LICENSE("DualBSD/GPL");inthello_init(void){ printk("<1>Hello,world\n"); return0;}voidhello_exit(void){ printk("\n<1>Goodbyecruelworld\n");}module_init(hello_init);module_exit(hello_exit);模块编程实例
4、最简单的模块程序实例——helloworld运行如下的编译命令,就可以得到相应的.o文件:#gcc-chello_module.c-D__KERNEL__-DMODULE-I/usr/src/linux-2.4.20-8/include/-O-Wall之后运行insmod命令,就可以加载该模块,通过lsmod命令可以看相应的模块已经加载。例子中的printk()函数用来向用户显示一些信息,但它并不是设计用来同用户交互的,它实际上是为内核提供日志功能,记录内核信息或用来给出警告。函数调用中的<1>为信息的优先级,内核共定义了8个优先级,并在linux/kernel.h中定义了8个优先级的宏,如实际中,推荐使用KERN_ALERT来代替<1>。调试时在真正的控制台下加载模块才能看到显示的信息,否则只记录在文件/var/log/messages中。模块编程
4、最简单的模块程序实例——helloworld注意事项:内核模块和内核的版本问题:为某个版本编译的模块不能被另一个版本的内核加载。使用图形界面的问题:建议在控制台下输入源代码、编译和加载,而不是在X下。gcc倾向于在默认的内核源文件路径(通常是/usr/src/)下寻找包含文件(头文件),可以通过gcc的-I选项来切换。模块编程
30
7.2.1字符设备驱动编写(运行)流程7.2字符设备驱动编程根据具体功能划分,设备驱动程序的代码则可以划分为以下几个部分:驱动程序的注册和注销、设备的打开、释放、读写、控制等操作操作、设备的中断和轮询处理。用户程序通常利用这几个部分对设备进行访问,由于用户程序不能直接访问内核驱动程序,所以Linux系统中用户对设备的操作需要Linux系统中虚拟文件系统的支持。用户采用标准的文件操作访问设备,虚拟文件系统将用户的这种文件访问转化成驱动程序的调用,从而产生对硬件的访问和操作。设备驱动程序的结构
32
7.2字符设备驱动编程字符设备驱动相关结构体(定义在linux/fs.h中):file_operations,对设备驱动相关的接口函数进行了统一的抽象。用来存储驱动内核模块提供的对设备进行各种操作的函数的指针,把系统调用和驱动程序关联起来。file,代表一个打开的文件描述符,系统中每一个打开的文件在内核中都有一个关联的structfile。它由内核在open时创建,并传递给在文件上操作的任何函数。inode,(i结点),内核中用inode结构表示具体的文件。对于单个文件,可能会有多个表示打开的文件描述符file结构,但是它们都指向了单个的inode结构。33
7.2字符设备驱动编程字符设备驱动相关结构体(定义在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(*open)(structinode*,structfile*);int(*release)(structinode*,structfile*); ……};为实现这种用户文件操作到设备操作的转换,虚拟文件系统向下为设备驱动提供了一个标准化的文件操作实现接口,这个接口由/include/linus/fs.h文件中的file_operations结构定义,其成员几乎全部是函数指针。开发设备驱动程序的主要任务就是实现这个文件操作实现接口结构中的函数指针。应用程序利用系统调用对设备进行读写操作时,Linux通过设备的主设备号找到相应的驱动程序,然后通过虚拟文件系统调用这个文件操作实现接口,文件操作实现接口调用设备驱动程序完成对硬件的操作。在file_operations结构中,一般只有主要的部分必须实现:open(),release(),read(),write(),ioctl(),llseek()等。设备驱动程序的结构
1.驱动程序的注册和注销
设备驱动程序可以在系统启动的时候初始化,也可以在需要的时候动态加载。每个字符设备或是块设备的初始化都是通过register_chrdev()或是register_blkdev()向内核注册。在关闭字符设备或是块设备时,还需要通过unregister_chrdev()或是unregister_blkdev()从内核中注销设备。
设备驱动程序的结构
1.驱动程序的注册和注销intregister_chrdev(unsignedintmajor,constchar*name,structfile_operations*fops);major:主设备号,为0则系统自动分配返回值:如果是动态分配主设备号,则返回分配的主设备号,且设备名会出现在/proc/devices中intunregister_chrdev(unsignedintmajor,constchar*name);设备驱动程序的结构
2.设备的打开和释放
打开设备的函数接口为open()。 int(*open)(structinode*,structfile*);在大部分设备驱动程序中,open完成如下工作:(1)增加设备的使用计数。(2)检查设备的相关错误,如设备尚未准备好或是类似硬件的问题。(3)检查是首次打开,则初始化设备。(4)识别次设备号,如有必要则更新f_op指针。(5)如果需要,分配且设置要放在filp->private_data里的数据结构。设备驱动程序的结构
2.设备的打开和释放
释放设备由release()来完成
int(*release)(structinode*,structfile*);
注意释放和关闭设备的区别。设备驱动程序的结构
3.设备的读写操作ssize_t(*read)(structfile*,char*,size_t,loff_t*);ssize_t(*write)(structfile*,constchar*,size_t,loff_t*);字符设备使用各自的read()和write()来进行数据读写。用户程序不能直接读写内核空间的数据,所以需要在驱动程序使用copy_to_user()和copy_from_user()函数来实现用户空间和内核空间的数据交换。设备驱动程序的结构
4.设备的控制操作除了读写操作,有时还要控制设备。这可以通过设备驱动程序中的ioctl()来完成。例如IDE硬盘的控制可以通过hd_ioctl(),对光驱的控制可以通过cdrom_ioctl()。设备驱动程序的结构
5.设备的轮询和中断处理对于不支持中断的设备,读写时需要轮询设备状态,以及是否需要继续进行数据传输。轮询方式(pollingmode)也就是查询等待方式。对于不支持中断方式的硬件设备只能采用这种方式来控制I/O过程,所以Linux中也配备了查询等待方式。设备驱动程序的结构
5.设备的轮询和中断处理在硬件支持中断的情况下,驱动程序可以使用中断方式控制I/O过程。Linux设置了名字为irq_action的中断例程描述符表:
staticstructirqaction*irq_action[NR_IRQS+1]; NR_IRQS表示中断源的数目。irq_action[]是一个指向irqaction结构的指针数组,它指向的irqaction结构是各个设备中断服务例程的描述符。
structirqaction{ void(*handler)(int,void*,structpt_regs*);
/*指向中断服务例程*/ unsignedlongflags;/*中断标志*/ unsignedlongmask;/*中断掩码*/ void*dev_id; structirqaction*next;/*指向下一个描述符*/};
在驱动程序初始化时,调用函数request_irq()建立该驱动程序的irqaction结构体,并把它登记到irq_action[]数组中设备驱动程序的结构
5.设备的轮询和中断处理
request_irq()函数的原型如下:intrequest_irq( usignedintirq, void(*handle)(int,void*,structpt_regs*), unsignedlongirqflags, constchar*devname, void*dev_id);参数irq是设备中断请求号,在向irq_action[]数组登记时,它作为数组的下标。把中断号为irq的irqaction结构体的首地址写入irq_action[irq]。这样,就把设备的中断请求号与该设备的服务例程联系在一起了。当CPU接收到中断请求后,根据中断号就可以通过irq_action[]找到该设备的中断服务程序。设备驱动程序的结构
※字符设备驱动案例
#defineDEVICE_NAME"demo"staticssize_tdemo_write(structfile*filp,constchar*buffer,size_tcount){ chardrv_buf[]; copy_from_user(drv_buf,buffer,count);…}staticssize_tdemo_read(structfile*filp,char*buffer,size_tcount,loff_t*ppos){ chardrv_buf[]; copy_to_user(buffer,drv_buf,count);….}程序代码框架※字符设备驱动案例
staticintdemo_ioctl(structinode*inode,structfile*file,unsignedintcmd,unsignedlongarg){}staticintdemo_open(structinode*inode,structfile*file){ MOD_INC_USE_COUNT; ……}staticintdemo_release(structinode*inode,structfile*filp){ MOD_DEC_USE_COUNT; ……}程序代码框架※字符设备驱动案例
staticstructfile_operationsdemo_fops={owner:THIS_MODULE,write:demo_write,read:demo_read,ioctl:demo_ioctl,open:demo_open,release:demo_release,};程序代码框架※字符设备驱动案例
staticint__initdemo_init(void){intresult;result=register_chrdev(demo_Major,“demo",&demo_fops);if(result<0)returnresult;if(demo_Major==0)demo_Major=result;/*dynamic*/printk(DEVICE_NAME"initialized\n");return0;}程序代码框架※字符设备驱动案例
staticvoid__exitdemo_exit(void){unregister_chrdev(demo_major,"demo");printk(DEVICE_NAME"unloaded\n");}module_init(demo_init);module_exit(demo_exit);程序代码框架※字符设备驱动案例
代码功能分析:#include<linux/config.h>#include<linux/module.h>#include<linux/devfs_fs_kernel.h>#include<linux/init.h>#include<linux/kernel.h>/*printk()*/#include<linux/slab.h>/*kmalloc()*/#include<linux/fs.h>/*everything...*/#include<linux/errno.h>/*errorcodes*/#include<linux/types.h>/*size_t*/#include<linux/proc_fs.h>#include<linux/fcntl.h>/*O_ACCMODE*/#include<linux/poll.h>/*COPY_TO_USER*/#include<asm/system.h>/*cli(),*_flags*※字符设备驱动案例
代码分析功能分析:#defineDEVICE_NAME "demo"#definedemo_MAJOR254#definedemo_MINOR0staticintMAX_BUF_LEN=1024;staticchardrv_buf[1024];staticintWRI_LENGTH=0;※字符设备驱动案例
代码分析功能分析:/*逆序排列缓冲区数据*/staticvoiddo_write(){ inti; intlen=WRI_LENGTH; chartmp; for(i=0;i<(len>>1);i++,len--){ tmp=drv_buf[len-1]; drv_buf[len-1]=drv_buf[i]; drv_buf[i]=tmp; }}※字符设备驱动案例
代码分析功能分析:staticssize_tdemo_write(structfile*filp,constchar*buffer,size_tcount){ if(count>MAX_BUF_LEN)count=MAX_BUF_LEN;
copy_from_user(drv_buf,buffer,count); WRI_LENGTH=count; printk("userwritedatatodriver\n"); do_write(); returncount;}※字符设备驱动案例
代码分析功能分析:staticssize_tdemo_read(structfile*filp,char*buffer,size_tcount,loff_t*ppos){ if(count>MAX_BUF_LEN) count=MAX_BUF_LEN;
copy_to_user(buffer,drv_buf,count); printk("userreaddatafromdriver\n"); returncount;}※字符设备驱动案例
代码分析功能分析:staticintdemo_ioctl(structinode*inode,structfile*file,unsignedintcmd,unsignedlongarg){ printk("ioctlruning\n"); switch(cmd){ case1:printk("runingcommand1\n");break; case2:printk("runingcommand2\n");break; default: printk("errorcmdnumber\n");break; } return0;}※字符设备驱动案例
代码分析功能分析:staticintdemo_open(structinode*inode,structfile*file){ MOD_INC_USE_COUNT; sprintf(drv_buf,"deviceopensucess!\n"); printk("deviceopensucess!\n"); return0;}※字符设备驱动案例
代码分析功能分析:staticintdemo_release(structinode*inode,structfile*filp){ MOD_DEC_USE_COUNT; printk("devicerelease\n"); return0;}※字符设备驱动案例
代码分析功能分析:staticstructfile_operationsdemo_fops={ owner: THIS_MODULE, write: demo_write, read: demo_read, ioctl: demo_ioctl, open: demo_open, release: demo_release,};※字符设备驱动案例
代码分析功能分析:staticint__initdemo_init(void){ intresult;SET_MODULE_OWNER(&demo_fops);result=register_chrdev(demo_MAJOR,"demo",&demo_fops);if(result<0)returnresult;//if(demo_MAJOR==0)demo_MAJOR=result;/*dynamic*/ printk(DEVICE_NAME"initialized\n"); return0;}※字符设备驱动案例
代码分析功能分析:staticvoid__exitdemo_exit(void){unregister_chrdev(demo_MAJOR,"demo"); printk(DEVICE_NAME"unloaded\n");}/*************************************************************************************/module_init(demo_init);module_exit(demo_exit);※字符设备驱动案例
模块化驱动程序的编译、加载和卸载:1、编译
#gcc-cdemo.c-D__KERNEL__-DMODULE-I/usr/src/linux-2.4.20-8/include/-O-Wall2、创建设备节点
#mknod/dev/democ25402、加载
#insmoddemo.o3、卸载
#rmmoddemo※字符设备驱动案例
新建设备的测试程序:#include<stdio.h>#include<stdlib.h>#include<fcntl.h>#include<unistd.h>#include<sys/ioctl.h>intMAX_LEN=32;※字符设备驱动案例
新建设备的测试程序:voidshowbuf(char*buf){ inti,j=0; for(i=0;i<MAX_LEN;i++){ if(i%4==0) printf("\n%4d:",j++); printf("%4d",buf[i]); } printf("\n*****************************************************\n");}※字符设备驱动案例
新建设备的测试程序:intmain(){ intfd; inti; charbuf[255];
for(i=0;i<MAX_LEN;i++){ buf[i]=i;
} fd=open("/dev/demo",O_RDWR); if(fd<0){ printf("####DEMOdeviceopenfail####\n"); return(-1);
}※字符设备驱动案例
新建设备的测试程序: printf("write%dbytesdatato/dev/demo\n",MAX_LEN); showbuf(buf); write(fd,buf,MAX_LEN); printf("Read%dbytesdatafrom/dev/demo\n",MAX_LEN); read(fd,buf,MAX_LEN); showbuf(buf); ioctl(fd,1,NULL); ioctl(fd,4,NULL); close(fd); return0;}※字符设备驱动案例
demo.c回顾:根据具体功能划分,设备驱动程序的代码则可以划分为以下几个部分:驱动程序的注册和注销、设备的打开和释放、设备的读写操作、设备的控制操作、设备的中断和轮询处理。问题:1、从demo.c中找出上述代码各部分的代码。
2、找出test_demo.c对驱动程序中相关功能调用的代码,观察功能调用的语法与demo.c中定义的相关函数原型之时的关系,有什么发现?66
设备驱动程序与整个软硬件系统的关系※字符设备驱动案例
总结:驱动模块的注册:推荐使用宏module_init()来指定驱动程序的初始化函数;用宏__init修饰初始化函数,使被修饰的函数代码只在启动时执行一次,启动之后可以释放相应的内存空间。向内核注册驱动程序:register_chrdev(unsignedintmajor,constchar*name,structfile_operations*fops)※字符设备驱动案例
总结:打开设备时需要做的工作:
(1)增加设备的使用计数(MOD_INC_USE_COUNT)。(2)检查设备的相关错误,如设备尚未准备好或是类似硬件的问题。(3)如果是首次打开,则初始化设备。(4)识别次设备号,如有必要则更新f_op指针。※字符设备驱动案例
总结:读、写操作:
使用copy_to_user(buffer,drv_buf,count)和copy_from_user(drv_buf,buffer,count)
在内核空间和用户空间之间传递数据。※字符设备驱动案例
总结:structfile_operations结构初始化(2.4内核中):
staticstructfile_operationsdemo_fops={ owner: THIS_MODULE, write: demo_write, read: demo_read, ioctl: demo_ioctl, open: demo_open, release: demo_release,};※字符设备驱动案例
总结:structfile_operations结构初始化(2.6内核中):
staticstructfile_operationsdemo_fops={ .owner= THIS_MODULE, .write= demo_write, .read= demo_read, .ioctl= demo_ioctl, .open= demo_open, .release= demo_release,};※字符设备驱动案例
总结:release/close:使用宏MOD_DEC_USE_COUNT递减计数器卸载:使用unregister_chrdev(demo_MAJOR,“demo”)向内核注销驱动模块。5.设备的中断处理在硬件支持中断的情况下,驱动程序可以使用中断方式控制I/O过程。Linux设置了名字为irq_action的中断例程描述符表:
staticstructirqaction*irq_action[NR_IRQS+1]; NR_IRQS表示中断源的数目。irq_action[]是一个指向irqaction结构的指针数组,它指向的irqaction结构是各个设备中断服务例程的描述符。
structirqaction{ void(*handler)(int,void*,structpt_regs*);
/*指向中断服务例程*/ unsignedlongflags;/*中断标志*/ unsignedlongmask;/*中断掩码*/ void*dev_id; structirqaction*next;/*指向下一个描述符*/};
在驱动程序初始化时,调用函数request_irq()建立该驱动程序的irqaction结构体,并把它登记到irq_action[]数组中设备驱动程序的结构
5.设备的中断处理
request_irq()函数的原型如下:intrequest_irq( usignedintirq, void(*handle)(int,void*,structpt_regs*), unsignedlongirqflags, constchar*devname, void*dev_id);参数irq是设备中断请求号,在向irq_action[]数组登记时,它作为数组的下标。把中断号为irq的irqaction结构体的首地址写入irq_action[irq]。这样,就把设备的中断请求号与该设备的服务例程联系在一起了。当CPU接收到中断请求后,根据中断号就可以通过irq_action[]找到该设备的中断服务程序。设备驱动程序的结构
※字符设备驱动案例
总结:字符设备驱动程序基本结构编写所需要的功能函数:read,write,ioctl,open,release以及中断服务程序等;定义structfile_operations结构变量,并将步骤1中定义的功能函数赋值该变量中相应的函数指针;编写初始化函数,实现驱动模块的注册加载、中断服务程序的登记;编写驱动程序注销函数;使用宏module_init,module_exit声明初始化(注册)和注销函数。767.3.1GPIO工作原理FS2410开发板的S3C2410处理器具有117个多功能通用I/O(GPIO)端口管脚,包括GPIO8个端口组,分别为GPA(23个输出端口)、GPB(11个输入/输出端口)、GPC(16个输入/输出端口)、GPD(16个输入/输出端口)、GPE(16个输入/输出端口)、GPF(8个输入/输出端口)、GPH(11个输入/输出端口)。根据各种系统设计的需求,通过软件方法可以将这些端口配置成具有相应功能(例如:外部中断或数据总线)的端口。7.3GPIO驱动程序实例77
7.3.1GPIO工作原理LED和蜂鸣器原理图7.3GPIO驱动程序实例78
7.3.1GPIO工作原理相关寄存器7.3GPIO驱动程序实例79Linux2.6新增的相关函数1、设备号相关函数在Linux2.6中,用dev_t类型来描述设备号,dev_t是32位数值类型,其中高12位表示主设备号,低20位表示次设备号。linux/kdev.h中定义了三个宏进行设备号的处理。获得主设备号:MAJOR(dev_tdev)获得次设备号:MINOR(dev_tdev)构造dev_t实例:MKDEV(intmajor,intminor)静态分配设备号:register_chrdev_region()动态分配设备号:alloc_chrdev_region()7.3GPIO驱动程序实例80Linux2.6新增的相关函数2、字符设备注册在Linux2.6中,用structcdev结构来描述字符设备。在驱动程序中必须将设备号及设备操作接口(file_operations结构来描述)赋予cdev结构变量:申请cdev结构: structcdev*cdev_alloc(void)与file_operations结构关联:
voidcdev_init(structcdev*,structfile_operations*)与设备号关联并向内核注册:
voidcdev_add(structcdev*,dev_t,unsignedint)7.3GPIO驱动程序实例81
7.3.1GPIO工作原理GPIO驱动程序第1部分:宏定义code\ch8\GPIO\gpio_drv.h第2部分:驱动模块加载部分:code\ch8\GPIO\gpio_drv_init.c第3部分:打开设备(open操作)code\ch8\GPIO\gpio_drv_open.c第4部分:控制设备(ioctl操作)code\ch8\GPIO\gpio_drv_ioctl.c7.3GPIO驱动程序实例82
7.3.1GPIO工作原理GPIO驱动程序第5部分设备释放操作code\ch8\GPIO\gpio_drv_release.c第6部分模块注销操作code\ch8\GPIO\gpio_drv_unregister.c测试程序:code\ch8\GPIO\gpio_test.c7.3GPIO驱动程序实例83
8.4.1中断编程实例注册中断intrequest_irq(unsignedintirq, void(*handler)(intirq,void*dev_id,structpt_regs*regs), unsignedlongirqflags,constchar*devname,oid*dev_id);释放中断voidfree_irq(unsignedintirq,void*dev_id);8.4按键驱动程序实例84
8.4.2按键工作原理8.4按键驱动程序实例85
8.4.2按键工作原理按键电路主要端口8.4按键驱动程序实例86
8.4.3按键驱动程序8.4按键驱动程序实例87
8.4.4按键测试程序8.4按键驱动程序实例88
驱动程序的结构驱动程序主要数据结构GPIO驱动程序按键驱动8.5小结89
根据书上的提示,将本章中所述的按键驱动程序进行进一步的改进,并在目标板上进行测试。实现各种外设(包括ADC、SPI、I2C等)的字符设备驱动程序。8.6
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 北师大版小学五年级下册语文全册教案
- 利用智能图像处理技术提升防伪效果
- 2024高中地理第六章人类与地理环境的协调发展章末总结提升练含解析新人教版必修2
- 2024高中生物第4章种群和群落第3节群落的结构课堂演练含解析新人教版必修3
- 2024高考物理一轮复习第八章恒定电流实验10练习使用多用电表学案新人教版
- 2024高考化学一轮复习第4章非金属及其化合物第12讲氯及其化合物卤族元素学案
- 2024高考历史一轮复习方案专题三现代中国的政治建设祖国统一与对外关系专题整合备考提能教学案+练习人民版
- 2024高考地理一轮复习第一章第2讲地球的自转及地理意义教案含解析新人教版
- (4篇)2024年幼儿园家访工作总结
- 2024年湖北交通职业技术学院高职单招职业技能测验历年参考题库(频考版)含答案解析
- 一年级语文上册《两件宝》教案1
- 咨询公司工作总结(共5篇)
- pcs-985ts-x说明书国内中文版
- GB/T 38836-2020农村三格式户厕建设技术规范
- 小品《天宫贺岁》台词剧本手稿
- 京东商业计划书课件
- 医院患者伤口换药操作课件
- 肥料采购验收单模板
- 部编版五年级下册语文根据课文内容填空(常用)
- 中铁集团会计核算手册
- 伤口护理小组工作总结共34张课件
评论
0/150
提交评论