资源描述
Click to edit the title text format,Click to edit the outline text format,Second Outline Level,Third Outline Level,Fourth Outline Level,Fifth Outline Level,Sixth Outline Level,Seventh Outline Level,Eighth Outline Level,Ninth Outline Level,*,Click to edit the title text format,*,Click to edit the outline text format,Second Outline Level,Third Outline Level,Fourth Outline Level,Fifth Outline Level,Sixth Outline Level,Seventh Outline Level,Eighth Outline Level,Ninth Outline Level,第 十 章,内核驱动设计入门,嵌入式系统及其软件工具,本章提要,1,驱动程序概述,2,Linux设备与设备驱动程序,3,设备驱动程序框架,10.1,驱动程序概述,设备驱动程序是操作系统内核和机器硬件之间的接口。所有对设备的操作都必须由被控设备相关的代码,驱动程序,来完成,。,设备驱动隐藏了设备的具体细节,为不同设备提供了统一的访问接口,。用户或者应用程序可以像操作普通文件一样对硬件设备进行操作,设备驱动是内核的一部分,。,10.1.1,设备驱动功能,设备的初始化和释放,负责内核与外设之间的数据交换,提供各类设备服务,检测和处理设备工作过程中出现的错误,驱动程序在嵌入式,Linux,中的作用:,在嵌入式系统中,要访问某个特定的硬件,实际上就是访问物理的地址空间(处理器内的寄存器、外设的地址映像)。然而,由于,MMU,的内存映射以及对操作系统的保护,运行在用户态的应用程序一般不能够直接访问硬件地址,因此需要驱动程序作为应用程序和访问硬件之间的媒介。,某些驱动程序并不能直接给应用程序调用,而是给,Linux,内核调用。例如:串口作为,console,的物理载体;或者驱动程序由操作系统调用,应用程序可以间接的使用驱动程序,如,framebuffer,,网络设备驱动等。(要去哦球驱动程序必须按照某个框架编写,供上层模块使用),10.1.2,驱动程序开发注意,开发驱动程序和开发应用程序不同,;,驱动程序应提供尽量多的选项给用户,不应该强加约束,;,尽量提高驱动程序的速度、效率,尽量简化,便于维护,。,10.2,Linux设备与设备驱动,Linux把设备分成3类,每类设备有自己的接口和常用框架,他们也有很多共性。,字符设备:采用字符流方式访问的设备,每次只传输一个字符,。,特点:顺序访问。鼠标、键盘、串口等,。,块设备:采用随机访问的方式访问,数据具有固定大小的块的设备;(Linux中块设备与字符设备使用基本一致,),特点:缓冲存储,随机访问。硬盘、光盘等,。,网络设备:采用数据包传输方式访问的设备,。,特点:与字符设备和块设备不同,没有设备文件。通过socket进行使用,。,设,备,设备的分类,10.2.1,Linux设备和设备驱动,设备驱动的共性,:,设备驱动工作在内核态,误操作后果很严重,;,设备驱动必须提供一个标准的内核接口,字符和块设备采用文件接口,网络设备采用套接字,;,驱动可以采用标准的内核服务,如:“内存分配”、等待队列、中断机制等,;,驱动可以动态的连接到内核中,不用时可以卸载。也可将驱动编译到内核中,。,10.2.1,Linux设备和设备驱动,10.2.2,Linux设备文件,bash#,ls,-l/dev,查看相关设备信息,crw,-,-,-,-,-1 root,root,14,3Jan30 2003,dsp,crw-rw,-,-,-1 root floppy 2,0Jan30 2003 fd0,文件类型及,访问权限,链接个数,所有者,组,主设备号,创建日期,设备文件名,次设备号,系统启动时会把设备文件系统挂载在/dev目录下,linux的设备文件的创建、删除和目录层次等都由各设备驱动程序管理。/dev中每个文件都动态的对应了一个系统上存在的设备驱动程序,。,10.2.3,Linux对设备文件的操作,open(),int,open(const,*pathname,int,flag);,open函数用于通过文件路径打开一个文件,返回一个整数类型的文件描述符,。,close(),int,close(int,fd,);,close函数用于关闭文件,close会让数据写回磁盘,并释放文件所占用的资源,。,10.2.3,Linux对设备文件的操作,read(),ssize_t,read(int,fd,void*,buf,size_t,count);,read函数从fd指向的文件传送count个字节到buf指针指向的内存中文件读写指针的位置会随着读取到的字节移动,。,当读取成功时,最好将返回值和count值进行比较若返回的值比count少,则可能读到了文件尾,或者read操作被中断了等,。,10.2.4,设备驱动程序接口,write(),ssize_t,write(int,fd,void*,buf,size_t,count);,write函数把参数buf指向的内存写入count个字节到fd指向的文件内。文件的读写位置也会随之变化,;,10.2.3,Linux对设备文件的操作,lseek,(),loff_t,lseek(int,fd,loff_t,offset,int,whence);,lseek函数用来控制文件的读写位置,。,ioctl,(),int,ioctl(int,fd,unsigned,int,cmd,);,ioctl是Linux中对设备文件操作的特殊函数。可以对应到驱动程序的相应函数中,。,文件操作示例,:,#include,#include,#include,#include,int,main(),int,fd,size,;,char s=”File Operationn”;,char buffer80;,fd,=open(“/,tmp/tempfile”,O_WRONLY|O_CREAT,);,write(fd,s,sizeof(s,);,close(fd,);,fd,=open(“/,tmp/tempfile”,O_RDONLY,);,size=,read(fd,buffer,sizeof(buffer,);,close(fd,);,printf(“Display:%s”,buffer,);,return 0;,以上的程序首先打开文件,接下来向文件中写入内容,然后从文件中读取内容,最后关闭文件。其运行结果为,:,Display:,File operation,10.2.4,Linux对设备文件的操作,设备驱动程序是操作系统内核和机器硬件之间的接口。所有对设备的操作都必须由被控设备相关的代码驱动程序来完成。设备驱动隐藏了设备的具体细节,为不同设备提供了统一的访问接口,。,应用程序与驱动程序之间的调用关系,10.2.4,Linux对设备文件的操作,调用模块,Linux内核,内核,空间,用户,空间,模块,文件,.o,插入,内核,文件系统接口,open()/,close()/,read()/,write()/,ioctl,(),用户,空间,1,2,3,Linux应用程序中,通过对文件操作的函数对驱动程序的设备文件进行调用,。,10.2.4,Linux对设备文件的操作,struct,file_operations,/,文件操作,struct,module*owner;,loff_t,(*,llseek)(struct,file*,loff_t,int,);,ssize_t,(*,read)(struct,file*,char*,size_t,loff_t,*);,ssize_t,(*,write)(struct,file*,const char*,size_t,loff_t,*);,int,(*,readdir)(struct,file*,void*,filldir_t,);,unsigned,int,(*,poll)(struct,file*,struct,poll_table_struct,*);,int,(*,ioctl)(struct,inode,*,struct,file*,unsigned,int,long);,int,(*,mmap)(struct,file*,struct,vm_area_struct,*);,int,(*,open)(struct,inode,*,struct,file*);,int,(*,release)(struct,inode,*,struct,file*);,字符设备操作,10.2.4,Linux对设备文件的操作,struct,block_device_operations,/,块设备操作,int,(*,open)(struct,inode,*,struct,file*);,int,(*,release)(struct,inode,*,struct,file*);,int,(*,ioctl)(struct,inode,*,struct,file*,unsigned,int,long);,int,(*,check_media_change)(kdev_t,);,int,(*,revalidate_disk)(kdev_t,);,struct,module*owner;,Linux2.4定义的块设备操作,10.2.4,Linux对设备文件的操作,struct,block_device_operations,int,(*,open)(struct,inode,*,struct,file*);,int,(*,release)(struct,inode,*,struct,file*);,int,(*,ioctl)(struct,inode,*,struct,file*,unsigned,int,long);,int,(*,compat_ioctl)(struct,file*,unsigned,int,long);,int,(*,media_changed)(struct,gendisk,*);,int,(*,revalidate_disk)(struct,gendisk,*);,struct,module*owner;,Linux2.6定义的块设备操作,10.2.5,内核空间和用户空间交互,int,access_ok(int,type,unsigned long,addr,unsigned long size);,unsigned long,copy_to_user(void,*to,const void*from,unsigned long fen);,unsigned long,copy_from_user(void,*to,const void*from,unsigned long fen);,void*,mmap(void,*start,size_t,length,int,prot,int,flags,int,fd,loff_t,offsize,);,直接编译到内核,再运行新的内核来调试,调试效率低,10.2.6,Linux驱动开发和调试方法,module,模式,将单独的驱动编译为module,模式,,单独加载调试,调试效率高,调试时不用重启内核,10.2.7,内核模块module,1.,什么是module,?,module就是一个程序,但他可以被动态加载到kernel里,成为kernel的一部分。加载到kernel里的module具有和kernel一样的权力,它可以访问kernel里发布的任何数据结构,使用发布的function,()。,Linux内核,模块module,注册功能函数,功能1,功能2,功能3,功能1,功能2,功能3,删除功能函数,驱,动,函,数,调,用,10.2.7,内核模块module,2.,kernel如何管理module,?,Linux,内核模块调用关系,insmod,rmmod,10.2.7,内核模块module,3.,module的初始化与回收,int,init_module(void,),/,模块初始化函数,int,result;,result=register_chrdev(“参数1”,“参数2”,“参数3”);,if(result,0),printk(DEVICE_NAME,“cant get major,mumber,”);,return result;,if(device_major,=0),device_major,=result;,return result;,10.2.7,内核模块module,3.,module的初始化与回收,void,cleanup_module(void,),/,模块卸载函数,unregister_chrdev(参数1,参数2);,Module_init(module,_,init),module_exit(module,_cleanup)这两个宏是在Linux内核头文件中定义的,Linux2.4中才具有。,10.3,设备驱动程序框架,include,include,include,include,include,define DEMO_DEVICE_MAJOR 125,define DEMO_DEVICE_NAME “demo”,define DEMO_DEVICE_COMMAND1 1,define DEMO_DEVICE_COMMAND2 2,define DEMO_DEVICE_COMMAND3 3,1,头文件包含,:,编写设备驱动的时候要包含内核的头文件,2,定义常量,:,定义模块的主设备号和模块的名称,10.3,设备驱动程序框架,module_init(demo_init,);,module_exit(demo_cleanup,);,static,struct,file_operation,demo_device_fops,=,owner:THIS_MOUDLE,read:,demo_read,write:,demo_write,ioctl,:,demo_ioctl,open:,demo_open,release:,demo_close,;,3,函数声明,;,4,文件操作结构的指针,10.3,设备驱动程序框架,static,int,demo_init(void,),int,result;,SET_MODULE_OWNER(&demo_flops,);,result=,register_chrdev(DEMO_DEVICE_MAJOR,DEMO_DEVICE_NAME,&,demo_device_fops,);,if(result,0)return result;,return 0;,;,static void,demo_cleanup(void,),unregister_chrdev(DEMO_DEVICE_MAJOR,DEMO_DEVICE_NAME);,return;,;,10.3,设备驱动程序框架,static,int,demo_write(struct,file*flip,const char*buffer,size_t,count),char,dev_buf,;,copy_from_user(dev_buff,buffer,count);,;,static,int,demo_read(struct,file*flip,char*buffer,size_t,count,loff_t,*,ppos,),char,dev_buf,;,copy_to_user(buffer,dev_buff,count);,;,5,各种操作,10.3,设备驱动程序框架,static,int,demo_ioctl(struct,inode,*,inode,struct,file*,file,unsigned,int,cmd,unsigned long,arg,),switch(cmd,),case DEMO_DEVICE_COMMAND1:,/*DEMO_DEVICE_COMMAND1处理函数*/break;,case DEMO_DEVICE_COMMAND2:,/*DEMO_DEVICE_COMMAND2处理函数*/break;,case DEMO_DEVICE_COMMAND3:,/*DEMO_DEVICE_COMMAND3处理函数*/break;,;,return 0;,10.3,设备驱动程序框架,static,int,demo_open(struct,inode,*,inode,struct,file*,file,),printk(KERN_CRIT,“,demo_device,openn”);,MOD_INC_USE_COUNT;,return 0;,;,static,int,demo_close(struct,inode,*,inode,struct,file*,file,),printk(KERN_CRIT,“,demo_device,closen”);,MOD_DEC_USE_COUNT;,return 0;,;,补充,:设备驱动程序开发中一些具体问题,IO Port,Linux下任何驱动程序都可以对任意IO进行操作,每个驱动程序都应该自己避免误用端口。有两个Kernel函数可以使用,。,check_region(int,io_port,int,off_set,),request_region(int,io_port,int,off_set,char*,devname,),另:设备驱动程序开发中一些具体问题,内存操作,在设备驱动中,动态,的开辟内存不是用malloc(),而是使用kmalloc(),,回收内存,使用kfree,()。注意kmalloc最大只能开辟(128K16)个字节的空间。其中16字节被页描述符占用了。,IO口、寄存器或者硬件设备的RAM一般占用F000000以上的地址空间,驱动程序不能直接访问,要通过kernel的vremap函数获得重新映射后的地址。,另:设备驱动程序开发中一些具体问题,有些硬件需要比较大的连续内存空间做DMA传送,这块空间要一直驻留使用,但kmalloc最大只能开辟128K的内存,需要用其它方法解决。,用vremap映射后的内存,不用时应用unremap释放,。,另:设备驱动程序开发中一些具体问题,中断处理,驱动中可以使用中断,Linux使用中断的过程如下,:,(1)在移植内核的时候根据硬件的情况,为每一个硬件设备分配中断号;,(2)当中断产生时根据中断类型号找到中断处理子程序进行处理;,(3)在驱动程序中需要将硬件的中断处理子程序注册到其中断号上。,另:设备驱动程序开发中一些具体问题,在Linux系统中,对中断的处理属于系统核心部分,设备驱动程序可以通过下面的函数来向系统申请和释放中断,。,int,request_irq(unsigned,int,irq,void(*,handle)(int,void*,struct,pt_regs,*),unsigned,int,long flags,const char*device),int,free_irq(unsigned,int,irq,void*,dev_id,);,
展开阅读全文